git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@21328 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Stefan Schimanski 2007-11-01 11:13:07 +00:00
parent 2ad61c0366
commit 1231489798
22 changed files with 1895 additions and 447 deletions

View File

@ -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;
}
}
}

View File

@ -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.

View File

@ -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().

View File

@ -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)

View File

@ -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;
};

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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_;
};

View File

@ -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));
}

View File

@ -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);
}

View File

@ -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

View File

@ -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_;

View File

@ -31,6 +31,8 @@ public:
///
std::size_t number() const { return number_; }
///
void setNumber(std::size_t n);
///
InsetCode lyxCode() const { return MATHMACROARG_CODE; }
///

View File

@ -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());
}

View File

@ -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_;
};

View File

@ -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() == "(") {