#include "InsetMathUnknown.h"
#include "MathAutoCorrect.h"
#include "MathCompletionList.h"
-#include "MathData.h"
#include "MathFactory.h"
#include "InsetMathMacro.h"
#include "InsetMathMacroArgument.h"
#include "LyX.h"
#include "LyXRC.h"
#include "MetricsInfo.h"
-#include "OutputParams.h"
#include "TexRow.h"
#include "Text.h"
InsetMathNest::InsetMathNest(Buffer * buf, idx_type nargs)
: InsetMath(buf), cells_(nargs), lock_(false)
{
- setBuffer(*buf);
+ // FIXME This should not really be necessary, but when we are
+ // initializing the table of global macros, we create macros
+ // with no associated Buffer.
+ if (buf)
+ setBuffer(*buf);
}
void InsetMathNest::setBuffer(Buffer & buffer)
{
InsetMath::setBuffer(buffer);
- for (idx_type i = 0, n = nargs(); i != n; ++i) {
- MathData & data = cell(i);
- for (size_t j = 0; j != data.size(); ++j)
- data[j].nucleus()->setBuffer(buffer);
- }
+ for (MathData & data : cells_)
+ data.setBuffer(buffer);
}
-InsetMath::idx_type InsetMathNest::nargs() const
+idx_type InsetMathNest::nargs() const
{
return cells_.size();
}
}
-void InsetMathNest::updateBuffer(ParIterator const & it, UpdateType utype)
+void InsetMathNest::updateBuffer(ParIterator const & it, UpdateType utype, bool const deleted)
{
for (idx_type i = 0, n = nargs(); i != n; ++i)
- cell(i).updateBuffer(it, utype);
+ cell(i).updateBuffer(it, utype, deleted);
}
if (cur.idx() == 0)
return false;
--cur.idx();
- cur.pos() = cur.lastpos();
+ cur.pos() = lyxrc.mac_like_cursor_movement ? cur.lastpos() : 0;
return true;
}
LASSERT(&cur.inset() == this, return false);
if (nargs() == 0)
return false;
- cur.idx() = 0;
+ cur.idx() = firstIdx();
cur.pos() = 0;
return true;
}
LASSERT(&cur.inset() == this, return false);
if (nargs() == 0)
return false;
- cur.idx() = cur.lastidx();
+ cur.idx() = lastIdx();
cur.pos() = cur.lastpos();
return true;
}
{
odocstringstream oss;
otexrowstream ots(oss);
- WriteStream os(ots);
+ TeXMathStream os(ots);
os << "---------------------------------------------\n";
write(os);
os << "\n";
}
-void InsetMathNest::write(WriteStream & os) const
+void InsetMathNest::write(TeXMathStream & os) const
{
MathEnsurer ensurer(os, currentMode() == MATH_MODE);
ModeSpecifier specifier(os, currentMode(), lockedMode());
docstring const latex_name = name();
os << '\\' << latex_name;
+ os.inMathClass(asClassInset());
for (size_t i = 0; i < nargs(); ++i) {
Changer dummy = os.changeRowEntry(TexRow::mathEntry(id(),i));
os << '{' << cell(i) << '}';
os << "\\lyxlock";
os.pendingSpace(true);
}
+ os.inMathClass(false);
}
void InsetMathNest::latex(otexstream & os, OutputParams const & runparams) const
{
- WriteStream wi(os, runparams.moving_arg, true,
- runparams.dryrun ? WriteStream::wsDryrun : WriteStream::wsDefault,
- runparams.encoding);
- wi.strikeoutMath(runparams.inDeletedInset
- && (!LaTeXFeatures::isAvailable("dvipost")
- || (runparams.flavor != OutputParams::LATEX
- && runparams.flavor != OutputParams::DVILUATEX)));
+ TeXMathStream wi(os, runparams.moving_arg, true,
+ runparams.dryrun ? TeXMathStream::wsDryrun : TeXMathStream::wsDefault,
+ runparams.encoding);
+ wi.strikeoutMath(runparams.inDeletedInset);
if (runparams.inulemcmd) {
- wi.ulemCmd(WriteStream::UNDERLINE);
+ wi.ulemCmd(TeXMathStream::UNDERLINE);
if (runparams.local_font) {
FontInfo f = runparams.local_font->fontInfo();
if (f.strikeout() == FONT_ON)
- wi.ulemCmd(WriteStream::STRIKEOUT);
+ wi.ulemCmd(TeXMathStream::STRIKEOUT);
}
}
wi.canBreakLine(os.canBreakLine());
Changer dummy = wi.changeRowEntry(TexRow::textEntry(runparams.lastid,
runparams.lastpos));
write(wi);
- // Reset parbreak status after a math inset.
+ // Reset parbreak and command termination status after a math inset.
os.lastChar(0);
os.canBreakLine(wi.canBreakLine());
+ os.terminateCommand(false);
}
void InsetMathNest::handleNest(Cursor & cur, MathAtom const & nest,
docstring const & arg)
{
- CursorSlice i1 = cur.selBegin();
- CursorSlice i2 = cur.selEnd();
+ DocIterator const i1 = cur.selectionBegin();
+ DocIterator const i2 = cur.selectionEnd();
if (!i1.inset().asInsetMath())
return;
if (i1.idx() == i2.idx()) {
}
// multiple selected cells in a simple non-grid inset
- if (i1.asInsetMath()->nrows() == 0 || i1.asInsetMath()->ncols() == 0) {
+ if (i1.inset().nrows() == 0 || i1.inset().ncols() == 0) {
for (idx_type i = i1.idx(); i <= i2.idx(); ++i) {
+ cur.setCursor(i1);
// select cell
cur.idx() = i;
cur.pos() = 0;
cur.pos() = cur.lastpos();
cur.setSelection();
- // change font of cell
+ // do the real job
cur.handleNest(nest);
cur.insert(arg);
-
- // cur is in the font inset now. If the loop continues,
- // we need to get outside again for the next cell
- if (i + 1 <= i2.idx())
- cur.pop_back();
}
return;
}
// the complicated case with multiple selected cells in a grid
row_type r1, r2;
col_type c1, c2;
- cap::region(i1, i2, r1, r2, c1, c2);
+ cap::region(i1.top(), i2.top(), r1, r2, c1, c2);
for (row_type row = r1; row <= r2; ++row) {
for (col_type col = c1; col <= c2; ++col) {
+ cur.setCursor(i1);
// select cell
- cur.idx() = i1.asInsetMath()->index(row, col);
+ cur.idx() = i1.inset().index(row, col);
cur.pos() = 0;
cur.resetAnchor();
cur.pos() = cur.lastpos();
cur.setSelection();
- //
+ // do the real job
cur.handleNest(nest);
cur.insert(arg);
-
- // cur is in the font inset now. If the loop continues,
- // we need to get outside again for the next cell
- if (col + 1 <= c2 || row + 1 <= r2)
- cur.pop_back();
}
}
}
// FIXME: support other font changes here as well?
}
-
void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
{
//LYXERR0("InsetMathNest: request: " << cmd);
size_t n = 0;
idocstringstream is(cmd.argument());
is >> n;
- topaste = cap::selection(n, buffer().params().documentClassPtr());
+ topaste = cap::selection(n, make_pair(buffer().params().documentClassPtr(),
+ buffer().params().authors()));
}
cur.niceInsert(topaste, parseflg, false);
cur.clearSelection(); // bug 393
case LFUN_CUT:
cur.recordUndo();
- cutSelection(cur, true, true);
+ cutSelection(cur, true);
cur.message(_("Cut"));
// Prevent stale position >= size crash
// Probably not necessary anymore, see eraseSelection (gb 2005-10-09)
cur.forceBufferUpdate();
break;
- case LFUN_COPY:
- copySelection(cur);
- cur.message(_("Copy"));
- break;
-
case LFUN_MOUSE_PRESS:
lfunMousePress(cur, cmd);
break;
|| act == LFUN_PARAGRAPH_UP_SELECT;
cur.selHandle(select);
- // handle autocorrect:
- if (lyxrc.autocorrection_math && cur.autocorrect()) {
- cur.autocorrect() = false;
- cur.message(_("Autocorrect Off ('!' to enter)"));
- }
-
// go up/down
bool up = act == LFUN_UP || act == LFUN_UP_SELECT
|| act == LFUN_PARAGRAPH_UP || act == LFUN_PARAGRAPH_UP_SELECT;
case LFUN_CELL_FORWARD:
cur.screenUpdateFlags(Update::Decoration | Update::FitCursor);
- cur.inset().idxNext(cur);
+ cur.selHandle(false);
+ cur.clearTargetX();
+ cur.macroModeClose();
+ if (!cur.inset().idxNext(cur)) {
+ cur.idx() = firstIdx();
+ cur.pos() = 0;
+ }
break;
case LFUN_CELL_BACKWARD:
cur.screenUpdateFlags(Update::Decoration | Update::FitCursor);
- cur.inset().idxPrev(cur);
+ cur.selHandle(false);
+ cur.clearTargetX();
+ cur.macroModeClose();
+ if (!cur.inset().idxPrev(cur)) {
+ cur.idx() = cur.lastidx();
+ cur.pos() = lyxrc.mac_like_cursor_movement ? cur.lastpos() : 0;
+ }
break;
case LFUN_WORD_DELETE_BACKWARD:
break;
case LFUN_SELF_INSERT:
- if (cmd.argument().size() != 1) {
- cur.recordUndoSelection();
- docstring const arg = cmd.argument();
- if (!interpretString(cur, arg))
- cur.insert(arg);
+ // special case first for big delimiters
+ if (cmd.argument().size() != 1 && interpretString(cur, cmd.argument()))
break;
- }
- // Don't record undo steps if we are in macro mode and thus
- // cmd.argument is the next character of the macro name.
- // Otherwise we'll get an invalid cursor if we undo after
- // the macro was finished and the macro is a known command,
- // e.g. sqrt. Cursor::macroModeClose replaces in this case
- // the InsetMathUnknown with name "frac" by an empty
- // InsetMathFrac -> a pos value > 0 is invalid.
- // A side effect is that an undo before the macro is finished
- // undoes the complete macro, not only the last character.
- // At the time we hit '\' we are not in macro mode, still.
- if (!cur.inMacroMode())
- cur.recordUndoSelection();
- // spacial handling of space. If we insert an inset
- // via macro mode, we want to put the cursor inside it
- // if relevant. Think typing "\frac<space>".
- if (cmd.argument()[0] == ' '
- && cur.inMacroMode() && cur.macroName() != "\\"
- && cur.macroModeClose() && cur.pos() > 0) {
- MathAtom const atom = cur.prevAtom();
- if (atom->asNestInset() && atom->isActive()) {
- cur.posBackward();
- cur.pushBackward(*cur.nextInset());
+ for (char_type c : cmd.argument()) {
+ // Don't record undo steps if we are in macro mode and thus
+ // cmd.argument is the next character of the macro name.
+ // Otherwise we'll get an invalid cursor if we undo after
+ // the macro was finished and the macro is a known command,
+ // e.g. sqrt. Cursor::macroModeClose replaces in this case
+ // the InsetMathUnknown with name "frac" by an empty
+ // InsetMathFrac -> a pos value > 0 is invalid.
+ // A side effect is that an undo before the macro is finished
+ // undoes the complete macro, not only the last character.
+ // At the time we hit '\' we are not in macro mode, still.
+ if (!cur.inMacroMode())
+ cur.recordUndoSelection();
+
+ // special handling of space. If we insert an inset
+ // via macro mode, we want to put the cursor inside it
+ // if relevant. Think typing "\frac<space>".
+ if (c == ' '
+ && cur.inMacroMode() && cur.macroName() != "\\"
+ && cur.macroModeClose() && cur.pos() > 0)
+ cur.editInsertedInset();
+ else if (!interpretChar(cur, c)) {
+ cmd = FuncRequest(LFUN_FINISHED_FORWARD);
+ cur.undispatched();
+ // FIXME: can we avoid skipping the end of the string?
+ break;
}
- } else if (!interpretChar(cur, cmd.argument()[0])) {
- cmd = FuncRequest(LFUN_FINISHED_FORWARD);
- cur.undispatched();
}
break;
if (cmd.argument().empty()) {
// do superscript if LyX handles
// deadkeys
- cur.recordUndoSelection();
+ cur.recordUndoInset();
script(cur, true, grabAndEraseSelection(cur));
}
break;
// check if we have a valid decoration
if (name != "pmatrix" && name != "bmatrix"
&& name != "Bmatrix" && name != "vmatrix"
- && name != "Vmatrix" && name != "matrix")
+ && name != "Vmatrix" && name != "matrix"
+ && name != "smallmatrix")
name = from_ascii("matrix");
cur.niceInsert(
cur.recordUndoSelection();
if (cmd.argument() == "^" || cmd.argument() == "_")
interpretChar(cur, cmd.argument()[0]);
+ else if (!cur.selection())
+ cur.niceInsert(cmd.argument());
else {
- MathData ar;
+ MathData ar(cur.buffer());
asArray(cmd.argument(), ar);
- if (cur.selection() && ar.size() == 1
+ if (ar.size() == 1
&& ar[0]->asNestInset()
&& ar[0]->asNestInset()->nargs() > 1)
handleNest(cur, ar[0]);
break;
}
- case LFUN_UNICODE_INSERT: {
- if (cmd.argument().empty())
- break;
- // splits on whitespace
- vector<docstring> args =
- getVectorFromString(cmd.argument(), from_ascii(" "), false, true);
- for (auto const & arg : args) {
- if (!isHex(arg)) {
- LYXERR0("Not a hexstring: " << arg);
- continue;
- }
- char_type c = hexToInt(arg);
- if (c >= 32 && c < 0x10ffff) {
- FuncCode code = currentMode() == MATH_MODE ?
- LFUN_MATH_INSERT : LFUN_SELF_INSERT;
- lyx::dispatch(FuncRequest(code, docstring(1, c)));
- }
- }
- break;
- }
-
case LFUN_DIALOG_SHOW_NEW_INSET: {
docstring const & name = cmd.argument();
string data;
}
break;
+ case LFUN_MATH_LIMITS: {
+ InsetMath * in = 0;
+ if (cur.pos() < cur.lastpos() && cur.nextMath().allowsLimitsChange())
+ in = &cur.nextMath();
+ else if (cur.pos() > 0 && cur.prevMath().allowsLimitsChange())
+ in = &cur.prevMath();
+ else if (cur.lastpos() > 0 && cur.cell().back()->allowsLimitsChange())
+ in = cur.cell().back().nucleus();
+ // only when nucleus allows this
+ if (!in)
+ return;
+ cur.recordUndoInset();
+ if (!cmd.argument().empty()) {
+ if (cmd.argument() == "limits")
+ in->limits(LIMITS);
+ else if (cmd.argument() == "nolimits")
+ in->limits(NO_LIMITS);
+ else
+ in->limits(AUTO_LIMITS);
+ } else if (in->limits() != AUTO_LIMITS)
+ in->limits(AUTO_LIMITS);
+ else if (in->defaultLimits(cur.cell().displayStyle()) == LIMITS)
+ in->limits(NO_LIMITS);
+ else
+ in->limits(LIMITS);
+ return;
+ }
+
+ case LFUN_PHANTOM_INSERT: {
+ docstring const & arg = cmd.argument();
+ docstring newarg;
+ if (arg == "Phantom")
+ newarg = from_ascii("\\phantom");
+ else if (arg == "HPhantom")
+ newarg = from_ascii("\\hphantom");
+ else if (arg == "VPhantom")
+ newarg = from_ascii("\\vphantom");
+ if (newarg.empty())
+ LYXERR0("Unknown phantom type " + newarg);
+ else {
+ FuncRequest const newfunc(LFUN_MATH_INSERT, newarg);
+ lyx::dispatch(newfunc);
+ }
+ break;
+ }
+
default:
InsetMath::doDispatch(cur, cmd);
break;
bool ret = true;
string const arg = to_utf8(cmd.argument());
switch (cmd.action()) {
- case LFUN_INSET_MODIFY:
- flag.setEnabled(false);
- break;
#if 0
case LFUN_INSET_MODIFY:
// FIXME: check temporarily disabled
case LFUN_INSET_INSERT: {
// Don't test createMathInset_fromDialogStr(), since
// getStatus is not called with a valid reference and the
- // dialog would not be applyable.
+ // dialog would not be applicable.
string const name = cmd.getArg(0);
flag.setEnabled(name == "ref" || name == "mathspace");
break;
break;
}
+ case LFUN_MATH_LIMITS: {
+ InsetMath * in = 0;
+ if (cur.pos() < cur.lastpos() && cur.nextMath().allowsLimitsChange())
+ in = &cur.nextMath();
+ else if (cur.pos() > 0 && cur.prevMath().allowsLimitsChange())
+ in = &cur.prevMath();
+ else if (cur.lastpos() > 0 && cur.cell().back()->allowsLimitsChange())
+ in = cur.cell().back().nucleus();
+ if (in) {
+ if (!cmd.argument().empty()) {
+ if (cmd.argument() == "limits")
+ flag.setOnOff(in->limits() == LIMITS);
+ else if (cmd.argument() == "nolimits")
+ flag.setOnOff(in->limits() == NO_LIMITS);
+ else
+ flag.setOnOff(in->limits() == AUTO_LIMITS);
+ }
+ flag.setEnabled(true);
+ } else
+ flag.setEnabled(false);
+ return true;
+ }
+
default:
ret = false;
break;
void InsetMathNest::edit(Cursor & cur, bool front, EntryDirection entry_from)
{
cur.push(*this);
- bool enter_front = (entry_from == Inset::ENTRY_DIRECTION_RIGHT ||
+ bool enter_front = (entry_from == Inset::ENTRY_DIRECTION_LEFT ||
(entry_from == Inset::ENTRY_DIRECTION_IGNORE && front));
- cur.idx() = enter_front ? 0 : cur.lastidx();
- cur.pos() = enter_front ? 0 : cur.lastpos();
+ enter_front ? idxFirst(cur) : idxLast(cur);
cur.resetAnchor();
//lyxerr << "InsetMathNest::edit, cur:\n" << cur << endl;
}
bool InsetMathNest::interpretChar(Cursor & cur, char_type const c)
{
+ // try auto-correction
+ if (lyxrc.autocorrection_math && cur.pos() != 0
+ && math_autocorrect(cur, c))
+ return true;
+
//lyxerr << "interpret 2: '" << c << "'" << endl;
docstring save_selection;
if (c == '^' || c == '_')
MathAtom const atom = cur.prevAtom();
if (atom->asNestInset() && atom->isActive()) {
cur.posBackward();
- cur.pushBackward(*cur.nextInset());
+ cur.nextInset()->edit(cur, true);
}
}
if (c == '{')
}
- // leave autocorrect mode if necessary
- if (lyxrc.autocorrection_math && c == ' ' && cur.autocorrect()) {
- cur.autocorrect() = false;
- cur.message(_("Autocorrect Off ('!' to enter)"));
- return true;
- }
- if (lyxrc.autocorrection_math && c == '!' && !cur.autocorrect()) {
- cur.autocorrect() = true;
- cur.message(_("Autocorrect On (<space> to exit)"));
- return true;
- }
-
// just clear selection on pressing the space bar
if (cur.selection() && c == ' ') {
cur.selection(false);
// but suppress direct insertion of two spaces in a row
// the still allows typing '<space>a<space>' and deleting the 'a', but
// it is better than nothing...
- if (cur.pos() == 0 || cur.prevAtom()->getChar() != ' ') {
+ pos_type const pos = cur.pos();
+ pos_type const lastpos = cur.lastpos();
+ if ((pos == 0 && lastpos == 0)
+ || (pos == 0 && cur.nextAtom()->getChar() != ' ')
+ || (pos == lastpos && cur.prevAtom()->getChar() != ' ')
+ || (pos > 0 && cur.prevAtom()->getChar() != ' '
+ && cur.nextAtom()->getChar() != ' ')) {
cur.insert(c);
// FIXME: we have to enable full redraw here because of the
// visual box corners that define the inset. If we know for
return true;
} else if (currentMode() != InsetMath::TEXT_MODE) {
if (c == '_') {
+ cur.recordUndoInset();
script(cur, false, save_selection);
return true;
}
if (c == '^') {
+ cur.recordUndoInset();
script(cur, true, save_selection);
return true;
}
if (currentMode() == InsetMath::MATH_MODE && Encodings::isUnicodeTextOnly(c)) {
MathAtom at = createInsetMath("text", buf);
at.nucleus()->cell(0).push_back(MathAtom(new InsetMathChar(c)));
- cur.niceInsert(at);
+ cur.insert(at);
cur.posForward();
return true;
}
return true;
}
-
- // try auto-correction
- if (lyxrc.autocorrection_math && cur.autocorrect() && cur.pos() != 0
- && math_autocorrect(cur.prevAtom(), c))
- return true;
-
// no special circumstances, so insert the character without any fuss
cur.insert(c);
- if (lyxrc.autocorrection_math) {
- if (!cur.autocorrect())
- cur.message(_("Autocorrect Off ('!' to enter)"));
- else
- cur.message(_("Autocorrect On (<space> to exit)"));
- }
return true;
}
bool InsetMathNest::interpretString(Cursor & cur, docstring const & str)
{
+ if (str == "\\limits" || str == "\\nolimits") {
+ if (cur.pos() > 0 && cur.prevMath().allowsLimitsChange()) {
+ cur.prevMath().limits(str == "\\limits" ? LIMITS : NO_LIMITS);
+ return true;
+ } else {
+ cur.message(bformat(_("Cannot apply %1$s here."), str));
+ return false;
+ }
+ }
// Create a InsetMathBig from cur.cell()[cur.pos() - 1] and t if
// possible
if (!cur.empty() && cur.pos() > 0 &&
prev = prev.substr(1);
latexkeys const * l = in_word_set(prev);
if (l && l->inset == "big") {
+ cur.recordUndoSelection();
cur.cell()[cur.pos() - 1] =
MathAtom(new InsetMathBig(prev, str));
return true;
InsetMathNest::createCompletionList(Cursor const & cur) const
{
if (!cur.inMacroMode())
- return 0;
+ return nullptr;
return new MathCompletionList(cur);
}
else
cmd = locals[idx];
- // get the icon resource name by stripping the backslash
+ // get the icon name by stripping the backslash
docstring icon_name = frontend::Application::mathIcon(cmd.substr(1));
if (icon_name.empty())
return std::string();
- return "images/math/" + to_utf8(icon_name);
+ return "math/" + to_utf8(icon_name);
}
std::vector<docstring> MathCompletionList::globals;