mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-25 10:58:52 +00:00
* dynamic macros as described in http://1stein.org/download/dynmacro.pdf
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@21328 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
parent
2ad61c0366
commit
1231489798
108
src/Buffer.cpp
108
src/Buffer.cpp
@ -4,6 +4,7 @@
|
||||
* Licence details can be found in the file COPYING.
|
||||
*
|
||||
* \author Lars Gullik Bjønnes
|
||||
* \author Stefan Schimanski
|
||||
*
|
||||
* Full author contact details are available in file CREDITS.
|
||||
*/
|
||||
@ -68,8 +69,8 @@
|
||||
#include "insets/InsetInclude.h"
|
||||
#include "insets/InsetText.h"
|
||||
|
||||
#include "mathed/MathMacroTemplate.h"
|
||||
#include "mathed/MacroTable.h"
|
||||
#include "mathed/MathMacroTemplate.h"
|
||||
#include "mathed/MathSupport.h"
|
||||
|
||||
#include "frontends/alert.h"
|
||||
@ -200,12 +201,14 @@ public:
|
||||
/// our Text that should be wrapped in an InsetText
|
||||
InsetText inset;
|
||||
|
||||
///
|
||||
MacroTable macros;
|
||||
|
||||
///
|
||||
TocBackend toc_backend;
|
||||
|
||||
/// macro table
|
||||
typedef std::map<unsigned int, MacroData, std::greater<int> > PositionToMacroMap;
|
||||
typedef std::map<docstring, PositionToMacroMap> NameToPositionMacroMap;
|
||||
NameToPositionMacroMap macros;
|
||||
|
||||
/// Container for all sort of Buffer dependant errors.
|
||||
map<string, ErrorList> errorLists;
|
||||
|
||||
@ -823,10 +826,6 @@ Buffer::ReadStatus Buffer::readFile(Lexer & lex, FileName const & filename,
|
||||
from_utf8(filename.absFilename())));
|
||||
}
|
||||
|
||||
//lyxerr << "removing " << MacroTable::localMacros().size()
|
||||
// << " temporary macro entries" << endl;
|
||||
//MacroTable::localMacros().clear();
|
||||
|
||||
pimpl_->file_fully_loaded = true;
|
||||
return success;
|
||||
}
|
||||
@ -1725,7 +1724,7 @@ void Buffer::setParentName(string const & name)
|
||||
Buffer const * Buffer::masterBuffer() const
|
||||
{
|
||||
if (!params().parentname.empty()
|
||||
&& theBufferList().exists(params().parentname)) {
|
||||
&& theBufferList().exists(params().parentname)) {
|
||||
Buffer const * buf = theBufferList().getBuffer(params().parentname);
|
||||
//We need to check if the parent is us...
|
||||
//FIXME RECURSIVE INCLUDE
|
||||
@ -1754,44 +1753,103 @@ Buffer * Buffer::masterBuffer()
|
||||
}
|
||||
|
||||
|
||||
MacroData const & Buffer::getMacro(docstring const & name) const
|
||||
bool Buffer::hasMacro(docstring const & name, Paragraph const & par) const
|
||||
{
|
||||
return pimpl_->macros.get(name);
|
||||
Impl::PositionToMacroMap::iterator it;
|
||||
it = pimpl_->macros[name].upper_bound(par.macrocontextPosition());
|
||||
if( it != pimpl_->macros[name].end() )
|
||||
return true;
|
||||
|
||||
// If there is a master buffer, query that
|
||||
const Buffer *master = masterBuffer();
|
||||
if (master && master!=this)
|
||||
return master->hasMacro(name);
|
||||
|
||||
return MacroTable::globalMacros().has(name);
|
||||
}
|
||||
|
||||
|
||||
bool Buffer::hasMacro(docstring const & name) const
|
||||
{
|
||||
return pimpl_->macros.has(name);
|
||||
if( !pimpl_->macros[name].empty() )
|
||||
return true;
|
||||
|
||||
// If there is a master buffer, query that
|
||||
const Buffer *master = masterBuffer();
|
||||
if (master && master!=this)
|
||||
return master->hasMacro(name);
|
||||
|
||||
return MacroTable::globalMacros().has(name);
|
||||
}
|
||||
|
||||
|
||||
void Buffer::insertMacro(docstring const & name, MacroData const & data)
|
||||
MacroData const & Buffer::getMacro(docstring const & name, Paragraph const & par) const
|
||||
{
|
||||
MacroTable::globalMacros().insert(name, data);
|
||||
pimpl_->macros.insert(name, data);
|
||||
Impl::PositionToMacroMap::iterator it;
|
||||
it = pimpl_->macros[name].upper_bound(par.macrocontextPosition());
|
||||
if( it != pimpl_->macros[name].end() )
|
||||
return it->second;
|
||||
|
||||
// If there is a master buffer, query that
|
||||
const Buffer *master = masterBuffer();
|
||||
if (master && master!=this)
|
||||
return master->getMacro(name);
|
||||
|
||||
return MacroTable::globalMacros().get(name);
|
||||
}
|
||||
|
||||
|
||||
void Buffer::buildMacros()
|
||||
MacroData const & Buffer::getMacro(docstring const & name) const
|
||||
{
|
||||
// Start with global table.
|
||||
pimpl_->macros = MacroTable::globalMacros();
|
||||
Impl::PositionToMacroMap::iterator it;
|
||||
it = pimpl_->macros[name].begin();
|
||||
if( it != pimpl_->macros[name].end() )
|
||||
return it->second;
|
||||
|
||||
// Now add our own.
|
||||
ParagraphList const & pars = text().paragraphs();
|
||||
// If there is a master buffer, query that
|
||||
const Buffer *master = masterBuffer();
|
||||
if (master && master!=this)
|
||||
return master->getMacro(name);
|
||||
|
||||
return MacroTable::globalMacros().get(name);
|
||||
}
|
||||
|
||||
|
||||
void Buffer::updateMacros()
|
||||
{
|
||||
// start with empty table
|
||||
pimpl_->macros = Impl::NameToPositionMacroMap();
|
||||
|
||||
// Iterate over buffer
|
||||
ParagraphList & pars = text().paragraphs();
|
||||
for (size_t i = 0, n = pars.size(); i != n; ++i) {
|
||||
// set position again
|
||||
pars[i].setMacrocontextPosition(i);
|
||||
|
||||
//lyxerr << "searching main par " << i
|
||||
// << " for macro definitions" << std::endl;
|
||||
InsetList const & insets = pars[i].insetList();
|
||||
InsetList::const_iterator it = insets.begin();
|
||||
InsetList::const_iterator end = insets.end();
|
||||
for ( ; it != end; ++it) {
|
||||
//lyxerr << "found inset code " << it->inset->lyxCode() << std::endl;
|
||||
if (it->inset->lyxCode() == MATHMACRO_CODE) {
|
||||
MathMacroTemplate const & mac
|
||||
= static_cast<MathMacroTemplate const &>(*it->inset);
|
||||
insertMacro(mac.name(), mac.asMacroData());
|
||||
if (it->inset->lyxCode() != MATHMACRO_CODE)
|
||||
continue;
|
||||
|
||||
// get macro data
|
||||
MathMacroTemplate const & macroTemplate
|
||||
= static_cast<MathMacroTemplate const &>(*it->inset);
|
||||
|
||||
// valid?
|
||||
if (macroTemplate.validMacro()) {
|
||||
MacroData macro = macroTemplate.asMacroData();
|
||||
|
||||
// redefinition?
|
||||
// call hasMacro here instead of directly querying mc to
|
||||
// also take the master document into consideration
|
||||
macro.setRedefinition(hasMacro(macroTemplate.name()));
|
||||
|
||||
// register macro (possibly overwrite the previous one of this paragraph)
|
||||
pimpl_->macros[macroTemplate.name()][i] = macro;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
15
src/Buffer.h
15
src/Buffer.h
@ -43,6 +43,7 @@ class LaTeXFeatures;
|
||||
class Language;
|
||||
class MacroData;
|
||||
class OutputParams;
|
||||
class Paragraph;
|
||||
class ParConstIterator;
|
||||
class ParIterator;
|
||||
class ParagraphList;
|
||||
@ -355,14 +356,16 @@ public:
|
||||
//
|
||||
// Macro handling
|
||||
//
|
||||
///
|
||||
void buildMacros();
|
||||
///
|
||||
/// Collect macros in paragraphs
|
||||
void updateMacros();
|
||||
/// Look for macro defined before par (or in the master buffer)
|
||||
bool hasMacro(docstring const & name, Paragraph const & par) const;
|
||||
/// Look for macro defined anywhere in the buffer (or in the master buffer)
|
||||
bool hasMacro(docstring const & name) const;
|
||||
///
|
||||
/// Return macro defined before par (or in the master buffer)
|
||||
MacroData const & getMacro(docstring const & name, Paragraph const & par) const;
|
||||
/// Return macro defined anywhere in the buffer (or in the master buffer)
|
||||
MacroData const & getMacro(docstring const & name) const;
|
||||
///
|
||||
void insertMacro(docstring const & name, MacroData const & data);
|
||||
|
||||
/// Replace the inset contents for insets which InsetCode is equal
|
||||
/// to the passed \p inset_code.
|
||||
|
@ -507,7 +507,7 @@ void BufferView::processUpdateFlags(Update::flags flags)
|
||||
|
||||
// Update macro store
|
||||
if (!(cursor().inMathed() && cursor().inMacroMode()))
|
||||
buffer_.buildMacros();
|
||||
buffer_.updateMacros();
|
||||
|
||||
// Now do the first drawing step if needed. This consists on updating
|
||||
// the CoordCache in updateMetrics().
|
||||
|
@ -38,17 +38,12 @@ MetricsBase::MetricsBase(BufferView * b, FontInfo const & f, int w)
|
||||
{}
|
||||
|
||||
|
||||
|
||||
MetricsInfo::MetricsInfo()
|
||||
MetricsInfo::MetricsInfo(BufferView * bv, FontInfo const & font, int textwidth,
|
||||
MacroContext const & mc)
|
||||
: base(bv, font, textwidth), macrocontext(mc)
|
||||
{}
|
||||
|
||||
|
||||
MetricsInfo::MetricsInfo(BufferView * bv, FontInfo const & font, int textwidth)
|
||||
: base(bv, font, textwidth)
|
||||
{}
|
||||
|
||||
|
||||
|
||||
PainterInfo::PainterInfo(BufferView * bv, lyx::frontend::Painter & painter)
|
||||
: pain(painter), ltr_pos(false), erased_(false), full_repaint(true),
|
||||
background_color(Color_background)
|
||||
|
@ -5,6 +5,7 @@
|
||||
* Licence details can be found in the file COPYING.
|
||||
*
|
||||
* \author André Pönitz
|
||||
* \author Stefan Schimanski
|
||||
*
|
||||
* Full author contact details are available in file CREDITS.
|
||||
*/
|
||||
@ -25,6 +26,7 @@ class BufferView;
|
||||
namespace lyx {
|
||||
|
||||
namespace frontend { class Painter; }
|
||||
class MacroContext;
|
||||
|
||||
|
||||
/// Standard Sizes (mode styles)
|
||||
@ -72,10 +74,12 @@ public:
|
||||
///
|
||||
MetricsInfo();
|
||||
///
|
||||
MetricsInfo(BufferView * bv, FontInfo const & font, int textwidth);
|
||||
MetricsInfo(BufferView * bv, FontInfo const & font, int textwidth, MacroContext const & mc);
|
||||
|
||||
///
|
||||
MetricsBase base;
|
||||
/// The context to resolve macros
|
||||
MacroContext const & macrocontext;
|
||||
};
|
||||
|
||||
|
||||
|
@ -190,6 +190,10 @@ public:
|
||||
///
|
||||
ParagraphParameters params_;
|
||||
|
||||
/// position of the paragraph in the buffer. Only macros from
|
||||
/// paragraphs strictly smaller are visible in this paragraph
|
||||
unsigned int macrocontext_position_;
|
||||
|
||||
/// for recording and looking up changes
|
||||
Changes changes_;
|
||||
|
||||
@ -243,6 +247,7 @@ Paragraph::Private::Private(Paragraph * owner)
|
||||
: owner_(owner), inset_owner_(0), begin_of_body_(0)
|
||||
{
|
||||
id_ = paragraph_id++;
|
||||
macrocontext_position_ = 0;
|
||||
text_.reserve(100);
|
||||
}
|
||||
|
||||
@ -2489,6 +2494,18 @@ int Paragraph::checkBiblio(bool track_changes)
|
||||
}
|
||||
|
||||
|
||||
unsigned int Paragraph::macrocontextPosition() const
|
||||
{
|
||||
return d->macrocontext_position_;
|
||||
}
|
||||
|
||||
|
||||
void Paragraph::setMacrocontextPosition(unsigned int pos)
|
||||
{
|
||||
d->macrocontext_position_ = pos;
|
||||
}
|
||||
|
||||
|
||||
void Paragraph::checkAuthors(AuthorList const & authorList)
|
||||
{
|
||||
d->changes_.checkAuthors(authorList);
|
||||
|
@ -347,6 +347,15 @@ public:
|
||||
/// was previously past that position. Return 0 otherwise.
|
||||
int checkBiblio(bool track_changes);
|
||||
|
||||
/// To resolve macros properly the paragraphs are numbered.
|
||||
/// Every macro definition is stored with its paragraph number
|
||||
/// as well. Only those macros with a smaller number become
|
||||
/// visible in a paragraph (plus those in the same paragraph, but
|
||||
/// in an earlier inset.
|
||||
unsigned int macrocontextPosition() const;
|
||||
///
|
||||
void setMacrocontextPosition(unsigned int pos);
|
||||
|
||||
/// For each author, set 'used' to true if there is a change
|
||||
/// by this author in the paragraph.
|
||||
void checkAuthors(AuthorList const & authorList);
|
||||
|
@ -1298,7 +1298,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
|
||||
int const nargs = s1.empty() ? 0 : convert<int>(s1);
|
||||
string const s2 = token(s, ' ', 2);
|
||||
string const type = s2.empty() ? "newcommand" : s2;
|
||||
cur.insert(new MathMacroTemplate(from_utf8(token(s, ' ', 0)), nargs, from_utf8(type)));
|
||||
cur.insert(new MathMacroTemplate(from_utf8(token(s, ' ', 0)), nargs, false, from_utf8(type)));
|
||||
//cur.nextInset()->edit(cur, true);
|
||||
}
|
||||
break;
|
||||
|
@ -40,6 +40,9 @@
|
||||
#include "Text.h"
|
||||
#include "VSpace.h"
|
||||
|
||||
#include "mathed/MacroTable.h"
|
||||
#include "mathed/MathMacroTemplate.h"
|
||||
|
||||
#include "frontends/FontMetrics.h"
|
||||
#include "frontends/Painter.h"
|
||||
|
||||
@ -382,15 +385,40 @@ bool TextMetrics::redoParagraph(pit_type const pit)
|
||||
// FIXME: We should always use getFont(), see documentation of
|
||||
// noFontChange() in Inset.h.
|
||||
Font const bufferfont = buffer.params().getFont();
|
||||
MacroContext mc(buffer, par);
|
||||
InsetList::const_iterator ii = par.insetList().begin();
|
||||
InsetList::const_iterator iend = par.insetList().end();
|
||||
for (; ii != iend; ++ii) {
|
||||
// the macro must come here, _before_ the metric call, because
|
||||
// the macro should see itself to detect recursions. To find out
|
||||
// whether the macro definition is a redefinition it will look
|
||||
// at the MacroData::redefinition_. So it doesn't get confused
|
||||
// by the already existing macro definition of itself in the
|
||||
// macro context.
|
||||
if (ii->inset->lyxCode() == MATHMACRO_CODE) {
|
||||
// get macro data
|
||||
MathMacroTemplate const & macroTemplate
|
||||
= static_cast<MathMacroTemplate const &>(*ii->inset);
|
||||
|
||||
// valid?
|
||||
if (macroTemplate.validMacro()) {
|
||||
MacroData macro = macroTemplate.asMacroData();
|
||||
|
||||
// redefinition?
|
||||
macro.setRedefinition(mc.has(macroTemplate.name()));
|
||||
|
||||
// register macro (possibly overwrite the previous one of this paragraph)
|
||||
mc.insert(macroTemplate.name(), macro);
|
||||
}
|
||||
}
|
||||
|
||||
// do the metric calculation
|
||||
Dimension dim;
|
||||
int const w = max_width_ - leftMargin(max_width_, pit, ii->pos)
|
||||
- right_margin;
|
||||
Font const & font = ii->inset->noFontChange() ?
|
||||
bufferfont : getDisplayFont(pit, ii->pos);
|
||||
MetricsInfo mi(bv_, font.fontInfo(), w);
|
||||
MetricsInfo mi(bv_, font.fontInfo(), w, mc);
|
||||
ii->inset->metrics(mi, dim);
|
||||
Dimension const old_dim = pm.insetDimension(ii->inset);
|
||||
pm.setInsetDimension(ii->inset, dim);
|
||||
|
@ -542,18 +542,6 @@ Inset * readInset(Lexer & lex, Buffer const & buf)
|
||||
}
|
||||
|
||||
inset->read(buf, lex);
|
||||
|
||||
// FIXME: hack..
|
||||
if (inset->lyxCode() == MATHMACRO_CODE) {
|
||||
MathMacroTemplate const * tmpl =
|
||||
static_cast<MathMacroTemplate*>(inset.get());
|
||||
MacroTable::globalMacros().insert
|
||||
(tmpl->name(), tmpl->asMacroData());
|
||||
LYXERR(Debug::DEBUG)
|
||||
<< BOOST_CURRENT_FUNCTION
|
||||
<< ": creating local macro " << to_utf8(tmpl->name())
|
||||
<< endl;
|
||||
}
|
||||
}
|
||||
|
||||
return inset.release();
|
||||
|
@ -16,6 +16,9 @@
|
||||
#include "MathSupport.h"
|
||||
#include "InsetMathSqrt.h"
|
||||
|
||||
#include "InsetMathNest.h"
|
||||
#include "buffer.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "DocIterator.h"
|
||||
|
||||
@ -36,20 +39,25 @@ using std::size_t;
|
||||
|
||||
|
||||
MacroData::MacroData()
|
||||
: numargs_(0), lockCount_(0)
|
||||
: numargs_(0), lockCount_(0), redefinition_(false)
|
||||
{}
|
||||
|
||||
|
||||
MacroData::MacroData(docstring const & def, int numargs, docstring const & disp, string const & requires)
|
||||
: def_(def), numargs_(numargs), disp_(disp), requires_(requires), lockCount_(0)
|
||||
{}
|
||||
MacroData::MacroData(docstring const & definition, std::vector<docstring> const & defaults,
|
||||
int numargs, int optionals, docstring const & display, string const & requires)
|
||||
: definition_(definition), numargs_(numargs), display_(display),
|
||||
requires_(requires), lockCount_(0), redefinition_(false), optionals_(optionals),
|
||||
defaults_(defaults)
|
||||
{
|
||||
defaults_.resize(optionals);
|
||||
}
|
||||
|
||||
|
||||
void MacroData::expand(vector<MathData> const & args, MathData & to) const
|
||||
{
|
||||
InsetMathSqrt inset; // Hack. Any inset with a cell would do.
|
||||
// FIXME UNICODE
|
||||
asArray(disp_.empty() ? def_ : disp_, inset.cell(0));
|
||||
asArray(display_.empty() ? definition_ : display_, inset.cell(0));
|
||||
//lyxerr << "MathData::expand: args: " << args << endl;
|
||||
//lyxerr << "MathData::expand: ar: " << inset.cell(0) << endl;
|
||||
for (DocIterator it = doc_iterator_begin(inset); it; it.forwardChar()) {
|
||||
@ -70,6 +78,18 @@ void MacroData::expand(vector<MathData> const & args, MathData & to) const
|
||||
}
|
||||
|
||||
|
||||
int MacroData::optionals() const
|
||||
{
|
||||
return optionals_;
|
||||
}
|
||||
|
||||
|
||||
std::vector<docstring> const & MacroData::defaults() const
|
||||
{
|
||||
return defaults_;
|
||||
}
|
||||
|
||||
|
||||
// The global table.
|
||||
MacroTable & MacroTable::globalMacros()
|
||||
{
|
||||
@ -114,11 +134,45 @@ void MacroTable::dump()
|
||||
lyxerr << "\n------------------------------------------" << endl;
|
||||
for (const_iterator it = begin(); it != end(); ++it)
|
||||
lyxerr << to_utf8(it->first)
|
||||
<< " [" << to_utf8(it->second.def()) << "] : "
|
||||
<< " [" << to_utf8(it->second.disp()) << "] : "
|
||||
<< " [" << to_utf8(it->second.definition()) << "] : "
|
||||
<< " [" << to_utf8(it->second.display()) << "] : "
|
||||
<< endl;
|
||||
lyxerr << "------------------------------------------" << endl;
|
||||
}
|
||||
|
||||
|
||||
MacroContext::MacroContext(Buffer const & buf, Paragraph const & par)
|
||||
: buf_(buf), par_(par)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool MacroContext::has(docstring const & name) const
|
||||
{
|
||||
// check if it's a local macro
|
||||
if (macros_.has(name))
|
||||
return true;
|
||||
|
||||
// otherwise ask the buffer
|
||||
return buf_.hasMacro(name, par_);
|
||||
}
|
||||
|
||||
|
||||
MacroData const & MacroContext::get(docstring const & name) const
|
||||
{
|
||||
// check if it's a local macro
|
||||
if (macros_.has(name))
|
||||
return macros_.get(name);
|
||||
|
||||
// ask the buffer for its macros
|
||||
return buf_.getMacro(name, par_);
|
||||
}
|
||||
|
||||
|
||||
void MacroContext::insert(docstring const & name, MacroData const & data)
|
||||
{
|
||||
macros_.insert(name, data);
|
||||
}
|
||||
|
||||
|
||||
} // namespace lyx
|
||||
|
@ -5,6 +5,7 @@
|
||||
* Licence details can be found in the file COPYING.
|
||||
*
|
||||
* \author André Pönitz
|
||||
* \author Stefan Schimanski
|
||||
*
|
||||
* Full author contact details are available in file CREDITS.
|
||||
*/
|
||||
@ -21,7 +22,9 @@
|
||||
|
||||
namespace lyx {
|
||||
|
||||
class Buffer;
|
||||
class MathData;
|
||||
class Paragraph;
|
||||
|
||||
///
|
||||
class MacroData {
|
||||
@ -29,54 +32,77 @@ public:
|
||||
///
|
||||
MacroData();
|
||||
///
|
||||
MacroData(docstring const & def, int nargs, docstring const & disp, std::string const &);
|
||||
MacroData(docstring const & definition, std::vector<docstring> const & defaults,
|
||||
int nargs, int optionals,
|
||||
docstring const & display, std::string const & requires);
|
||||
///
|
||||
docstring def() const { return def_; }
|
||||
///
|
||||
docstring disp() const { return disp_; }
|
||||
docstring const & definition() const { return definition_; }
|
||||
///
|
||||
docstring const & display() const { return display_; }
|
||||
/// arity including optional arguments (if there is any)
|
||||
int numargs() const { return numargs_; }
|
||||
/// replace #1,#2,... by given MathAtom 0,1,..
|
||||
/// replace #1,#2,... by given MathAtom 0,1,.., _including_ the possible optional argument
|
||||
void expand(std::vector<MathData> const & from, MathData & to) const;
|
||||
/// number of optional arguments
|
||||
int optionals() const;
|
||||
///
|
||||
std::vector<docstring> const & defaults() const;
|
||||
///
|
||||
std::string requires() const { return requires_; }
|
||||
///
|
||||
std::string & requires() { return requires_; }
|
||||
/// lock while being drawn
|
||||
|
||||
/// lock while being drawn to avoid recursions
|
||||
int lock() const { return ++lockCount_; }
|
||||
/// is it being drawn?
|
||||
bool locked() const { return lockCount_ != 0; }
|
||||
///
|
||||
///
|
||||
void unlock() const { --lockCount_; BOOST_ASSERT(lockCount_ >= 0); }
|
||||
|
||||
|
||||
///
|
||||
bool redefinition() const { return redefinition_; }
|
||||
///
|
||||
void setRedefinition(bool redefined) { redefinition_ = redefined; }
|
||||
|
||||
///
|
||||
bool operator==(MacroData const & x) const {
|
||||
return def_ == x.def_ &&
|
||||
numargs_ == x.numargs_ &&
|
||||
disp_ == x.disp_ &&
|
||||
requires_ == x.requires_;
|
||||
return definition_ == x.definition_
|
||||
&& numargs_ == x.numargs_
|
||||
&& display_ == x.display_
|
||||
&& requires_ == x.requires_
|
||||
&& optionals_ == x.optionals_
|
||||
&& defaults_ == x.defaults_;
|
||||
}
|
||||
///
|
||||
bool operator!=(MacroData const & x) const { return !operator==(x); }
|
||||
|
||||
private:
|
||||
///
|
||||
docstring def_;
|
||||
docstring definition_;
|
||||
///
|
||||
int numargs_;
|
||||
///
|
||||
docstring disp_;
|
||||
docstring display_;
|
||||
///
|
||||
std::string requires_;
|
||||
///
|
||||
mutable int lockCount_;
|
||||
///
|
||||
bool redefinition_;
|
||||
///
|
||||
int optionals_;
|
||||
///
|
||||
std::vector<docstring> defaults_;
|
||||
};
|
||||
|
||||
|
||||
// This contains a table of "global" macros that are always accessible,
|
||||
// either because they implement a feature of standard LaTeX or some
|
||||
// hack to display certain contents nicely.
|
||||
|
||||
/// A lookup table of macro definitions.
|
||||
/**
|
||||
* This contains a table of "global" macros that are always accessible,
|
||||
* either because they implement a feature of standard LaTeX or some
|
||||
* hack to display certain contents nicely.
|
||||
*
|
||||
**/
|
||||
class MacroTable : public std::map<docstring, MacroData>
|
||||
{
|
||||
public:
|
||||
@ -93,11 +119,39 @@ public:
|
||||
|
||||
/// the global list
|
||||
static MacroTable & globalMacros();
|
||||
/// the local list hack
|
||||
//static MacroTable & localMacros();
|
||||
};
|
||||
|
||||
|
||||
/// A context to lookup macros at a certain position in a buffer.
|
||||
/**
|
||||
* The MacroContext is used during metrics calculation to resolve
|
||||
* macro instances according to the position of them in the buffer
|
||||
* document. Only macro definition in front of the macro instance
|
||||
* are visible and are resolved.
|
||||
*
|
||||
**/
|
||||
class MacroContext {
|
||||
public:
|
||||
/// construct context for insets in par (not including the ones defined in par itself)
|
||||
MacroContext(Buffer const & buf, Paragraph const & par);
|
||||
|
||||
/// Look for macro
|
||||
bool has(docstring const & name) const;
|
||||
/// Lookup macro
|
||||
MacroData const & get(docstring const & name) const;
|
||||
|
||||
/// Insert pre-digested macro definition
|
||||
void insert(docstring const & name, MacroData const & data);
|
||||
|
||||
private:
|
||||
/// context local macros
|
||||
MacroTable macros_;
|
||||
///
|
||||
Buffer const & buf_;
|
||||
///
|
||||
Paragraph const & par_;
|
||||
};
|
||||
|
||||
} // namespace lyx
|
||||
|
||||
#endif
|
||||
|
@ -4,6 +4,7 @@
|
||||
* Licence details can be found in the file COPYING.
|
||||
*
|
||||
* \author André Pönitz
|
||||
* \author Stefan Schimanski
|
||||
*
|
||||
* Full author contact details are available in file CREDITS.
|
||||
*/
|
||||
@ -11,10 +12,11 @@
|
||||
#include <config.h>
|
||||
|
||||
#include "MathData.h"
|
||||
#include "InsetMathBrace.h"
|
||||
#include "InsetMathFont.h"
|
||||
#include "InsetMathScript.h"
|
||||
#include "MathMacro.h"
|
||||
#include "MacroTable.h"
|
||||
#include "MathMacro.h"
|
||||
#include "MathStream.h"
|
||||
#include "MathSupport.h"
|
||||
#include "ReplaceData.h"
|
||||
@ -247,35 +249,14 @@ void MathData::metrics(MetricsInfo & mi, Dimension & dim) const
|
||||
return;
|
||||
}
|
||||
|
||||
const_cast<MathData*>(this)->updateMacros(mi);
|
||||
|
||||
dim.asc = 0;
|
||||
dim.wid = 0;
|
||||
Dimension d;
|
||||
atom_dims_.clear();
|
||||
//BufferView & bv = *mi.base.bv;
|
||||
//Buffer const & buf = bv.buffer();
|
||||
for (size_t i = 0, n = size(); i != n; ++i) {
|
||||
MathAtom const & at = operator[](i);
|
||||
#if 0
|
||||
MathMacro const * mac = at->asMacro();
|
||||
if (mac && buf.hasMacro(mac->name())) {
|
||||
MacroData const & tmpl = buf.getMacro(mac->name());
|
||||
int numargs = tmpl.numargs();
|
||||
if (i + numargs > n)
|
||||
numargs = n - i - 1;
|
||||
lyxerr << "metrics:found macro: " << mac->name()
|
||||
<< " numargs: " << numargs << endl;
|
||||
if (!isInside(bv.cursor(), *this, i + 1, i + numargs + 1)) {
|
||||
MathData args(begin() + i + 1, begin() + i + numargs + 1);
|
||||
MathData exp;
|
||||
tmpl.expand(args, exp);
|
||||
mac->setExpansion(exp, args);
|
||||
mac->metricsExpanded(mi, d);
|
||||
dim.wid += mac->widthExpanded();
|
||||
i += numargs;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
at->metrics(mi, d);
|
||||
atom_dims_.push_back(d);
|
||||
dim += d;
|
||||
@ -309,23 +290,6 @@ void MathData::draw(PainterInfo & pi, int x, int y) const
|
||||
|
||||
for (size_t i = 0, n = size(); i != n; ++i) {
|
||||
MathAtom const & at = operator[](i);
|
||||
#if 0
|
||||
Buffer const & buf = bv.buffer();
|
||||
// special macro handling
|
||||
MathMacro const * mac = at->asMacro();
|
||||
if (mac && buf.hasMacro(mac->name())) {
|
||||
MacroData const & tmpl = buf.getMacro(mac->name());
|
||||
int numargs = tmpl.numargs();
|
||||
if (i + numargs > n)
|
||||
numargs = n - i - 1;
|
||||
if (!isInside(bv.cursor(), *this, i + 1, i + numargs + 1)) {
|
||||
mac->drawExpanded(pi, x, y);
|
||||
x += mac->widthExpanded();
|
||||
i += numargs;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
bv.coordCache().insets().add(at.nucleus(), x, y);
|
||||
at->drawSelection(pi, x, y);
|
||||
at->draw(pi, x, y);
|
||||
@ -360,6 +324,355 @@ void MathData::drawT(TextPainter & pain, int x, int y) const
|
||||
}
|
||||
|
||||
|
||||
void MathData::updateMacros(MetricsInfo & mi)
|
||||
{
|
||||
Cursor & cur = mi.base.bv->cursor();
|
||||
|
||||
// go over the array and look for macros
|
||||
for (size_t i = 0; i < size(); ++i) {
|
||||
MathMacro * macroInset = operator[](i).nucleus()->asMacro();
|
||||
if (!macroInset)
|
||||
continue;
|
||||
|
||||
// get macro
|
||||
macroInset->updateMacro(mi);
|
||||
size_t macroNumArgs = 0;
|
||||
int macroOptionals = 0;
|
||||
MacroData const * macro = macroInset->macro();
|
||||
if (macro) {
|
||||
macroNumArgs = macro->numargs();
|
||||
macroOptionals = macro->optionals();
|
||||
}
|
||||
|
||||
// store old and compute new display mode
|
||||
MathMacro::DisplayMode newDisplayMode;
|
||||
MathMacro::DisplayMode oldDisplayMode = macroInset->displayMode();
|
||||
newDisplayMode = macroInset->computeDisplayMode(mi);
|
||||
|
||||
// arity changed or other reason to detach?
|
||||
if (oldDisplayMode == MathMacro::DISPLAY_NORMAL
|
||||
&& (macroInset->arity() != macroNumArgs
|
||||
|| macroInset->optionals() != macroOptionals
|
||||
|| newDisplayMode == MathMacro::DISPLAY_UNFOLDED)) {
|
||||
detachMacroParameters(cur, i);
|
||||
}
|
||||
|
||||
// the macro could have been copied while resizing this
|
||||
macroInset = operator[](i).nucleus()->asMacro();
|
||||
|
||||
// Cursor in \label?
|
||||
if (newDisplayMode != MathMacro::DISPLAY_UNFOLDED
|
||||
&& oldDisplayMode == MathMacro::DISPLAY_UNFOLDED) {
|
||||
// put cursor in front of macro
|
||||
int macroSlice = cur.find(macroInset);
|
||||
if (macroSlice != -1)
|
||||
cur.cutOff(macroSlice - 1);
|
||||
}
|
||||
|
||||
// update the display mode
|
||||
macroInset->setDisplayMode(newDisplayMode);
|
||||
|
||||
// arity changed?
|
||||
if (newDisplayMode == MathMacro::DISPLAY_NORMAL
|
||||
&& (macroInset->arity() != macroNumArgs
|
||||
|| macroInset->optionals() != macroOptionals)) {
|
||||
// is it a virgin macro which was never attached to parameters?
|
||||
bool fromInitToNormalMode
|
||||
= oldDisplayMode == MathMacro::DISPLAY_INIT
|
||||
&& newDisplayMode == MathMacro::DISPLAY_NORMAL;
|
||||
|
||||
// attach parameters
|
||||
attachMacroParameters(cur, i, macroNumArgs, macroOptionals,
|
||||
fromInitToNormalMode);
|
||||
}
|
||||
|
||||
// give macro the chance to adapt to new situation
|
||||
InsetMath * inset = operator[](i).nucleus();
|
||||
if (inset->asScriptInset())
|
||||
inset = inset->asScriptInset()->nuc()[0].nucleus();
|
||||
BOOST_ASSERT(inset->asMacro());
|
||||
inset->asMacro()->updateRepresentation(mi);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MathData::detachMacroParameters(Cursor & cur, const size_type macroPos)
|
||||
{
|
||||
MathMacro * macroInset = operator[](macroPos).nucleus()->asMacro();
|
||||
|
||||
// detach all arguments
|
||||
std::vector<MathData> detachedArgs;
|
||||
if (macroPos + 1 == size())
|
||||
// strip arguments if we are at the MathData end
|
||||
macroInset->detachArguments(detachedArgs, true);
|
||||
else
|
||||
macroInset->detachArguments(detachedArgs, false);
|
||||
|
||||
// find cursor slice
|
||||
int curMacroSlice = cur.find(macroInset);
|
||||
int curMacroIdx = -1;
|
||||
int curMacroPos = -1;
|
||||
std::vector<CursorSlice> argSlices;
|
||||
if (curMacroSlice != -1) {
|
||||
curMacroPos = cur[curMacroSlice].pos();
|
||||
curMacroIdx = cur[curMacroSlice].idx();
|
||||
cur.cutOff(curMacroSlice, argSlices);
|
||||
cur.pop_back();
|
||||
}
|
||||
|
||||
// only [] after the last non-empty argument can be dropped later
|
||||
size_t lastNonEmptyOptional = 0;
|
||||
for (size_t l = 0; l < detachedArgs.size() && l < macroInset->optionals(); ++l) {
|
||||
if (!detachedArgs[l].empty())
|
||||
lastNonEmptyOptional = l;
|
||||
}
|
||||
|
||||
// optional arguments to be put back?
|
||||
size_t p = macroPos + 1;
|
||||
size_t j = 0;
|
||||
for (; j < detachedArgs.size() && j < macroInset->optionals(); ++j) {
|
||||
// another non-empty parameter follows?
|
||||
bool canDropEmptyOptional = j >= lastNonEmptyOptional;
|
||||
|
||||
// then we can drop empty optional parameters
|
||||
if (detachedArgs[j].empty() && canDropEmptyOptional) {
|
||||
if (curMacroIdx == j)
|
||||
cur[curMacroSlice - 1].pos() = macroPos + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise we don't drop an empty optional, put it back normally
|
||||
MathData optarg;
|
||||
asArray(from_ascii("[]"), optarg);
|
||||
MathData & arg = detachedArgs[j];
|
||||
|
||||
// look for "]", i.e. put a brace around?
|
||||
InsetMathBrace * brace = 0;
|
||||
for (size_t q = 0; q < arg.size(); ++q) {
|
||||
if (arg[q]->getChar() == ']') {
|
||||
// put brace
|
||||
brace = new InsetMathBrace();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// put arg between []
|
||||
if (brace) {
|
||||
brace->cell(0) = arg;
|
||||
optarg.insert(1, MathAtom(brace));
|
||||
} else
|
||||
optarg.insert(1, arg);
|
||||
|
||||
// insert it into the array
|
||||
insert(p, optarg);
|
||||
p += optarg.size();
|
||||
|
||||
// cursor in optional argument of macro?
|
||||
if (curMacroIdx == j) {
|
||||
if (brace) {
|
||||
cur.append(0, curMacroPos);
|
||||
cur[curMacroSlice - 1].pos() = macroPos + 2;
|
||||
} else
|
||||
cur[curMacroSlice - 1].pos() = macroPos + 2 + curMacroPos;
|
||||
cur.append(argSlices);
|
||||
} else if (cur[curMacroSlice - 1].pos() >= int(p))
|
||||
// cursor right of macro
|
||||
cur[curMacroSlice - 1].pos() += optarg.size();
|
||||
}
|
||||
|
||||
// put them back into the MathData
|
||||
for (; j < detachedArgs.size(); ++j) {
|
||||
MathData const & arg = detachedArgs[j];
|
||||
if (arg.size() == 1 && !arg[0]->asScriptInset()) // && arg[0]->asCharInset())
|
||||
insert(p, arg[0]);
|
||||
else
|
||||
insert(p, MathAtom(new InsetMathBrace(arg)));
|
||||
|
||||
// cursor in j-th argument of macro?
|
||||
if (curMacroIdx == int(j)) {
|
||||
if (operator[](p).nucleus()->asBraceInset()) {
|
||||
cur[curMacroSlice - 1].pos() = p;
|
||||
cur.append(0, curMacroPos);
|
||||
cur.append(argSlices);
|
||||
} else {
|
||||
cur[curMacroSlice - 1].pos() = p; // + macroPos;
|
||||
cur.append(argSlices);
|
||||
}
|
||||
} else if (cur[curMacroSlice - 1].pos() >= int(p))
|
||||
++cur[curMacroSlice - 1].pos();
|
||||
|
||||
++p;
|
||||
}
|
||||
|
||||
// FIXME: proper anchor handling, this removes the selection
|
||||
cur.clearSelection();
|
||||
cur.updateInsets(&cur.bottom().inset());
|
||||
}
|
||||
|
||||
|
||||
void MathData::attachMacroParameters(Cursor & cur,
|
||||
const size_type macroPos, const size_type macroNumArgs,
|
||||
const int macroOptionals, const bool fromInitToNormalMode)
|
||||
{
|
||||
MathMacro * macroInset = operator[](macroPos).nucleus()->asMacro();
|
||||
|
||||
// start at atom behind the macro again, maybe with some new arguments from above
|
||||
// to add them back into the macro inset
|
||||
size_t p = macroPos + 1;
|
||||
size_t j = 0;
|
||||
std::vector<MathData>detachedArgs;
|
||||
MathAtom scriptToPutAround;
|
||||
|
||||
// find cursor slice again
|
||||
int thisSlice = cur.find(*this);
|
||||
int thisPos = -1;
|
||||
if (thisSlice != -1)
|
||||
thisPos = cur[thisSlice].pos();
|
||||
|
||||
// insert optional arguments?
|
||||
for (; j < macroOptionals && p < size(); ++j) {
|
||||
// is a [] block following which could be an optional parameter?
|
||||
if (operator[](p)->getChar() != '[') {
|
||||
detachedArgs.push_back(MathData());
|
||||
continue;
|
||||
}
|
||||
|
||||
// found optional argument, look for "]"
|
||||
size_t right = p + 1;
|
||||
for (; right < size(); ++right) {
|
||||
if (operator[](right)->getChar() == ']')
|
||||
// found right end
|
||||
break;
|
||||
}
|
||||
|
||||
// found?
|
||||
if (right < size()) {
|
||||
// add everything between [ and ] as optional argument
|
||||
MathData optarg(begin() + p + 1, begin() + right);
|
||||
// a brace?
|
||||
bool brace = false;
|
||||
if (optarg.size() == 1 && optarg[0]->asBraceInset()) {
|
||||
brace = true;
|
||||
detachedArgs.push_back(optarg[0]->asBraceInset()->cell(0));
|
||||
} else
|
||||
detachedArgs.push_back(optarg);
|
||||
// place cursor in optional argument of macro
|
||||
if (thisPos >= int(p) && thisPos <= int(right)) {
|
||||
int pos = std::max(0, thisPos - int(p) - 1);
|
||||
std::vector<CursorSlice> x;
|
||||
cur.cutOff(thisSlice, x);
|
||||
cur[thisSlice].pos() = macroPos;
|
||||
if (brace) {
|
||||
pos = x[0].pos();
|
||||
x.erase(x.begin());
|
||||
}
|
||||
cur.append(0, pos);
|
||||
cur.append(x);
|
||||
}
|
||||
p = right + 1;
|
||||
} else {
|
||||
// no ] found, so it's not an optional argument
|
||||
// Note: This was "macroPos = p" before, which probably
|
||||
// does not make sense. We want to stop with optional
|
||||
// argument handling instead, so go back to the beginning.
|
||||
j = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// insert normal arguments
|
||||
for (; j < macroNumArgs && p < size(); ++j) {
|
||||
MathAtom & cell = operator[](p);
|
||||
|
||||
// fix cursor
|
||||
std::vector<CursorSlice> argSlices;
|
||||
int argPos = 0;
|
||||
if (thisPos == int(p)) {
|
||||
cur.cutOff(thisSlice, argSlices);
|
||||
}
|
||||
|
||||
InsetMathBrace const * brace = cell->asBraceInset();
|
||||
if (brace) {
|
||||
// found brace, convert into argument
|
||||
detachedArgs.push_back(brace->cell(0));
|
||||
|
||||
// cursor inside of the brace or just in front of?
|
||||
if (thisPos == int(p) && !argSlices.empty()) {
|
||||
argPos = argSlices[0].pos();
|
||||
argSlices.erase(argSlices.begin());
|
||||
}
|
||||
} else if (cell->asScriptInset() && j + 1 == macroNumArgs) {
|
||||
// last inset with scripts without braces
|
||||
// -> they belong to the macro, not the argument
|
||||
InsetMathScript * script = cell.nucleus()->asScriptInset();
|
||||
if (script->nuc().size() == 1 && script->nuc()[0]->asBraceInset())
|
||||
// nucleus in brace? Unpack!
|
||||
detachedArgs.push_back(script->nuc()[0]->asBraceInset()->cell(0));
|
||||
else
|
||||
detachedArgs.push_back(script->nuc());
|
||||
|
||||
// script will be put around below
|
||||
scriptToPutAround = cell;
|
||||
|
||||
// this should only happen after loading, so make cursor handling simple
|
||||
if (thisPos >= int(macroPos) && thisPos <= int(macroPos + macroNumArgs)) {
|
||||
argSlices.clear();
|
||||
cur.append(0, 0);
|
||||
}
|
||||
} else {
|
||||
MathData array;
|
||||
array.insert(0, cell);
|
||||
detachedArgs.push_back(array);
|
||||
}
|
||||
|
||||
// put cursor in argument again
|
||||
if (thisPos == int(p)) {
|
||||
cur.append(j, argPos);
|
||||
cur.append(argSlices);
|
||||
cur[thisSlice].pos() = macroPos;
|
||||
}
|
||||
|
||||
++p;
|
||||
}
|
||||
|
||||
// attach arguments back to macro inset
|
||||
macroInset->attachArguments(detachedArgs, macroNumArgs, macroOptionals);
|
||||
|
||||
// found tail script? E.g. \foo{a}b^x
|
||||
if (scriptToPutAround.nucleus()) {
|
||||
// put macro into a script inset
|
||||
scriptToPutAround.nucleus()->asScriptInset()->nuc()[0]
|
||||
= operator[](macroPos);
|
||||
operator[](macroPos) = scriptToPutAround;
|
||||
|
||||
if (thisPos == int(macroPos))
|
||||
cur.append(0, 0);
|
||||
}
|
||||
|
||||
// remove them from the MathData
|
||||
erase(begin() + macroPos + 1, begin() + p);
|
||||
|
||||
// fix cursor if right of p
|
||||
if (thisPos >= int(p))
|
||||
cur[thisSlice].pos() -= p - (macroPos + 1);
|
||||
|
||||
// was the macro inset just inserted and was now folded?
|
||||
if (cur[thisSlice].pos() == int(macroPos + 1)
|
||||
&& fromInitToNormalMode
|
||||
&& macroInset->arity() > 0
|
||||
&& thisSlice + 1 == int(cur.depth())) {
|
||||
// then enter it if the cursor was just behind
|
||||
cur[thisSlice].pos() = macroPos;
|
||||
cur.push_back(CursorSlice(*macroInset));
|
||||
macroInset->idxFirst(cur);
|
||||
}
|
||||
|
||||
// FIXME: proper anchor handling, this removes the selection
|
||||
cur.updateInsets(&cur.bottom().inset());
|
||||
cur.clearSelection();
|
||||
}
|
||||
|
||||
|
||||
int MathData::pos2x(size_type pos) const
|
||||
{
|
||||
return pos2x(pos, 0);
|
||||
|
@ -7,6 +7,7 @@
|
||||
* \author Alejandro Aguilar Sierra
|
||||
* \author André Pönitz
|
||||
* \author Lars Gullik Bjønnes
|
||||
* \author Stefan Schimanski
|
||||
*
|
||||
* Full author contact details are available in file CREDITS.
|
||||
*/
|
||||
@ -16,8 +17,9 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "MathAtom.h"
|
||||
#include "Cursor.h"
|
||||
#include "Dimension.h"
|
||||
#include "MathAtom.h"
|
||||
|
||||
#include "support/docstream.h"
|
||||
|
||||
@ -27,6 +29,7 @@ namespace lyx {
|
||||
class BufferView;
|
||||
class LaTeXFeatures;
|
||||
class ReplaceData;
|
||||
class MathMacro;
|
||||
class MetricsInfo;
|
||||
class PainterInfo;
|
||||
class TextMetricsInfo;
|
||||
@ -158,11 +161,19 @@ protected:
|
||||
mutable int slevel_;
|
||||
mutable int sshift_;
|
||||
mutable int kerning_;
|
||||
|
||||
|
||||
private:
|
||||
/// is this an exact match at this position?
|
||||
bool find1(MathData const & ar, size_type pos) const;
|
||||
|
||||
/// attach/detach arguments to macros
|
||||
void updateMacros(MetricsInfo & mi);
|
||||
///
|
||||
void detachMacroParameters(Cursor & cur, const size_type macroPos);
|
||||
///
|
||||
void attachMacroParameters(Cursor & cur, const size_type macroPos,
|
||||
const size_type macroNumArgs, const int macroOptionals,
|
||||
const bool fromInitToNormalMode);
|
||||
///
|
||||
mutable std::vector<Dimension> atom_dims_;
|
||||
};
|
||||
|
@ -403,15 +403,7 @@ MathAtom createInsetMath(docstring const & s)
|
||||
if (s == "vphantom")
|
||||
return MathAtom(new InsetMathPhantom(InsetMathPhantom::vphantom));
|
||||
|
||||
if (MacroTable::globalMacros().has(s))
|
||||
return MathAtom(new MathMacro(s,
|
||||
MacroTable::globalMacros().get(s).numargs()));
|
||||
//if (MacroTable::localMacros().has(s))
|
||||
// return MathAtom(new MathMacro(s,
|
||||
// MacroTable::localMacros().get(s).numargs()));
|
||||
|
||||
//lyxerr << "creating unknown inset '" << s << "'" << endl;
|
||||
return MathAtom(new InsetMathUnknown(s));
|
||||
return MathAtom(new MathMacro(s));
|
||||
}
|
||||
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
*
|
||||
* \author Alejandro Aguilar Sierra
|
||||
* \author André Pönitz
|
||||
* \author Stefan Schimanski
|
||||
*
|
||||
* Full author contact details are available in file CREDITS.
|
||||
*/
|
||||
@ -17,12 +18,17 @@
|
||||
#include "MathStream.h"
|
||||
|
||||
#include "Buffer.h"
|
||||
#include "BufferView.h"
|
||||
#include "Cursor.h"
|
||||
#include "debug.h"
|
||||
#include "BufferView.h"
|
||||
#include "LaTeXFeatures.h"
|
||||
#include "FuncStatus.h"
|
||||
#include "FuncRequest.h"
|
||||
#include "Undo.h"
|
||||
|
||||
#include "frontends/Painter.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace lyx {
|
||||
|
||||
@ -30,70 +36,85 @@ using std::string;
|
||||
using std::max;
|
||||
|
||||
|
||||
/// This class is the value of a macro argument, technically
|
||||
/// a wrapper of the cells of MathMacro.
|
||||
class MathMacroArgumentValue : public InsetMath {
|
||||
/// A proxy for the macro values
|
||||
class ArgumentProxy : public InsetMath {
|
||||
public:
|
||||
///
|
||||
MathMacroArgumentValue(MathMacro const & mathMacro, size_t idx)
|
||||
ArgumentProxy(MathMacro & mathMacro, size_t idx)
|
||||
: mathMacro_(mathMacro), idx_(idx) {}
|
||||
///
|
||||
void metrics(MetricsInfo & mi, Dimension & dim) const;
|
||||
ArgumentProxy(MathMacro & mathMacro, size_t idx, docstring const & def)
|
||||
: mathMacro_(mathMacro), idx_(idx)
|
||||
{
|
||||
asArray(def, def_);
|
||||
}
|
||||
///
|
||||
void draw(PainterInfo &, int x, int y) const;
|
||||
void metrics(MetricsInfo & mi, Dimension & dim) const {
|
||||
mathMacro_.macro()->unlock();
|
||||
mathMacro_.cell(idx_).metrics(mi, dim);
|
||||
if (!mathMacro_.editing() && !def_.empty())
|
||||
def_.metrics(mi, dim);
|
||||
mathMacro_.macro()->lock();
|
||||
}
|
||||
///
|
||||
void draw(PainterInfo & pi, int x, int y) const {
|
||||
if (mathMacro_.editing()) {
|
||||
pi.pain.leaveMonochromeMode();
|
||||
mathMacro_.cell(idx_).draw(pi, x, y);
|
||||
// FIXME: use real min/max colors here, not necessarely the ones of MathMacro are set
|
||||
pi.pain.enterMonochromeMode(Color_mathbg, Color_mathmacroblend);
|
||||
} else {
|
||||
if (def_.empty())
|
||||
mathMacro_.cell(idx_).draw(pi, x, y);
|
||||
else {
|
||||
mathMacro_.cell(idx_).setXY(*pi.base.bv, x, y);
|
||||
def_.draw(pi, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
///
|
||||
size_t idx() const { return idx_; }
|
||||
///
|
||||
int kerning() const { return mathMacro_.cell(idx_).kerning(); }
|
||||
|
||||
private:
|
||||
Inset * clone() const;
|
||||
MathMacro const & mathMacro_;
|
||||
///
|
||||
Inset * clone() const
|
||||
{
|
||||
return new ArgumentProxy(*this);
|
||||
}
|
||||
///
|
||||
MathMacro & mathMacro_;
|
||||
///
|
||||
size_t idx_;
|
||||
///
|
||||
MathData def_;
|
||||
};
|
||||
|
||||
|
||||
Inset * MathMacroArgumentValue::clone() const
|
||||
{
|
||||
return new MathMacroArgumentValue(*this);
|
||||
}
|
||||
|
||||
|
||||
void MathMacroArgumentValue::metrics(MetricsInfo & mi, Dimension & dim) const
|
||||
{
|
||||
// unlock outer macro in arguments, and lock it again later
|
||||
MacroData const & macro = MacroTable::globalMacros().get(mathMacro_.name());
|
||||
macro.unlock();
|
||||
mathMacro_.cell(idx_).metrics(mi, dim);
|
||||
macro.lock();
|
||||
}
|
||||
|
||||
|
||||
void MathMacroArgumentValue::draw(PainterInfo & pi, int x, int y) const
|
||||
{
|
||||
// unlock outer macro in arguments, and lock it again later
|
||||
MacroData const & macro = MacroTable::globalMacros().get(mathMacro_.name());
|
||||
macro.unlock();
|
||||
mathMacro_.cell(idx_).draw(pi, x, y);
|
||||
macro.lock();
|
||||
}
|
||||
|
||||
|
||||
MathMacro::MathMacro(docstring const & name, int numargs)
|
||||
: InsetMathNest(numargs), name_(name), editing_(false)
|
||||
MathMacro::MathMacro(docstring const & name)
|
||||
: InsetMathNest(0), name_(name), displayMode_(DISPLAY_INIT),
|
||||
attachedArgsNum_(0), previousCurIdx_(-1),
|
||||
optionals_(0), nextFoldMode_(true),
|
||||
macro_(0), editing_(false), needsUpdate_(false)
|
||||
{}
|
||||
|
||||
|
||||
Inset * MathMacro::clone() const
|
||||
{
|
||||
MathMacro * x = new MathMacro(*this);
|
||||
x->expanded_ = MathData();
|
||||
x->macroBackup_ = MacroData();
|
||||
return x;
|
||||
MathMacro * copy = new MathMacro(*this);
|
||||
copy->needsUpdate_ = true;
|
||||
copy->expanded_.cell(0).clear();
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
||||
docstring MathMacro::name() const
|
||||
{
|
||||
return name_;
|
||||
if (displayMode_ == DISPLAY_UNFOLDED)
|
||||
return asString(cell(0));
|
||||
else
|
||||
return name_;
|
||||
}
|
||||
|
||||
|
||||
@ -106,166 +127,370 @@ void MathMacro::cursorPos(BufferView const & bv,
|
||||
}
|
||||
|
||||
|
||||
int MathMacro::cursorIdx(Cursor const & cur) const {
|
||||
for (size_t i = 0; i != cur.depth(); ++i)
|
||||
if (&cur[i].inset() == this)
|
||||
return cur[i].idx();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
bool MathMacro::editMode(Cursor const & cur) const {
|
||||
// find this in cursor trace
|
||||
for (size_t i = 0; i != cur.depth(); ++i)
|
||||
if (&cur[i].inset() == this) {
|
||||
// look if there is no other macro in edit mode above
|
||||
++i;
|
||||
for (; i != cur.depth(); ++i) {
|
||||
MathMacro const * macro = dynamic_cast<MathMacro const *>(&cur[i].inset());
|
||||
if (macro && macro->displayMode() == DISPLAY_NORMAL)
|
||||
return false;
|
||||
}
|
||||
|
||||
// ok, none found, I am the highest one
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const
|
||||
{
|
||||
kerning_ = 0;
|
||||
if (!MacroTable::globalMacros().has(name())) {
|
||||
mathed_string_dim(mi.base.font, "Unknown: " + name(), dim);
|
||||
// calculate new metrics according to display mode
|
||||
if (displayMode_ == DISPLAY_INIT) {
|
||||
mathed_string_dim(mi.base.font, from_ascii("\\") + name(), dim);
|
||||
} else if (displayMode_ == DISPLAY_UNFOLDED) {
|
||||
cell(0).metrics(mi, dim);
|
||||
Dimension bsdim;
|
||||
mathed_string_dim(mi.base.font, from_ascii("\\"), bsdim);
|
||||
dim.wid += bsdim.width() + 1;
|
||||
dim.asc = std::max(bsdim.ascent(), dim.ascent());
|
||||
dim.des = std::max(bsdim.descent(), dim.descent());
|
||||
metricsMarkers(dim);
|
||||
} else {
|
||||
MacroData const & macro = MacroTable::globalMacros().get(name());
|
||||
BOOST_ASSERT(macro_ != 0);
|
||||
|
||||
if (macroBackup_ != macro)
|
||||
updateExpansion();
|
||||
// calculate metric finally
|
||||
macro_->lock();
|
||||
expanded_.cell(0).metrics(mi, dim);
|
||||
macro_->unlock();
|
||||
|
||||
if (macro.locked()) {
|
||||
mathed_string_dim(mi.base.font, "Self reference: " + name(), dim);
|
||||
} else if (editing(mi.base.bv)) {
|
||||
// calculate dimension with label while editing
|
||||
if (editing_) {
|
||||
FontInfo font = mi.base.font;
|
||||
augmentFont(font, from_ascii("lyxtex"));
|
||||
tmpl_.metrics(mi, dim);
|
||||
// FIXME UNICODE
|
||||
dim.wid += mathed_string_width(font, name()) + 10;
|
||||
// FIXME UNICODE
|
||||
int ww = mathed_string_width(font, from_ascii("#1: "));
|
||||
for (idx_type i = 0; i < nargs(); ++i) {
|
||||
MathData const & c = cell(i);
|
||||
Dimension dimc;
|
||||
c.metrics(mi, dimc);
|
||||
dim.wid = max(dim.wid, dimc.width() + ww);
|
||||
dim.des += dimc.height() + 10;
|
||||
}
|
||||
editing_ = true;
|
||||
} else {
|
||||
macro.lock();
|
||||
expanded_.metrics(mi, dim);
|
||||
macro.unlock();
|
||||
kerning_ = expanded_.kerning();
|
||||
editing_ = false;
|
||||
Dimension namedim;
|
||||
mathed_string_dim(font, name(), namedim);
|
||||
#if 0
|
||||
dim.wid += 2 + namedim.wid + 2 + 2;
|
||||
dim.asc = std::max(dim.asc, namedim.asc) + 2;
|
||||
dim.des = std::max(dim.des, namedim.des) + 2;
|
||||
#endif
|
||||
dim.wid = std::max(1 + namedim.wid + 1, 2 + dim.wid + 2);
|
||||
dim.asc += 1 + namedim.height() + 1;
|
||||
dim.des += 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Cache the inset dimension.
|
||||
setDimCache(mi, dim);
|
||||
}
|
||||
|
||||
|
||||
int MathMacro::kerning() const {
|
||||
if (displayMode_ == DISPLAY_NORMAL && !editing_)
|
||||
return expanded_.kerning();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void MathMacro::updateMacro(MetricsInfo & mi)
|
||||
{
|
||||
if (validName() && mi.macrocontext.has(name())) {
|
||||
macro_ = &mi.macrocontext.get(name());
|
||||
if (macroBackup_ != *macro_) {
|
||||
macroBackup_ = *macro_;
|
||||
needsUpdate_ = true;
|
||||
}
|
||||
} else {
|
||||
macro_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MathMacro::updateRepresentation(MetricsInfo & mi)
|
||||
{
|
||||
// index of child where the cursor is (or -1 if none is edited)
|
||||
int curIdx = cursorIdx(mi.base.bv->cursor());
|
||||
previousCurIdx_ = curIdx;
|
||||
|
||||
// known macro?
|
||||
if (macro_) {
|
||||
requires_ = macro_->requires();
|
||||
|
||||
if (displayMode_ == DISPLAY_NORMAL) {
|
||||
// set edit mode to draw box around if needed
|
||||
bool prevEditing = editing_;
|
||||
editing_ = editMode(mi.base.bv->cursor());
|
||||
|
||||
// editMode changed and we have to switch default value and hole of optional?
|
||||
if (optionals_ > 0 && nargs() > 0 &&
|
||||
prevEditing != editing_)
|
||||
needsUpdate_ = true;
|
||||
|
||||
// macro changed?
|
||||
if (needsUpdate_) {
|
||||
needsUpdate_ = false;
|
||||
|
||||
// get default values of macro
|
||||
std::vector<docstring> const & defaults = macro_->defaults();
|
||||
|
||||
// create MathMacroArgumentValue objects pointing to the cells of the macro
|
||||
std::vector<MathData> values(nargs());
|
||||
for (size_t i = 0; i < nargs(); ++i) {
|
||||
if (!cell(i).empty() || i >= defaults.size() ||
|
||||
defaults[i].empty() || curIdx == (int)i)
|
||||
values[i].insert(0, MathAtom(new ArgumentProxy(*this, i)));
|
||||
else
|
||||
values[i].insert(0, MathAtom(new ArgumentProxy(*this, i, defaults[i])));
|
||||
}
|
||||
|
||||
// expanding macro with the values
|
||||
macro_->expand(values, expanded_.cell(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MathMacro::draw(PainterInfo & pi, int x, int y) const
|
||||
{
|
||||
if (!MacroTable::globalMacros().has(name())) {
|
||||
// FIXME UNICODE
|
||||
drawStrRed(pi, x, y, "Unknown: " + name());
|
||||
} else {
|
||||
MacroData const & macro = MacroTable::globalMacros().get(name());
|
||||
Dimension const dim = dimension(*pi.base.bv);
|
||||
|
||||
// warm up cache
|
||||
setPosCache(pi, x, y);
|
||||
int expx = x;
|
||||
int expy = y;
|
||||
|
||||
if (displayMode_ == DISPLAY_INIT) {
|
||||
PainterInfo pi2(pi.base.bv, pi.pain);
|
||||
pi2.base.font.setColor(macro_ ? Color_latex : Color_error);
|
||||
//pi2.base.style = LM_ST_TEXT;
|
||||
pi2.pain.text(x, y, from_ascii("\\") + name(), pi2.base.font);
|
||||
} else if (displayMode_ == DISPLAY_UNFOLDED) {
|
||||
PainterInfo pi2(pi.base.bv, pi.pain);
|
||||
pi2.base.font.setColor(macro_ ? Color_latex : Color_error);
|
||||
//pi2.base.style = LM_ST_TEXT;
|
||||
pi2.pain.text(x, y, from_ascii("\\"), pi2.base.font);
|
||||
x += mathed_string_width(pi2.base.font, from_ascii("\\")) + 1;
|
||||
cell(0).draw(pi2, x, y);
|
||||
drawMarkers(pi2, expx, expy);
|
||||
} else {
|
||||
// warm up cells
|
||||
for (size_t i = 0; i < nargs(); ++i)
|
||||
cell(i).setXY(*pi.base.bv, x, y);
|
||||
|
||||
if (macro.locked()) {
|
||||
// FIXME UNICODE
|
||||
drawStrRed(pi, x, y, "Self reference: " + name());
|
||||
} else if (editing_) {
|
||||
if (editing_) {
|
||||
// draw header and rectangle around
|
||||
FontInfo font = pi.base.font;
|
||||
augmentFont(font, from_ascii("lyxtex"));
|
||||
Dimension const dim = dimension(*pi.base.bv);
|
||||
Dimension const & dim_tmpl = tmpl_.dimension(*pi.base.bv);
|
||||
int h = y - dim.ascent() + 2 + dim_tmpl.ascent();
|
||||
pi.pain.text(x + 3, h, name(), font);
|
||||
int const w = mathed_string_width(font, name());
|
||||
tmpl_.draw(pi, x + w + 12, h);
|
||||
h += dim_tmpl.descent();
|
||||
Dimension ldim;
|
||||
docstring t = from_ascii("#1: ");
|
||||
mathed_string_dim(font, t, ldim);
|
||||
for (idx_type i = 0; i < nargs(); ++i) {
|
||||
MathData const & c = cell(i);
|
||||
Dimension const & dimc = c.dimension(*pi.base.bv);
|
||||
h += max(dimc.ascent(), ldim.asc) + 5;
|
||||
c.draw(pi, x + ldim.wid, h);
|
||||
char_type str[] = { '#', '1', ':', '\0' };
|
||||
str[1] += static_cast<char_type>(i);
|
||||
pi.pain.text(x + 3, h, str, font);
|
||||
h += max(dimc.descent(), ldim.des) + 5;
|
||||
}
|
||||
} else {
|
||||
macro.lock();
|
||||
expanded_.draw(pi, x, y);
|
||||
macro.unlock();
|
||||
}
|
||||
font.setSize(FONT_SIZE_TINY);
|
||||
font.setColor(Color_mathmacrolabel);
|
||||
Dimension namedim;
|
||||
mathed_string_dim(font, name(), namedim);
|
||||
#if 0
|
||||
pi.pain.fillRectangle(x, y - dim.asc, 2 + namedim.width() + 2, dim.height(), Color_mathmacrobg);
|
||||
pi.pain.text(x + 2, y, name(), font);
|
||||
expx += 2 + namew + 2;
|
||||
#endif
|
||||
pi.pain.fillRectangle(x, y - dim.asc, dim.wid, 1 + namedim.height() + 1, Color_mathmacrobg);
|
||||
pi.pain.text(x + 1, y - dim.asc + namedim.asc + 2, name(), font);
|
||||
expx += (dim.wid - expanded_.cell(0).dimension(*pi.base.bv).width()) / 2;
|
||||
|
||||
// edit mode changed?
|
||||
if (editing_ != editing(pi.base.bv) || macroBackup_ != macro)
|
||||
pi.base.bv->cursor().updateFlags(Update::Force);
|
||||
pi.pain.enterMonochromeMode(Color_mathbg, Color_mathmacroblend);
|
||||
expanded_.cell(0).draw(pi, expx, expy);
|
||||
pi.pain.leaveMonochromeMode();
|
||||
} else
|
||||
expanded_.cell(0).draw(pi, expx, expy);
|
||||
|
||||
// draw frame while editing
|
||||
if (editing_)
|
||||
pi.pain.rectangle(x, y - dim.asc, dim.wid, dim.height(), Color_mathmacroframe);
|
||||
}
|
||||
|
||||
// another argument selected?
|
||||
int curIdx = cursorIdx(pi.base.bv->cursor());
|
||||
if (previousCurIdx_ != curIdx || editing_ != editMode(pi.base.bv->cursor()))
|
||||
pi.base.bv->cursor().updateFlags(Update::Force);
|
||||
}
|
||||
|
||||
|
||||
void MathMacro::drawSelection(PainterInfo & pi, int x, int y) const
|
||||
{
|
||||
// We may have 0 arguments, but InsetMathNest requires at least one.
|
||||
if (nargs() > 0)
|
||||
if (cells_.size() > 0)
|
||||
InsetMathNest::drawSelection(pi, x, y);
|
||||
}
|
||||
|
||||
|
||||
void MathMacro::setDisplayMode(MathMacro::DisplayMode mode)
|
||||
{
|
||||
if (displayMode_ != mode) {
|
||||
// transfer name if changing from or to DISPLAY_UNFOLDED
|
||||
if (mode == DISPLAY_UNFOLDED) {
|
||||
cells_.resize(1);
|
||||
asArray(name_, cell(0));
|
||||
} else if (displayMode_ == DISPLAY_UNFOLDED) {
|
||||
name_ = asString(cell(0));
|
||||
cells_.resize(0);
|
||||
}
|
||||
|
||||
displayMode_ = mode;
|
||||
needsUpdate_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MathMacro::DisplayMode MathMacro::computeDisplayMode(MetricsInfo const & mi) const
|
||||
{
|
||||
if (nextFoldMode_ == true && macro_ && !macro_->locked())
|
||||
return DISPLAY_NORMAL;
|
||||
else
|
||||
return DISPLAY_UNFOLDED;
|
||||
}
|
||||
|
||||
|
||||
bool MathMacro::validName() const
|
||||
{
|
||||
docstring n = name();
|
||||
|
||||
// empty name?
|
||||
if (n.size() == 0)
|
||||
return false;
|
||||
|
||||
// converting back and force doesn't swallow anything?
|
||||
/*MathData ma;
|
||||
asArray(n, ma);
|
||||
if (asString(ma) != n)
|
||||
return false;*/
|
||||
|
||||
// valid characters?
|
||||
for (size_t i = 0; i<n.size(); ++i) {
|
||||
if (!(n[i] >= 'a' && n[i] <= 'z') &&
|
||||
!(n[i] >= 'A' && n[i] <= 'Z'))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void MathMacro::validate(LaTeXFeatures & features) const
|
||||
{
|
||||
string const require = MacroTable::globalMacros().get(name()).requires();
|
||||
if (!require.empty())
|
||||
features.require(require);
|
||||
if (!requires_.empty())
|
||||
features.require(requires_);
|
||||
|
||||
if (name() == "binom" || name() == "mathcircumflex")
|
||||
features.require(to_utf8(name()));
|
||||
}
|
||||
|
||||
|
||||
void MathMacro::edit(Cursor & cur, bool left)
|
||||
{
|
||||
cur.updateFlags(Update::Force);
|
||||
InsetMathNest::edit(cur, left);
|
||||
}
|
||||
|
||||
|
||||
Inset * MathMacro::editXY(Cursor & cur, int x, int y)
|
||||
{
|
||||
// We may have 0 arguments, but InsetMathNest requires at least one.
|
||||
if (nargs() > 0) {
|
||||
// Prevent crash due to cold coordcache
|
||||
// FIXME: This is only a workaround, the call of
|
||||
// InsetMathNest::editXY is correct. The correct fix would
|
||||
// ensure that the coordcache of the arguments is valid.
|
||||
if (!editing(&cur.bv())) {
|
||||
edit(cur, true);
|
||||
return this;
|
||||
}
|
||||
return InsetMathNest::editXY(cur, x, y);
|
||||
}
|
||||
return this;
|
||||
cur.updateFlags(Update::Force);
|
||||
return InsetMathNest::editXY(cur, x, y);
|
||||
} else
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
bool MathMacro::idxFirst(Cursor & cur) const
|
||||
void MathMacro::removeArgument(size_t pos) {
|
||||
if (displayMode_ == DISPLAY_NORMAL) {
|
||||
BOOST_ASSERT(pos >= 0 && pos < cells_.size());
|
||||
cells_.erase(cells_.begin() + pos);
|
||||
if (pos < attachedArgsNum_)
|
||||
--attachedArgsNum_;
|
||||
if (pos < optionals_) {
|
||||
--optionals_;
|
||||
}
|
||||
|
||||
needsUpdate_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MathMacro::insertArgument(size_t pos) {
|
||||
if (displayMode_ == DISPLAY_NORMAL) {
|
||||
BOOST_ASSERT(pos >= 0 && pos <= cells_.size());
|
||||
cells_.insert(cells_.begin() + pos, MathData());
|
||||
if (pos < attachedArgsNum_)
|
||||
++attachedArgsNum_;
|
||||
if (pos < optionals_)
|
||||
++optionals_;
|
||||
|
||||
needsUpdate_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MathMacro::detachArguments(std::vector<MathData> & args, bool strip)
|
||||
{
|
||||
BOOST_ASSERT(displayMode_ == DISPLAY_NORMAL);
|
||||
args = cells_;
|
||||
|
||||
// strip off empty cells, but not more than arity-attachedArgsNum_
|
||||
if (strip) {
|
||||
size_t i;
|
||||
for (i = cells_.size(); i > attachedArgsNum_; --i)
|
||||
if (!cell(i - 1).empty()) break;
|
||||
args.resize(i);
|
||||
}
|
||||
|
||||
attachedArgsNum_ = 0;
|
||||
expanded_.cell(0) = MathData();
|
||||
cells_.resize(0);
|
||||
|
||||
needsUpdate_ = true;
|
||||
}
|
||||
|
||||
|
||||
void MathMacro::attachArguments(std::vector<MathData> const & args, size_t arity, int optionals)
|
||||
{
|
||||
BOOST_ASSERT(displayMode_ == DISPLAY_NORMAL);
|
||||
cells_ = args;
|
||||
attachedArgsNum_ = args.size();
|
||||
cells_.resize(arity);
|
||||
expanded_.cell(0) = MathData();
|
||||
optionals_ = optionals;
|
||||
|
||||
needsUpdate_ = true;
|
||||
}
|
||||
|
||||
|
||||
bool MathMacro::idxFirst(Cursor & cur) const
|
||||
{
|
||||
cur.updateFlags(Update::Force);
|
||||
return InsetMathNest::idxFirst(cur);
|
||||
}
|
||||
|
||||
|
||||
bool MathMacro::idxLast(Cursor & cur) const
|
||||
bool MathMacro::idxLast(Cursor & cur) const
|
||||
{
|
||||
cur.updateFlags(Update::Force);
|
||||
return InsetMathNest::idxLast(cur);
|
||||
}
|
||||
|
||||
|
||||
bool MathMacro::idxUpDown(Cursor & cur, bool up) const
|
||||
{
|
||||
if (up) {
|
||||
if (cur.idx() == 0)
|
||||
return false;
|
||||
--cur.idx();
|
||||
} else {
|
||||
if (cur.idx() + 1 >= nargs())
|
||||
return false;
|
||||
++cur.idx();
|
||||
}
|
||||
cur.pos() = cell(cur.idx()).x2pos(cur.x_target());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool MathMacro::notifyCursorLeaves(Cursor & cur)
|
||||
{
|
||||
cur.updateFlags(Update::Force);
|
||||
@ -273,38 +498,98 @@ bool MathMacro::notifyCursorLeaves(Cursor & cur)
|
||||
}
|
||||
|
||||
|
||||
void MathMacro::fold(Cursor & cur)
|
||||
{
|
||||
if (!nextFoldMode_) {
|
||||
nextFoldMode_ = true;
|
||||
cur.updateFlags(Update::Force);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MathMacro::unfold(Cursor & cur)
|
||||
{
|
||||
if (nextFoldMode_) {
|
||||
nextFoldMode_ = false;
|
||||
cur.updateFlags(Update::Force);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool MathMacro::folded() const
|
||||
{
|
||||
return nextFoldMode_;
|
||||
}
|
||||
|
||||
|
||||
void MathMacro::write(WriteStream & os) const
|
||||
{
|
||||
if (displayMode_ == DISPLAY_NORMAL) {
|
||||
BOOST_ASSERT(macro_);
|
||||
|
||||
os << "\\" << name();
|
||||
bool first = true;
|
||||
size_t i = 0;
|
||||
|
||||
// Use macroBackup_ instead of macro_ here, because
|
||||
// this is outside the metrics/draw calls, hence the macro_
|
||||
// variable can point to a MacroData which was freed already.
|
||||
std::vector<docstring> const & defaults = macroBackup_.defaults();
|
||||
|
||||
// Optional argument
|
||||
if (os.latex()) {
|
||||
if (i < optionals_) {
|
||||
// the first real optional, the others are non-optional in latex
|
||||
if (!cell(i).empty()) {
|
||||
first = false;
|
||||
os << "[" << cell(0) << "]";
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
} else {
|
||||
// In lyx mode print all in any case
|
||||
for (; i < cells_.size() && i < optionals_; ++i) {
|
||||
first = false;
|
||||
os << "[" << cell(i) << "]";
|
||||
}
|
||||
}
|
||||
|
||||
for (; i < cells_.size(); ++i) {
|
||||
if (cell(i).empty() && i < optionals_) {
|
||||
os << "{" << defaults[i] << "}";
|
||||
} else if (cell(i).size() == 1 && cell(i)[0].nucleus()->asCharInset()) {
|
||||
if (first)
|
||||
os << " ";
|
||||
os << cell(i);
|
||||
} else
|
||||
os << "{" << cell(i) << "}";
|
||||
first = false;
|
||||
}
|
||||
if (first)
|
||||
os.pendingSpace(true);
|
||||
} else {
|
||||
os << "\\" << name() << " ";
|
||||
os.pendingSpace(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MathMacro::maple(MapleStream & os) const
|
||||
{
|
||||
updateExpansion();
|
||||
lyx::maple(expanded_, os);
|
||||
lyx::maple(expanded_.cell(0), os);
|
||||
}
|
||||
|
||||
|
||||
void MathMacro::mathmlize(MathStream & os) const
|
||||
{
|
||||
updateExpansion();
|
||||
lyx::mathmlize(expanded_, os);
|
||||
lyx::mathmlize(expanded_.cell(0), os);
|
||||
}
|
||||
|
||||
|
||||
void MathMacro::octave(OctaveStream & os) const
|
||||
{
|
||||
updateExpansion();
|
||||
lyx::octave(expanded_, os);
|
||||
}
|
||||
|
||||
|
||||
void MathMacro::updateExpansion() const
|
||||
{
|
||||
MacroData const & macro = MacroTable::globalMacros().get(name());
|
||||
|
||||
// create MathMacroArgumentValue object pointing to the cells of the macro
|
||||
std::vector<MathData> values(nargs());
|
||||
for (size_t i = 0; i != nargs(); ++i)
|
||||
values[i].insert(0, MathAtom(new MathMacroArgumentValue(*this, i)));
|
||||
macro.expand(values, expanded_);
|
||||
asArray(macro.def(), tmpl_);
|
||||
macroBackup_ = macro;
|
||||
lyx::octave(expanded_.cell(0), os);
|
||||
}
|
||||
|
||||
|
||||
|
@ -14,10 +14,9 @@
|
||||
#define MATH_MACRO_H
|
||||
|
||||
#include "InsetMathNest.h"
|
||||
#include "MathData.h"
|
||||
#include "InsetMathNest.h"
|
||||
#include "InsetMathSqrt.h"
|
||||
#include "MacroTable.h"
|
||||
|
||||
#include "MathData.h"
|
||||
|
||||
namespace lyx {
|
||||
|
||||
@ -26,11 +25,13 @@ namespace lyx {
|
||||
class MathMacro : public InsetMathNest {
|
||||
public:
|
||||
/// A macro can be built from an existing template
|
||||
MathMacro(docstring const & name, int numargs);
|
||||
MathMacro(docstring const & name);
|
||||
///
|
||||
virtual MathMacro * asMacro() { return this; }
|
||||
///
|
||||
virtual MathMacro const * asMacro() const { return this; }
|
||||
///
|
||||
void draw(PainterInfo & pi, int x, int y) const;
|
||||
///
|
||||
void drawExpanded(PainterInfo & pi, int x, int y) const;
|
||||
/// draw selection background
|
||||
void drawSelection(PainterInfo & pi, int x, int y) const;
|
||||
/// draw decorations.
|
||||
@ -38,29 +39,34 @@ public:
|
||||
{ drawMarkers2(pi, x, y); }
|
||||
///
|
||||
void metrics(MetricsInfo & mi, Dimension & dim) const;
|
||||
///
|
||||
int kerning() const;
|
||||
/// get cursor position
|
||||
void cursorPos(BufferView const & bv, CursorSlice const & sl,
|
||||
bool boundary, int & x, int & y) const;
|
||||
///
|
||||
void edit(Cursor & cur, bool left);
|
||||
///
|
||||
Inset * editXY(Cursor & cur, int x, int y);
|
||||
|
||||
/// target pos when we enter the inset from the left by pressing "Right"
|
||||
bool idxFirst(Cursor &) const;
|
||||
/// target pos when we enter the inset from the right by pressing "Left"
|
||||
bool idxLast(Cursor &) const;
|
||||
///
|
||||
bool idxUpDown(Cursor & cur, bool up) const;
|
||||
|
||||
///
|
||||
virtual bool notifyCursorLeaves(Cursor &);
|
||||
///
|
||||
docstring name() const;
|
||||
///
|
||||
int kerning() const { return kerning_; }
|
||||
///
|
||||
void setExpansion(MathData const & exp, MathData const & args) const;
|
||||
|
||||
/// Remove cell (starting from 0)
|
||||
void removeArgument(size_t pos);
|
||||
/// Insert empty cell (starting from 0)
|
||||
void insertArgument(size_t pos);
|
||||
|
||||
///
|
||||
void validate(LaTeXFeatures &) const;
|
||||
|
||||
///
|
||||
void MathMacro::write(WriteStream & os) const;
|
||||
///
|
||||
void maple(MapleStream &) const;
|
||||
///
|
||||
@ -72,27 +78,104 @@ public:
|
||||
///
|
||||
void infoize2(odocstream &) const;
|
||||
|
||||
private:
|
||||
virtual Inset * clone() const;
|
||||
///
|
||||
void updateExpansion() const;
|
||||
/// fold the macro in the next metrics call
|
||||
void fold(Cursor & cur);
|
||||
/// unfold the macro in the next metrics call
|
||||
void unfold(Cursor & cur);
|
||||
/// will it be folded or unfolded in the next metric call?
|
||||
bool folded() const;
|
||||
|
||||
enum DisplayMode {
|
||||
DISPLAY_INIT,
|
||||
DISPLAY_UNFOLDED,
|
||||
DISPLAY_NORMAL,
|
||||
};
|
||||
|
||||
///
|
||||
DisplayMode displayMode() const { return displayMode_; }
|
||||
|
||||
///
|
||||
bool extraBraces() const { return displayMode_ == DISPLAY_NORMAL && arity() > 0; }
|
||||
|
||||
|
||||
///
|
||||
docstring name() const;
|
||||
///
|
||||
bool validName() const;
|
||||
///
|
||||
size_t arity() const {
|
||||
if (displayMode_ == DISPLAY_NORMAL )
|
||||
return cells_.size();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
///
|
||||
int optionals() const { return optionals_; }
|
||||
///
|
||||
void setOptionals(int n) {
|
||||
if (n <= int(nargs()))
|
||||
optionals_ = n;
|
||||
}
|
||||
|
||||
protected:
|
||||
friend class MathData;
|
||||
friend class ArgumentProxy;
|
||||
|
||||
/// update the display mode (should only be called after detaching arguments)
|
||||
void setDisplayMode(DisplayMode mode);
|
||||
/// compute the next display mode
|
||||
DisplayMode computeDisplayMode(MetricsInfo const & mi) const;
|
||||
/// update macro definition
|
||||
void updateMacro(MetricsInfo & mi);
|
||||
/// check if macro definition changed, argument changed etc. and adapt
|
||||
void updateRepresentation(MetricsInfo & mi);
|
||||
/// empty macro, put arguments into args, possibly strip arity-attachedArgsNum_ empty ones.
|
||||
/// Includes the optional arguments.
|
||||
void detachArguments(std::vector<MathData> & args, bool strip);
|
||||
/// attach arguments (maybe less than arity at the end of an MathData),
|
||||
/// including the optional ones (even if it can be empty here)
|
||||
void attachArguments(std::vector<MathData> const & args, size_t arity, int optionals);
|
||||
///
|
||||
bool editing() { return editing_; }
|
||||
///
|
||||
MacroData const * macro() { return macro_; }
|
||||
|
||||
private:
|
||||
///
|
||||
virtual Inset * clone() const;
|
||||
/// the index of the cursor slice of the macro, or -1 if it is not edited
|
||||
int cursorIdx(Cursor const & cur) const;
|
||||
///
|
||||
bool editMode(Cursor const & cur) const;
|
||||
|
||||
/// name of macro
|
||||
docstring name_;
|
||||
/// the unexpanded macro defintition
|
||||
mutable MathData tmpl_;
|
||||
/// the macro substituted with our args
|
||||
mutable MathData expanded_;
|
||||
/// current display mode
|
||||
DisplayMode displayMode_;
|
||||
/// display mode before change
|
||||
InsetMathSqrt expanded_;
|
||||
/// number of arguments that were really attached
|
||||
size_t attachedArgsNum_;
|
||||
/// cursor position during last draw
|
||||
int previousCurIdx_;
|
||||
/// optional argument attached? (only in DISPLAY_NORMAL mode)
|
||||
int optionals_;
|
||||
/// fold mode to be set in next metrics call?
|
||||
bool nextFoldMode_;
|
||||
/// if macro_ == true, then here is a copy of the macro
|
||||
/// don't use it for locking
|
||||
MacroData macroBackup_;
|
||||
/// if macroNotFound_ == false, then here is a reference to the macro
|
||||
/// this might invalidate after metrics was called
|
||||
MacroData const * macro_;
|
||||
///
|
||||
mutable MacroData macroBackup_;
|
||||
bool editing_;
|
||||
///
|
||||
mutable bool editing_;
|
||||
///
|
||||
mutable int kerning_;
|
||||
std::string requires_;
|
||||
/// update macro representation
|
||||
bool needsUpdate_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace lyx
|
||||
#endif
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "MathMacroArgument.h"
|
||||
#include "MathStream.h"
|
||||
#include "MathSupport.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
@ -30,6 +31,7 @@ MathMacroArgument::MathMacroArgument(size_t n)
|
||||
lyxerr << "MathMacroArgument::MathMacroArgument: wrong Argument id: "
|
||||
<< n << endl;
|
||||
}
|
||||
|
||||
// The profiler tells us not to use
|
||||
// str_ = '#' + convert<docstring>(n);
|
||||
// so we do the conversion of n to ASCII manually.
|
||||
@ -46,6 +48,18 @@ Inset * MathMacroArgument::clone() const
|
||||
}
|
||||
|
||||
|
||||
void MathMacroArgument::setNumber(std::size_t n)
|
||||
{
|
||||
if (n < 1 || n > 9) {
|
||||
lyxerr << "MathMacroArgument::setNumber: wrong Argument id: "
|
||||
<< n << endl;
|
||||
}
|
||||
|
||||
number_ = n;
|
||||
str_[1] = '0' + n;
|
||||
}
|
||||
|
||||
|
||||
void MathMacroArgument::write(WriteStream & os) const
|
||||
{
|
||||
os << str_;
|
||||
|
@ -31,6 +31,8 @@ public:
|
||||
///
|
||||
std::size_t number() const { return number_; }
|
||||
///
|
||||
void setNumber(std::size_t n);
|
||||
///
|
||||
InsetCode lyxCode() const { return MATHMACROARG_CODE; }
|
||||
|
||||
///
|
||||
|
@ -10,22 +10,43 @@
|
||||
|
||||
#include <config.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 "MathMacroArgument.h"
|
||||
|
||||
#include "Buffer.h"
|
||||
#include "Color.h"
|
||||
#include "Cursor.h"
|
||||
#include "debug.h"
|
||||
#include "DispatchResult.h"
|
||||
#include "FuncRequest.h"
|
||||
#include "FuncStatus.h"
|
||||
#include "gettext.h"
|
||||
#include "Lexer.h"
|
||||
#include "Undo.h"
|
||||
|
||||
#include "frontends/FontMetrics.h"
|
||||
#include "frontends/Painter.h"
|
||||
|
||||
#include "support/convert.h"
|
||||
#include "support/lstrings.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/function.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
|
||||
namespace lyx {
|
||||
|
||||
@ -35,29 +56,74 @@ using std::ostream;
|
||||
using std::endl;
|
||||
|
||||
|
||||
class InsetMathWrapper : public InsetMath {
|
||||
public:
|
||||
///
|
||||
InsetMathWrapper(MathData const * value) : value_(value) {}
|
||||
///
|
||||
void metrics(MetricsInfo & mi, Dimension & dim) const;
|
||||
///
|
||||
void draw(PainterInfo &, int x, int y) const;
|
||||
|
||||
private:
|
||||
///
|
||||
Inset * clone() const;
|
||||
///
|
||||
MathData const * value_;
|
||||
};
|
||||
|
||||
|
||||
Inset * InsetMathWrapper::clone() const
|
||||
{
|
||||
return new InsetMathWrapper(*this);
|
||||
}
|
||||
|
||||
|
||||
void InsetMathWrapper::metrics(MetricsInfo & mi, Dimension & dim) const
|
||||
{
|
||||
value_->metrics(mi, dim);
|
||||
//metricsMarkers2(dim);
|
||||
}
|
||||
|
||||
|
||||
void InsetMathWrapper::draw(PainterInfo & pi, int x, int y) const
|
||||
{
|
||||
value_->draw(pi, x, y);
|
||||
//drawMarkers(pi, x, y);
|
||||
}
|
||||
|
||||
|
||||
MathMacroTemplate::MathMacroTemplate()
|
||||
: InsetMathNest(2), numargs_(0), name_(), type_(from_ascii("newcommand"))
|
||||
: InsetMathNest(3), numargs_(0), optionals_(0), type_(from_ascii("newcommand"))
|
||||
{
|
||||
initMath();
|
||||
}
|
||||
|
||||
|
||||
MathMacroTemplate::MathMacroTemplate(docstring const & name, int numargs,
|
||||
docstring const & type, MathData const & ar1, MathData const & ar2)
|
||||
: InsetMathNest(2), numargs_(numargs), name_(name), type_(type)
|
||||
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)
|
||||
{
|
||||
initMath();
|
||||
|
||||
if (numargs_ > 9)
|
||||
lyxerr << "MathMacroTemplate::MathMacroTemplate: wrong # of arguments: "
|
||||
<< numargs_ << std::endl;
|
||||
cell(0) = ar1;
|
||||
cell(1) = ar2;
|
||||
|
||||
asArray(name, cell(0));
|
||||
optionalValues_.resize(9);
|
||||
for (int i = 0; i < optionals_; ++i)
|
||||
cell(optIdx(i)) = optionalValues_[i];
|
||||
cell(defIdx()) = def;
|
||||
cell(displayIdx()) = display;
|
||||
}
|
||||
|
||||
|
||||
MathMacroTemplate::MathMacroTemplate(docstring const & str)
|
||||
: InsetMathNest(2), numargs_(0), name_()
|
||||
: InsetMathNest(3), numargs_(0)
|
||||
{
|
||||
initMath();
|
||||
|
||||
@ -77,110 +143,499 @@ Inset * MathMacroTemplate::clone() const
|
||||
}
|
||||
|
||||
|
||||
void MathMacroTemplate::edit(Cursor & cur, bool)
|
||||
{
|
||||
//lyxerr << "MathMacroTemplate: edit left/right" << endl;
|
||||
cur.push(*this);
|
||||
}
|
||||
|
||||
|
||||
int MathMacroTemplate::numargs() const
|
||||
{
|
||||
return numargs_;
|
||||
}
|
||||
|
||||
|
||||
void MathMacroTemplate::numargs(int numargs)
|
||||
{
|
||||
numargs_ = numargs;
|
||||
}
|
||||
|
||||
|
||||
docstring MathMacroTemplate::name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
|
||||
docstring MathMacroTemplate::prefix() const
|
||||
{
|
||||
return bformat(_(" Macro: %1$s: "), name_);
|
||||
return asString(cell(0));
|
||||
}
|
||||
|
||||
|
||||
void MathMacroTemplate::metrics(MetricsInfo & mi, Dimension & dim) const
|
||||
{
|
||||
bool lockMacro = MacroTable::globalMacros().has(name_);
|
||||
if (lockMacro)
|
||||
MacroTable::globalMacros().get(name_).lock();
|
||||
FontSetChanger dummy1(mi.base, from_ascii("mathnormal"));
|
||||
StyleChanger dummy2(mi.base, LM_ST_TEXT);
|
||||
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
||||
// create label "{#1}{#2}:="
|
||||
label_.clear();
|
||||
int i = 0;
|
||||
for (; i < optionals_; ++i) {
|
||||
label_.push_back(MathAtom(new InsetMathChar('[')));
|
||||
label_.push_back(MathAtom(new InsetMathWrapper(&cell(1 + i))));
|
||||
label_.push_back(MathAtom(new InsetMathChar(']')));
|
||||
}
|
||||
for (; i < numargs_; ++i) {
|
||||
MathData arg;
|
||||
arg.push_back(MathAtom(new MathMacroArgument(i + 1)));
|
||||
label_.push_back(MathAtom(new InsetMathBrace(arg)));
|
||||
}
|
||||
label_.push_back(MathAtom(new InsetMathChar(':')));
|
||||
label_.push_back(MathAtom(new InsetMathChar('=')));
|
||||
|
||||
// do metrics
|
||||
if (macro)
|
||||
macro->lock();
|
||||
|
||||
Dimension dim0;
|
||||
Dimension labeldim;
|
||||
Dimension defdim;
|
||||
Dimension dspdim;
|
||||
|
||||
cell(0).metrics(mi, dim0);
|
||||
Dimension dim1;
|
||||
cell(1).metrics(mi, dim1);
|
||||
docstring dp = prefix();
|
||||
dim.wid = dim0.width() + dim1.width() + 20
|
||||
+ theFontMetrics(mi.base.font).width(dp);
|
||||
dim.asc = std::max(dim0.ascent(), dim1.ascent()) + 7;
|
||||
dim.des = std::max(dim0.descent(), dim1.descent()) + 7;
|
||||
label_.metrics(mi, labeldim);
|
||||
cell(defIdx()).metrics(mi, defdim);
|
||||
cell(displayIdx()).metrics(mi, dspdim);
|
||||
|
||||
if (lockMacro)
|
||||
MacroTable::globalMacros().get(name_).unlock();
|
||||
if (macro)
|
||||
macro->unlock();
|
||||
|
||||
// Cache the inset dimension.
|
||||
// calculate metrics taking all cells and labels into account
|
||||
dim.wid = 2 + mathed_string_width(mi.base.font, from_ascii("\\")) +
|
||||
dim0.width() +
|
||||
labeldim.width() +
|
||||
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.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());
|
||||
|
||||
// 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;
|
||||
|
||||
setDimCache(mi, dim);
|
||||
}
|
||||
|
||||
|
||||
void MathMacroTemplate::draw(PainterInfo & p, int x, int y) const
|
||||
void MathMacroTemplate::draw(PainterInfo & pi, int x, int y) const
|
||||
{
|
||||
bool lockMacro = MacroTable::globalMacros().has(name_);
|
||||
if (lockMacro)
|
||||
MacroTable::globalMacros().get(name_).lock();
|
||||
FontSetChanger dummy1(pi.base, from_ascii("mathnormal"));
|
||||
StyleChanger dummy2(pi.base, LM_ST_TEXT);
|
||||
|
||||
setPosCache(p, x, y);
|
||||
setPosCache(pi, x, y);
|
||||
Dimension const dim = dimension(*pi.base.bv);
|
||||
|
||||
Dimension const dim = dimension(*p.base.bv);
|
||||
|
||||
// label
|
||||
FontInfo font = p.base.font;
|
||||
font.setColor(Color_math);
|
||||
|
||||
PainterInfo pi(p.base.bv, p.pain);
|
||||
pi.base.style = LM_ST_TEXT;
|
||||
pi.base.font = font;
|
||||
// create fonts
|
||||
bool valid = validMacro();
|
||||
FontInfo font = pi.base.font;
|
||||
if (valid)
|
||||
font.setColor(Color_latex);
|
||||
else
|
||||
font.setColor(Color_error);
|
||||
|
||||
// draw outer frame
|
||||
int const a = y - dim.asc + 1;
|
||||
int const w = dim.wid - 2;
|
||||
int const h = dim.height() - 2;
|
||||
pi.pain.rectangle(x, a, w, h, Color_mathframe);
|
||||
x += 4;
|
||||
|
||||
// Color_mathbg used to be "AntiqueWhite" but is "linen" now, too
|
||||
// the next line would overwrite the selection!
|
||||
//pi.pain.fillRectangle(x, a, w, h, Color_mathmacrobg);
|
||||
pi.pain.rectangle(x, a, w, h, Color_mathframe);
|
||||
// draw backslash
|
||||
pi.pain.text(x, y, from_ascii("\\"), font);
|
||||
x += mathed_string_width(font, from_ascii("\\"));
|
||||
|
||||
// FIXME:
|
||||
#if 0
|
||||
Cursor & cur = p.base.bv->cursor();
|
||||
if (cur.isInside(this))
|
||||
cur.drawSelection(pi);
|
||||
#endif
|
||||
docstring dp = prefix();
|
||||
pi.pain.text(x + 2, y, dp, font);
|
||||
// FIXME: Painter text should retain the drawn text width
|
||||
x += theFontMetrics(font).width(dp) + 6;
|
||||
// draw name
|
||||
PainterInfo namepi = pi;
|
||||
namepi.base.font = font;
|
||||
cell(0).draw(namepi, x, y);
|
||||
x += cell(0).dimension(*pi.base.bv).width();
|
||||
|
||||
int const w0 = cell(0).dimension(*pi.base.bv).width();
|
||||
int const w1 = cell(1).dimension(*pi.base.bv).width();
|
||||
cell(0).draw(pi, x + 2, y + 1);
|
||||
pi.pain.rectangle(x, y - dim.ascent() + 3,
|
||||
w0 + 4, dim.height() - 6, Color_mathline);
|
||||
cell(1).draw(pi, x + 8 + w0, y + 1);
|
||||
pi.pain.rectangle(x + w0 + 6, y - dim.ascent() + 3,
|
||||
w1 + 4, dim.height() - 6, Color_mathline);
|
||||
// draw label
|
||||
label_.draw(pi, x, y);
|
||||
x += label_.dimension(*pi.base.bv).width();
|
||||
|
||||
// draw definition
|
||||
cell(defIdx()).draw(pi, x + 2, y);
|
||||
int const w1 = cell(defIdx()).dimension(*pi.base.bv).width();
|
||||
pi.pain.rectangle(x, y - dim.ascent() + 3, w1 + 4, dim.height() - 6, Color_mathline);
|
||||
x += w1 + 8;
|
||||
|
||||
if (lockMacro)
|
||||
MacroTable::globalMacros().get(name_).unlock();
|
||||
// draw display
|
||||
cell(displayIdx()).draw(pi, x + 2, y);
|
||||
int const w2 = cell(displayIdx()).dimension(*pi.base.bv).width();
|
||||
pi.pain.rectangle(x, y - dim.ascent() + 3, w2 + 4, dim.height() - 6, Color_mathline);
|
||||
}
|
||||
|
||||
|
||||
void MathMacroTemplate::removeArguments(Cursor & cur, int from, int to) {
|
||||
for (DocIterator it = doc_iterator_begin(*this); it; it.forwardChar()) {
|
||||
if (!it.nextInset())
|
||||
continue;
|
||||
if (it.nextInset()->lyxCode() != MATHMACROARG_CODE)
|
||||
continue;
|
||||
MathMacroArgument * arg = static_cast<MathMacroArgument*>(it.nextInset());
|
||||
int n = arg->number() - 1;
|
||||
if (from <= n && n <= to) {
|
||||
int cellSlice = cur.find(it.cell());
|
||||
if (cellSlice != -1 && cur[cellSlice].pos() > it.pos())
|
||||
--cur[cellSlice].pos();
|
||||
|
||||
it.cell().erase(it.pos());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MathMacroTemplate::shiftArguments(size_t from, int by) {
|
||||
for (DocIterator it = doc_iterator_begin(*this); it; it.forwardChar()) {
|
||||
if (!it.nextInset())
|
||||
continue;
|
||||
if (it.nextInset()->lyxCode() != MATHMACROARG_CODE)
|
||||
continue;
|
||||
MathMacroArgument * arg = static_cast<MathMacroArgument*>(it.nextInset());
|
||||
if (arg->number() >= from + 1)
|
||||
arg->setNumber(arg->number() + by);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// FIXME: factorize those functions here with a functional style, maybe using Boost's function
|
||||
// objects?
|
||||
|
||||
void fixMacroInstancesAddRemove(Cursor const & from, docstring const & name, int n, bool insert) {
|
||||
Cursor dit = from;
|
||||
|
||||
for (; dit; dit.forwardPos()) {
|
||||
// only until a macro is redefined
|
||||
if (dit.inset().lyxCode() == MATHMACRO_CODE) {
|
||||
MathMacroTemplate const & macroTemplate
|
||||
= static_cast<MathMacroTemplate const &>(dit.inset());
|
||||
if (macroTemplate.name() == name)
|
||||
break;
|
||||
}
|
||||
|
||||
// in front of macro instance?
|
||||
Inset * inset = dit.nextInset();
|
||||
if (inset) {
|
||||
InsetMath * insetMath = inset->asInsetMath();
|
||||
if (insetMath) {
|
||||
MathMacro * macro = insetMath->asMacro();
|
||||
if (macro && macro->name() == name && macro->folded()) {
|
||||
// found macro instance
|
||||
if (insert)
|
||||
macro->insertArgument(n);
|
||||
else
|
||||
macro->removeArgument(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fixMacroInstancesOptional(Cursor const & from, docstring const & name, int optionals) {
|
||||
Cursor dit = from;
|
||||
|
||||
for (; dit; dit.forwardPos()) {
|
||||
// only until a macro is redefined
|
||||
if (dit.inset().lyxCode() == MATHMACRO_CODE) {
|
||||
MathMacroTemplate const & macroTemplate
|
||||
= static_cast<MathMacroTemplate const &>(dit.inset());
|
||||
if (macroTemplate.name() == name)
|
||||
break;
|
||||
}
|
||||
|
||||
// in front of macro instance?
|
||||
Inset * inset = dit.nextInset();
|
||||
if (inset) {
|
||||
InsetMath * insetMath = inset->asInsetMath();
|
||||
if (insetMath) {
|
||||
MathMacro * macro = insetMath->asMacro();
|
||||
if (macro && macro->name() == name && macro->folded()) {
|
||||
// found macro instance
|
||||
macro->setOptionals(optionals);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class F>
|
||||
void fixMacroInstancesFunctional(Cursor const & from,
|
||||
docstring const & name, F & fix) {
|
||||
Cursor dit = from;
|
||||
|
||||
for (; dit; dit.forwardPos()) {
|
||||
// only until a macro is redefined
|
||||
if (dit.inset().lyxCode() == MATHMACRO_CODE) {
|
||||
MathMacroTemplate const & macroTemplate
|
||||
= static_cast<MathMacroTemplate const &>(dit.inset());
|
||||
if (macroTemplate.name() == name)
|
||||
break;
|
||||
}
|
||||
|
||||
// in front of macro instance?
|
||||
Inset * inset = dit.nextInset();
|
||||
if (inset) {
|
||||
InsetMath * insetMath = inset->asInsetMath();
|
||||
if (insetMath) {
|
||||
MathMacro * macro = insetMath->asMacro();
|
||||
if (macro && macro->name() == name && macro->folded())
|
||||
F(macro);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MathMacroTemplate::insertParameter(Cursor & cur, int pos, bool greedy)
|
||||
{
|
||||
if (pos <= numargs_ && pos >= optionals_ && numargs_ < 9) {
|
||||
++numargs_;
|
||||
shiftArguments(pos, 1);
|
||||
|
||||
// append example #n
|
||||
cell(defIdx()).push_back(MathAtom(new MathMacroArgument(pos + 1)));
|
||||
if (!cell(displayIdx()).empty())
|
||||
cell(displayIdx()).push_back(MathAtom(new MathMacroArgument(pos + 1)));
|
||||
|
||||
if (!greedy) {
|
||||
Cursor dit = cur;
|
||||
dit.leaveInset(*this);
|
||||
// TODO: this was dit.forwardPosNoDescend before. Check that this is the same
|
||||
dit.top().forwardPos();
|
||||
|
||||
// fix macro instances
|
||||
fixMacroInstancesAddRemove(dit, name(), pos, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MathMacroTemplate::removeParameter(Cursor & cur, int pos, bool greedy)
|
||||
{
|
||||
if (pos < numargs_ && pos >= 0) {
|
||||
--numargs_;
|
||||
removeArguments(cur, pos, pos);
|
||||
shiftArguments(pos + 1, -1);
|
||||
|
||||
// removed optional parameter?
|
||||
if (pos < optionals_) {
|
||||
--optionals_;
|
||||
optionalValues_[pos] = cell(optIdx(pos));
|
||||
cells_.erase(cells_.begin() + optIdx(pos));
|
||||
|
||||
// fix cursor
|
||||
int macroSlice = cur.find(this);
|
||||
if (macroSlice != -1) {
|
||||
if (cur[macroSlice].idx() == optIdx(pos)) {
|
||||
cur.cutOff(macroSlice);
|
||||
cur[macroSlice].idx() = 1;
|
||||
cur[macroSlice].pos() = 0;
|
||||
} else if (cur[macroSlice].idx() > optIdx(pos))
|
||||
--cur[macroSlice].idx();
|
||||
}
|
||||
}
|
||||
|
||||
if (!greedy) {
|
||||
// fix macro instances
|
||||
//boost::function<void(MathMacro *)> fix = _1->insertArgument(n);
|
||||
//fixMacroInstancesFunctional(dit, name(), fix);
|
||||
Cursor dit = cur;
|
||||
dit.leaveInset(*this);
|
||||
// TODO: this was dit.forwardPosNoDescend before. Check that this is the same
|
||||
dit.top().forwardPos();
|
||||
fixMacroInstancesAddRemove(dit, name(), pos, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MathMacroTemplate::makeOptional(Cursor & cur) {
|
||||
if (numargs_ > 0 && optionals_ < numargs_) {
|
||||
++optionals_;
|
||||
cells_.insert(cells_.begin() + optIdx(optionals_ - 1), optionalValues_[optionals_ - 1]);
|
||||
// fix cursor
|
||||
int macroSlice = cur.find(this);
|
||||
if (macroSlice != -1 && cur[macroSlice].idx() >= optIdx(optionals_ - 1))
|
||||
++cur[macroSlice].idx();
|
||||
|
||||
// fix macro instances
|
||||
Cursor dit = cur;
|
||||
dit.leaveInset(*this);
|
||||
// TODO: this was dit.forwardPosNoDescend before. Check that this is the same
|
||||
dit.top().forwardPos();
|
||||
fixMacroInstancesOptional(dit, name(), optionals_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MathMacroTemplate::makeNonOptional(Cursor & cur) {
|
||||
if (numargs_ > 0 && optionals_ > 0) {
|
||||
--optionals_;
|
||||
optionalValues_[optionals_ - 1] = cell(optIdx(optionals_));
|
||||
cells_.erase(cells_.begin() + optIdx(optionals_));
|
||||
|
||||
// fix cursor
|
||||
int macroSlice = cur.find(this);
|
||||
if (macroSlice != -1) {
|
||||
if (cur[macroSlice].idx() > optIdx(optionals_))
|
||||
--cur[macroSlice].idx();
|
||||
else if (cur[macroSlice].idx() == optIdx(optionals_)) {
|
||||
cur.cutOff(macroSlice);
|
||||
cur[macroSlice].idx() = optIdx(optionals_);
|
||||
cur[macroSlice].pos() = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// fix macro instances
|
||||
Cursor dit = cur;
|
||||
dit.leaveInset(*this);
|
||||
// TODO: this was dit.forwardPosNoDescend before. Check that this is the same
|
||||
dit.top().forwardPos();
|
||||
fixMacroInstancesOptional(dit, name(), optionals_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MathMacroTemplate::doDispatch(Cursor & cur, FuncRequest & cmd)
|
||||
{
|
||||
std::string const arg = to_utf8(cmd.argument());
|
||||
switch (cmd.action) {
|
||||
|
||||
case LFUN_MATH_MACRO_ADD_PARAM:
|
||||
if (numargs_ < 9) {
|
||||
cur.recordUndoFullDocument();
|
||||
size_t pos = numargs_;
|
||||
if (arg.size() != 0)
|
||||
pos = (size_t)convert<int>(arg) - 1; // it is checked for >=0 in getStatus
|
||||
insertParameter(cur, pos);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case LFUN_MATH_MACRO_REMOVE_PARAM:
|
||||
if (numargs_ > 0) {
|
||||
cur.recordUndoFullDocument();
|
||||
size_t pos = numargs_ - 1;
|
||||
if (arg.size() != 0)
|
||||
pos = (size_t)convert<int>(arg) - 1; // it is checked for >=0 in getStatus
|
||||
removeParameter(cur, pos);
|
||||
}
|
||||
break;
|
||||
|
||||
case LFUN_MATH_MACRO_APPEND_GREEDY_PARAM:
|
||||
if (numargs_ < 9) {
|
||||
cur.recordUndoFullDocument();
|
||||
insertParameter(cur, numargs_, true);
|
||||
}
|
||||
break;
|
||||
|
||||
case LFUN_MATH_MACRO_REMOVE_GREEDY_PARAM:
|
||||
if (numargs_ > 0) {
|
||||
cur.recordUndoFullDocument();
|
||||
removeParameter(cur, numargs_ - 1, true);
|
||||
}
|
||||
break;
|
||||
|
||||
case LFUN_MATH_MACRO_MAKE_OPTIONAL:
|
||||
cur.recordUndoFullDocument();
|
||||
makeOptional(cur);
|
||||
break;
|
||||
|
||||
case LFUN_MATH_MACRO_MAKE_NONOPTIONAL:
|
||||
cur.recordUndoFullDocument();
|
||||
makeNonOptional(cur);
|
||||
break;
|
||||
|
||||
case LFUN_MATH_MACRO_ADD_OPTIONAL_PARAM:
|
||||
if (numargs_ < 9) {
|
||||
cur.recordUndoFullDocument();
|
||||
insertParameter(cur, optionals_);
|
||||
makeOptional(cur);
|
||||
}
|
||||
break;
|
||||
|
||||
case LFUN_MATH_MACRO_REMOVE_OPTIONAL_PARAM:
|
||||
if (optionals_ > 0)
|
||||
removeParameter(cur, optionals_ - 1);
|
||||
break;
|
||||
|
||||
case LFUN_MATH_MACRO_ADD_GREEDY_OPTIONAL_PARAM:
|
||||
if (numargs_ == optionals_) {
|
||||
cur.recordUndoFullDocument();
|
||||
insertParameter(cur, 0, true);
|
||||
makeOptional(cur);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
InsetMathNest::doDispatch(cur, cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool MathMacroTemplate::getStatus(Cursor & cur, FuncRequest const & cmd,
|
||||
FuncStatus & flag) const
|
||||
{
|
||||
bool ret = true;
|
||||
std::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);
|
||||
flag.enabled(on);
|
||||
break;
|
||||
}
|
||||
|
||||
case LFUN_MATH_MACRO_APPEND_GREEDY_PARAM:
|
||||
flag.enabled(numargs_ < 9);
|
||||
break;
|
||||
|
||||
case LFUN_MATH_MACRO_REMOVE_PARAM: {
|
||||
int num = numargs_;
|
||||
if (arg.size() != 0)
|
||||
num = convert<int>(arg);
|
||||
flag.enabled(num >= 1 && num <= numargs_);
|
||||
break;
|
||||
}
|
||||
|
||||
case LFUN_MATH_MACRO_MAKE_OPTIONAL:
|
||||
flag.enabled(numargs_ > 0 && optionals_ < numargs_ && type_ != from_ascii("def"));
|
||||
break;
|
||||
|
||||
case LFUN_MATH_MACRO_MAKE_NONOPTIONAL:
|
||||
flag.enabled(optionals_ > 0 && type_ != from_ascii("def"));
|
||||
break;
|
||||
|
||||
case LFUN_MATH_MACRO_ADD_OPTIONAL_PARAM:
|
||||
flag.enabled(numargs_ < 9);
|
||||
break;
|
||||
|
||||
case LFUN_MATH_MACRO_REMOVE_OPTIONAL_PARAM:
|
||||
flag.enabled(optionals_ > 0);
|
||||
break;
|
||||
|
||||
case LFUN_MATH_MACRO_ADD_GREEDY_OPTIONAL_PARAM:
|
||||
flag.enabled(numargs_ == 0 && type_ != from_ascii("def"));
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@ -210,25 +665,46 @@ void MathMacroTemplate::write(Buffer const &, std::ostream & os) const
|
||||
void MathMacroTemplate::write(WriteStream & os) const
|
||||
{
|
||||
if (type_ == "def") {
|
||||
os << "\\def\\" << name_.c_str();
|
||||
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() << '}';
|
||||
os << "\\" << type_.c_str() << "{\\" << name().c_str() << '}';
|
||||
if (numargs_ > 0)
|
||||
os << '[' << numargs_ << ']';
|
||||
|
||||
// optional values
|
||||
if (os.latex()) {
|
||||
// in latex only one optional possible, simulate the others
|
||||
if (optionals_ >= 1) {
|
||||
docstring optValue = asString(cell(optIdx(0)));
|
||||
if (optValue.find(']') != docstring::npos)
|
||||
os << "[{" << cell(optIdx(0)) << "}]";
|
||||
else
|
||||
os << "[" << cell(optIdx(0)) << "]";
|
||||
}
|
||||
} else {
|
||||
// in lyx we handle all optionals as real optionals
|
||||
for (int i = 0; i < optionals_; ++i) {
|
||||
docstring optValue = asString(cell(optIdx(i)));
|
||||
if (optValue.find(']') != docstring::npos)
|
||||
os << "[{" << cell(optIdx(i)) << "}]";
|
||||
else
|
||||
os << "[" << cell(optIdx(i)) << "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
os << '{' << cell(0) << "}";
|
||||
os << "{" << cell(defIdx()) << "}";
|
||||
|
||||
if (os.latex()) {
|
||||
// writing .tex. done.
|
||||
os << "\n";
|
||||
} else {
|
||||
// writing .lyx, write special .tex export only if necessary
|
||||
if (!cell(1).empty())
|
||||
os << "\n{" << cell(1) << '}';
|
||||
if (!cell(displayIdx()).empty())
|
||||
os << "\n{" << cell(displayIdx()) << '}';
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,9 +719,44 @@ int MathMacroTemplate::plaintext(Buffer const & buf, odocstream & os,
|
||||
}
|
||||
|
||||
|
||||
bool MathMacroTemplate::validName() const
|
||||
{
|
||||
docstring n = name();
|
||||
|
||||
// empty name?
|
||||
if (n.size() == 0)
|
||||
return false;
|
||||
|
||||
// converting back and force doesn't swallow anything?
|
||||
/*MathData ma;
|
||||
asArray(n, ma);
|
||||
if (asString(ma) != n)
|
||||
return false;*/
|
||||
|
||||
// valid characters?
|
||||
for (size_t i = 0; i < n.size(); ++i) {
|
||||
if (!(n[i] >= 'a' && n[i] <= 'z') &&
|
||||
!(n[i] >= 'A' && n[i] <= 'Z'))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool MathMacroTemplate::validMacro() const
|
||||
{
|
||||
return validName();
|
||||
}
|
||||
|
||||
|
||||
MacroData MathMacroTemplate::asMacroData() const
|
||||
{
|
||||
return MacroData(asString(cell(0)), numargs(), asString(cell(1)), std::string());
|
||||
std::vector<docstring> defaults(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());
|
||||
}
|
||||
|
||||
|
||||
|
@ -13,9 +13,9 @@
|
||||
#ifndef MATH_MACROTEMPLATE_H
|
||||
#define MATH_MACROTEMPLATE_H
|
||||
|
||||
#include "MathData.h"
|
||||
#include "MacroTable.h"
|
||||
#include "InsetMathNest.h"
|
||||
#include "MacroTable.h"
|
||||
#include "MathData.h"
|
||||
|
||||
#include "support/types.h"
|
||||
|
||||
@ -28,15 +28,14 @@ public:
|
||||
///
|
||||
MathMacroTemplate();
|
||||
///
|
||||
MathMacroTemplate(docstring const & name, int nargs,
|
||||
docstring const & type,
|
||||
MathData const & = MathData(),
|
||||
MathData const & = MathData());
|
||||
MathMacroTemplate(docstring const & name, int nargs, int optional,
|
||||
docstring const & type,
|
||||
std::vector<MathData> const & optionalValues = std::vector<MathData>(),
|
||||
MathData const & def = MathData(),
|
||||
MathData const & display = MathData());
|
||||
///
|
||||
explicit MathMacroTemplate(const docstring & str);
|
||||
///
|
||||
void edit(Cursor & cur, bool left);
|
||||
///
|
||||
EDITABLE editable() const { return HIGHLY_EDITABLE; }
|
||||
///
|
||||
void read(Buffer const &, Lexer & lex);
|
||||
@ -46,14 +45,14 @@ public:
|
||||
void write(WriteStream & os) const;
|
||||
///
|
||||
int plaintext(Buffer const &, odocstream &,
|
||||
OutputParams const &) const;
|
||||
OutputParams const &) const;
|
||||
|
||||
/// Number of arguments
|
||||
int numargs() const;
|
||||
///
|
||||
void numargs(int);
|
||||
///
|
||||
docstring name() const;
|
||||
/// check name and possible other formal properties
|
||||
bool validMacro() const;
|
||||
///
|
||||
bool validName() const;
|
||||
///
|
||||
MacroData asMacroData() const;
|
||||
///
|
||||
@ -67,17 +66,44 @@ public:
|
||||
///
|
||||
InsetCode lyxCode() const { return MATHMACRO_CODE; }
|
||||
|
||||
protected:
|
||||
///
|
||||
virtual void doDispatch(Cursor & cur, FuncRequest & cmd);
|
||||
/// do we want to handle this event?
|
||||
bool getStatus(Cursor & cur, FuncRequest const & cmd,
|
||||
FuncStatus & status) const;
|
||||
|
||||
private:
|
||||
virtual Inset * clone() const;
|
||||
/// prefix in inset
|
||||
docstring prefix() const;
|
||||
|
||||
/// remove #n with from<=n<=to
|
||||
void removeArguments(Cursor & cur, int from, int to);
|
||||
/// shift every #n with from<=n, i.e. #n -> #(n-by)
|
||||
void shiftArguments(size_t from, int by);
|
||||
///
|
||||
int numargs_;
|
||||
void insertParameter(Cursor & cur, int pos, bool greedy = false);
|
||||
///
|
||||
docstring name_;
|
||||
void removeParameter(Cursor & cur, int pos, bool greedy = false );
|
||||
///
|
||||
void makeOptional(Cursor & cur);
|
||||
///
|
||||
void makeNonOptional(Cursor & cur);
|
||||
///
|
||||
pos_type defIdx() const { return optionals_ + 1; }
|
||||
/// index of default value cell of optional parameter (#1 -> n=0)
|
||||
pos_type optIdx(int n) const { return n + 1; }
|
||||
///
|
||||
pos_type displayIdx() const { return optionals_ + 2; }
|
||||
/// The label with some holes to edit
|
||||
mutable MathData label_;
|
||||
///
|
||||
mutable int numargs_;
|
||||
///
|
||||
int optionals_;
|
||||
/// keeps the old optional default value when an optional argument is disabled
|
||||
std::vector<MathData> optionalValues_;
|
||||
/// newcommand or renewcommand or def
|
||||
docstring type_;
|
||||
mutable docstring type_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -905,6 +905,8 @@ void Parser::parse1(InsetMathGrid & grid, unsigned flags,
|
||||
docstring const type = t.cs();
|
||||
docstring name;
|
||||
int nargs = 0;
|
||||
int optionals = 0;
|
||||
std::vector<MathData> optionalValues;
|
||||
if (t.cs() == "def") {
|
||||
// get name
|
||||
name = getToken().cs();
|
||||
@ -919,7 +921,6 @@ void Parser::parse1(InsetMathGrid & grid, unsigned flags,
|
||||
//lyxerr << "read \\def parameter list '" << pars << "'" << endl;
|
||||
|
||||
} else { // t.cs() == "newcommand" || t.cs() == "renewcommand"
|
||||
|
||||
if (getToken().cat() != catBegin) {
|
||||
error("'{' in \\newcommand expected (1) ");
|
||||
return;
|
||||
@ -936,27 +937,27 @@ void Parser::parse1(InsetMathGrid & grid, unsigned flags,
|
||||
if (!arg.empty())
|
||||
nargs = convert<int>(arg);
|
||||
|
||||
// optional argument given?
|
||||
skipSpaces();
|
||||
while (nextToken().character() == '[') {
|
||||
getToken();
|
||||
optionalValues.push_back(MathData());
|
||||
parse(optionalValues[optionals], FLAG_BRACK_LAST, mode);
|
||||
++optionals;
|
||||
}
|
||||
}
|
||||
|
||||
MathData ar1;
|
||||
parse(ar1, FLAG_ITEM, InsetMath::UNDECIDED_MODE);
|
||||
|
||||
// we cannot handle recursive stuff at all
|
||||
//MathData test;
|
||||
//test.push_back(createInsetMath(name));
|
||||
//if (ar1.contains(test)) {
|
||||
// error("we cannot handle recursive macros at all.");
|
||||
// return;
|
||||
//}
|
||||
MathData def;
|
||||
parse(def, FLAG_ITEM, InsetMath::UNDECIDED_MODE);
|
||||
|
||||
// is a version for display attached?
|
||||
skipSpaces();
|
||||
MathData ar2;
|
||||
MathData display;
|
||||
if (nextToken().cat() == catBegin)
|
||||
parse(ar2, FLAG_ITEM, InsetMath::MATH_MODE);
|
||||
parse(display, FLAG_ITEM, InsetMath::MATH_MODE);
|
||||
|
||||
cell->push_back(MathAtom(new MathMacroTemplate(name, nargs, type,
|
||||
ar1, ar2)));
|
||||
cell->push_back(MathAtom(new MathMacroTemplate(name, nargs, optionals, type,
|
||||
optionalValues, def, display)));
|
||||
}
|
||||
|
||||
else if (t.cs() == "(") {
|
||||
|
Loading…
Reference in New Issue
Block a user