#include "math_nestinset.h"
#include "math_arrayinset.h"
+#include "math_boxinset.h"
+#include "math_braceinset.h"
+#include "math_colorinset.h"
+#include "math_commentinset.h"
#include "math_data.h"
#include "math_deliminset.h"
#include "math_factory.h"
#include "math_hullinset.h"
#include "math_mathmlstream.h"
+#include "math_macroarg.h"
+//#include "math_mboxinset.h"
#include "math_parser.h"
+#include "math_scriptinset.h"
#include "math_spaceinset.h"
+#include "math_symbolinset.h"
#include "math_support.h"
-#include "math_mboxinset.h"
+#include "math_unknowninset.h"
+#include "ref_inset.h"
#include "BufferView.h"
+#include "CutAndPaste.h"
+#include "FuncStatus.h"
+#include "LColor.h"
#include "bufferview_funcs.h"
+#include "coordcache.h"
#include "cursor.h"
#include "debug.h"
#include "dispatchresult.h"
#include "funcrequest.h"
#include "gettext.h"
-#include "LColor.h"
+#include "outputparams.h"
#include "undo.h"
-#include "support/std_sstream.h"
#include "support/lstrings.h"
#include "frontends/Dialogs.h"
#include "frontends/LyXView.h"
#include "frontends/Painter.h"
+#include "frontends/nullpainter.h"
+#include <sstream>
+
+using lyx::cap::copySelection;
+using lyx::cap::grabAndEraseSelection;
+using lyx::cap::cutSelection;
+using lyx::cap::replaceSelection;
+using lyx::cap::selClearOrDel;
using std::endl;
using std::string;
using std::istringstream;
-namespace {
-
-// local global
-int first_x;
-int first_y;
-
-} // namespace anon
-
-
-
-
MathNestInset::MathNestInset(idx_type nargs)
: cells_(nargs), lock_(false)
{}
}
-void MathNestInset::getCursorPos(CursorSlice const & cur,
+void MathNestInset::cursorPos(CursorSlice const & sl, bool /*boundary*/,
int & x, int & y) const
{
- BOOST_ASSERT(ptr_cmp(cur.inset(), this));
- MathArray const & ar = cur.cell();
- x = ar.xo() + ar.pos2x(cur.pos());
- y = ar.yo();
+// FIXME: This is a hack. Ideally, the coord cache should not store
+// absolute positions, but relative ones. This would mean to call
+// setXY() not in MathArray::draw(), but in the parent insets' draw()
+// with the correctly adjusted x,y values. But this means that we'd have
+// to touch all (math)inset's draw() methods. Right now, we'll store
+// absolute value, and make them here relative, only to make them
+// absolute again when actually drawing the cursor. What a mess.
+ BOOST_ASSERT(ptr_cmp(&sl.inset(), this));
+ MathArray const & ar = sl.cell();
+ if (!theCoords.getArrays().has(&ar)) {
+ // this can (semi-)legally happen if we just created this cell
+ // and it never has been drawn before. So don't ASSERT.
+ //lyxerr << "no cached data for array " << &ar << endl;
+ x = 0;
+ y = 0;
+ return;
+ }
+ Point const pt = theCoords.getArrays().xy(&ar);
+ if (!theCoords.getInsets().has(this)) {
+ // same as above
+ //lyxerr << "no cached data for inset " << this << endl;
+ x = 0;
+ y = 0;
+ return;
+ }
+ Point const pt2 = theCoords.getInsets().xy(this);
+ //lyxerr << "retrieving position cache for MathArray "
+ // << pt.x_ << ' ' << pt.y_ << std::endl;
+ x = pt.x_ - pt2.x_ + ar.pos2x(sl.pos());
+ y = pt.y_ - pt2.y_;
+// lyxerr << "pt.y_ : " << pt.y_ << " pt2_.y_ : " << pt2.y_
+// << " asc: " << ascent() << " des: " << descent()
+// << " ar.asc: " << ar.ascent() << " ar.des: " << ar.descent() << endl;
// move cursor visually into empty cells ("blue rectangles");
- if (cur.cell().empty())
+ if (ar.empty())
x += 2;
}
-void MathNestInset::substitute(MathMacro const & m)
-{
- for (idx_type i = 0; i < nargs(); ++i)
- cell(i).substitute(m);
-}
-
-
void MathNestInset::metrics(MetricsInfo const & mi) const
{
MetricsInfo m = mi;
- for (idx_type i = 0; i < nargs(); ++i)
+ for (idx_type i = 0, n = nargs(); i != n; ++i)
cell(i).metrics(m);
}
bool MathNestInset::idxNext(LCursor & cur) const
{
- BOOST_ASSERT(ptr_cmp(cur.inset(), this));
+ BOOST_ASSERT(ptr_cmp(&cur.inset(), this));
if (cur.idx() == cur.lastidx())
return false;
++cur.idx();
bool MathNestInset::idxPrev(LCursor & cur) const
{
- BOOST_ASSERT(ptr_cmp(cur.inset(), this));
+ BOOST_ASSERT(ptr_cmp(&cur.inset(), this));
if (cur.idx() == 0)
return false;
--cur.idx();
bool MathNestInset::idxFirst(LCursor & cur) const
{
- BOOST_ASSERT(ptr_cmp(cur.inset(), this));
+ BOOST_ASSERT(ptr_cmp(&cur.inset(), this));
if (nargs() == 0)
return false;
cur.idx() = 0;
bool MathNestInset::idxLast(LCursor & cur) const
{
- BOOST_ASSERT(ptr_cmp(cur.inset(), this));
+ BOOST_ASSERT(ptr_cmp(&cur.inset(), this));
if (nargs() == 0)
return false;
cur.idx() = cur.lastidx();
}
-bool MathNestInset::idxHome(LCursor & cur) const
-{
- BOOST_ASSERT(ptr_cmp(cur.inset(), this));
- if (cur.pos() == 0)
- return false;
- cur.pos() = 0;
- return true;
-}
-
-
-bool MathNestInset::idxEnd(LCursor & cur) const
-{
- BOOST_ASSERT(ptr_cmp(cur.inset(), this));
- if (cur.lastpos() == cur.lastpos())
- return false;
- cur.pos() = cur.lastpos();
- return true;
-}
-
-
void MathNestInset::dump() const
{
WriteStream os(lyxerr);
os << "---------------------------------------------\n";
write(os);
os << "\n";
- for (idx_type i = 0; i < nargs(); ++i)
+ for (idx_type i = 0, n = nargs(); i != n; ++i)
os << cell(i) << "\n";
os << "---------------------------------------------\n";
}
-//void MathNestInset::draw(PainterInfo & pi, int x, int y) const
-void MathNestInset::draw(PainterInfo &, int, int) const
+void MathNestInset::draw(PainterInfo & pi, int x, int y) const
{
#if 0
if (lock_)
pi.pain.fillRectangle(x, y - ascent(), width(), height(),
LColor::mathlockbg);
#endif
+ setPosCache(pi, x, y);
}
-void MathNestInset::drawSelection(PainterInfo & pi, int, int) const
+void MathNestInset::drawSelection(PainterInfo & pi, int x, int y) const
{
// this should use the x/y values given, not the cached values
LCursor & cur = pi.base.bv->cursor();
if (!cur.selection())
return;
- if (!ptr_cmp(cur.inset(), this))
+ if (!ptr_cmp(&cur.inset(), this))
return;
- CursorSlice & s1 = cur.selBegin();
- CursorSlice & s2 = cur.selEnd();
+
+ // FIXME: hack to get position cache warm
+ static NullPainter nop;
+ PainterInfo pinop(pi);
+ pinop.pain = nop;
+ draw(pinop, x, y);
+
+ CursorSlice s1 = cur.selBegin();
+ CursorSlice s2 = cur.selEnd();
+ //lyxerr << "MathNestInset::drawing selection: "
+ // << " s1: " << s1 << " s2: " << s2 << endl;
if (s1.idx() == s2.idx()) {
MathArray const & c = cell(s1.idx());
int x1 = c.xo() + c.pos2x(s1.pos());
int x2 = c.xo() + c.pos2x(s2.pos());
int y2 = c.yo() + c.descent();
pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
+ //lyxerr << "MathNestInset::drawing selection 3: "
+ // << " x1: " << x1 << " x2: " << x2
+ // << " y1: " << y1 << " y2: " << y2 << endl;
} else {
for (idx_type i = 0; i < nargs(); ++i) {
if (idxBetween(i, s1.idx(), s2.idx())) {
}
-void MathNestInset::notifyCursorLeaves(idx_type idx)
+int MathNestInset::latex(Buffer const &, std::ostream & os,
+ OutputParams const & runparams) const
{
- cell(idx).notifyCursorLeaves();
+ WriteStream wi(os, runparams.moving_arg, true);
+ write(wi);
+ return wi.line();
+}
+
+
+void MathNestInset::notifyCursorLeaves(LCursor & cur)
+{
+#ifdef WITH_WARNINGS
+#warning look here
+#endif
+#if 0
+ MathArray & ar = cur.cell();
+ // remove base-only "scripts"
+ for (pos_type i = 0; i + 1 < ar.size(); ++i) {
+ MathScriptInset * p = operator[](i).nucleus()->asScriptInset();
+ if (p && p->nargs() == 1) {
+ MathArray ar = p->nuc();
+ erase(i);
+ insert(i, ar);
+ cur.adjust(i, ar.size() - 1);
+ }
+ }
+
+ // glue adjacent font insets of the same kind
+ for (pos_type i = 0; i + 1 < size(); ++i) {
+ MathFontInset * p = operator[](i).nucleus()->asFontInset();
+ MathFontInset const * q = operator[](i + 1)->asFontInset();
+ if (p && q && p->name() == q->name()) {
+ p->cell(0).append(q->cell(0));
+ erase(i + 1);
+ cur.adjust(i, -1);
+ }
+ }
+#endif
}
{
// this whole function is a hack and won't work for incremental font
// changes...
- //recordUndo(cur, Undo::ATOMIC);
+ recordUndo(cur, Undo::ATOMIC);
- if (cur.inset()->asMathInset()->name() == font)
+ if (cur.inset().asMathInset()->name() == font)
cur.handleFont(font);
else {
cur.handleNest(createMathInset(font));
void MathNestInset::handleFont2(LCursor & cur, string const & arg)
{
- //recordUndo(cur, Undo::ATOMIC);
+ recordUndo(cur, Undo::ATOMIC);
LyXFont font;
bool b;
bv_funcs::string2font(arg, font, b);
if (font.color() != LColor::inherit) {
- MathAtom at = createMathInset("color");
- asArray(lcolor.getGUIName(font.color()), at.nucleus()->cell(0));
- cur.handleNest(at, 1);
+ MathAtom at = MathAtom(new MathColorInset(true, font.color()));
+ cur.handleNest(at, 0);
}
}
-void MathNestInset::priv_dispatch(LCursor & cur, FuncRequest const & cmd)
+void MathNestInset::doDispatch(LCursor & cur, FuncRequest & cmd)
{
- lyxerr << "MathNestInset: request: " << cmd << std::endl;
+ //lyxerr << "MathNestInset: request: " << cmd << std::endl;
+ //CursorSlice sl = cur.current();
switch (cmd.action) {
- case LFUN_PASTE:
- if (!cmd.argument.empty()) {
- MathArray ar;
- mathed_parse_cell(ar, cmd.argument);
- cur.cell().insert(cur.pos(), ar);
- cur.pos() += ar.size();
- }
- break;
-/*
case LFUN_PASTE: {
+ recordUndo(cur);
+ cur.message(_("Paste"));
+ replaceSelection(cur);
size_t n = 0;
- istringstream is(cmd.argument.c_str());
+ istringstream is(cmd.argument);
is >> n;
- if (was_macro)
- cur.macroModeClose();
- //recordUndo(cur, Undo::ATOMIC);
- cur.selPaste(n);
+ string const selection = lyx::cap::getSelection(cur.buffer(), n);
+ cur.niceInsert(selection);
+ cur.clearSelection(); // bug 393
+ cur.bv().switchKeyMap();
+ finishUndo();
break;
}
-*/
- case LFUN_PASTESELECTION:
- dispatch(cur, FuncRequest(LFUN_PASTE, cur.bv().getClipboard()));
+ case LFUN_CUT:
+ recordUndo(cur);
+ cutSelection(cur, true, true);
+ cur.message(_("Cut"));
+ // Prevent stale position >= size crash
+ // Probably not necessary anymore, see eraseSelection (gb 2005-10-09)
+ cur.normalize();
+ break;
+
+ case LFUN_COPY:
+ copySelection(cur);
+ cur.message(_("Copy"));
break;
case LFUN_MOUSE_PRESS:
lfunMouseRelease(cur, cmd);
break;
- case LFUN_MOUSE_DOUBLE:
- case LFUN_MOUSE_TRIPLE:
- //lyxerr << "Mouse double" << endl;
- //lyxerr << "Mouse triple" << endl;
- dispatch(cur, FuncRequest(LFUN_WORDSEL));
- break;
-
case LFUN_FINISHED_LEFT:
- cur.pop(cur.currentDepth());
cur.bv().cursor() = cur;
break;
case LFUN_FINISHED_RIGHT:
- cur.pop(cur.currentDepth());
++cur.pos();
cur.bv().cursor() = cur;
break;
case LFUN_FINISHED_UP:
- cur.pop(cur.currentDepth());
- //idxUpDown(cur, true);
cur.bv().cursor() = cur;
break;
case LFUN_FINISHED_DOWN:
- cur.pop(cur.currentDepth());
- //idxUpDown(cur, false);
+ ++cur.pos();
cur.bv().cursor() = cur;
break;
case LFUN_RIGHTSEL:
case LFUN_RIGHT:
cur.selHandle(cmd.action == LFUN_RIGHTSEL);
- if (!cur.right())
- cur.dispatched(FINISHED_RIGHT);
+ cur.autocorrect() = false;
+ cur.clearTargetX();
+ cur.macroModeClose();
+ if (cur.pos() != cur.lastpos() && cur.openable(cur.nextAtom())) {
+ cur.pushLeft(*cur.nextAtom().nucleus());
+ cur.inset().idxFirst(cur);
+ } else if (cur.posRight() || idxRight(cur)
+ || cur.popRight() || cur.selection())
+ ;
+ else {
+ cmd = FuncRequest(LFUN_FINISHED_RIGHT);
+ cur.undispatched();
+ }
break;
case LFUN_LEFTSEL:
case LFUN_LEFT:
cur.selHandle(cmd.action == LFUN_LEFTSEL);
- if (!cur.left())
- cur.dispatched(FINISHED_LEFT);
+ cur.autocorrect() = false;
+ cur.clearTargetX();
+ cur.macroModeClose();
+ if (cur.pos() != 0 && cur.openable(cur.prevAtom())) {
+ cur.posLeft();
+ cur.push(*cur.nextAtom().nucleus());
+ cur.inset().idxLast(cur);
+ } else if (cur.posLeft() || idxLeft(cur)
+ || cur.popLeft() || cur.selection())
+ ;
+ else {
+ cmd = FuncRequest(LFUN_FINISHED_LEFT);
+ cur.undispatched();
+ }
break;
case LFUN_UPSEL:
case LFUN_UP:
+ // FIXME Tried to use clearTargetX and macroModeClose, crashed on cur.up()
+ if (cur.inMacroMode()) {
+ // Make Helge happy
+ cur.macroModeClose();
+ break;
+ }
cur.selHandle(cmd.action == LFUN_UPSEL);
- if (!cur.up())
- cur.dispatched(FINISHED_UP);
+ if (!cur.up()) {
+ cmd = FuncRequest(LFUN_FINISHED_UP);
+ cur.undispatched();
+ }
+ // fixes bug 1598. Please check!
+ cur.normalize();
break;
case LFUN_DOWNSEL:
case LFUN_DOWN:
+ if (cur.inMacroMode()) {
+ cur.macroModeClose();
+ break;
+ }
cur.selHandle(cmd.action == LFUN_DOWNSEL);
- if (!cur.down())
- cur.dispatched(FINISHED_DOWN);
+ if (!cur.down()) {
+ cmd = FuncRequest(LFUN_FINISHED_DOWN);
+ cur.undispatched();
+ }
+ // fixes bug 1598. Please check!
+ cur.normalize();
break;
+ case LFUN_MOUSE_DOUBLE:
+ case LFUN_MOUSE_TRIPLE:
case LFUN_WORDSEL:
- cur.home();
+ cur.pos() = 0;
+ cur.idx() = 0;
cur.resetAnchor();
cur.selection() = true;
- cur.end();
+ cur.pos() = cur.lastpos();
+ cur.idx() = cur.lastidx();
break;
case LFUN_UP_PARAGRAPHSEL:
case LFUN_DOWN_PARAGRAPH:
break;
+ case LFUN_HOMESEL:
+ case LFUN_HOME:
case LFUN_WORDLEFTSEL:
case LFUN_WORDLEFT:
- cur.selHandle(cmd.action == LFUN_WORDLEFTSEL);
- if (!cur.home())
- cur.dispatched(FINISHED_LEFT);
+ cur.selHandle(cmd.action == LFUN_WORDLEFTSEL || cmd.action == LFUN_HOMESEL);
+ cur.macroModeClose();
+ if (cur.pos() != 0) {
+ cur.pos() = 0;
+ } else if (cur.col() != 0) {
+ cur.idx() -= cur.col();
+ cur.pos() = 0;
+ } else if (cur.idx() != 0) {
+ cur.idx() = 0;
+ cur.pos() = 0;
+ } else {
+ cmd = FuncRequest(LFUN_FINISHED_LEFT);
+ cur.undispatched();
+ }
break;
case LFUN_WORDRIGHTSEL:
case LFUN_WORDRIGHT:
- cur.selHandle(cmd.action == LFUN_WORDRIGHTSEL);
- if (!cur.end())
- cur.dispatched(FINISHED_RIGHT);
- break;
-
- case LFUN_HOMESEL:
- case LFUN_HOME:
- cur.selHandle(cmd.action == LFUN_HOMESEL);
- if (!cur.home())
- cur.dispatched(FINISHED_LEFT);
- break;
-
case LFUN_ENDSEL:
case LFUN_END:
- cur.selHandle(cmd.action == LFUN_ENDSEL);
- if (!cur.end())
- cur.dispatched(FINISHED_RIGHT);
+ cur.selHandle(cmd.action == LFUN_WORDRIGHTSEL || cmd.action == LFUN_ENDSEL);
+ cur.macroModeClose();
+ cur.clearTargetX();
+ if (cur.pos() != cur.lastpos()) {
+ cur.pos() = cur.lastpos();
+ } else if (ncols() && (cur.col() != cur.lastcol())) {
+ cur.idx() = cur.idx() - cur.col() + cur.lastcol();
+ cur.pos() = cur.lastpos();
+ } else if (cur.idx() != cur.lastidx()) {
+ cur.idx() = cur.lastidx();
+ cur.pos() = cur.lastpos();
+ } else {
+ cmd = FuncRequest(LFUN_FINISHED_RIGHT);
+ cur.undispatched();
+ }
break;
case LFUN_PRIORSEL:
case LFUN_PRIOR:
- case LFUN_BEGINNINGBUFSEL:
- case LFUN_BEGINNINGBUF:
- cur.dispatched(FINISHED_LEFT);
+ cmd = FuncRequest(LFUN_FINISHED_LEFT);
+ cur.undispatched();
break;
case LFUN_NEXTSEL:
case LFUN_NEXT:
- case LFUN_ENDBUFSEL:
- case LFUN_ENDBUF:
- cur.dispatched(FINISHED_RIGHT);
+ cmd = FuncRequest(LFUN_FINISHED_RIGHT);
+ cur.undispatched();
break;
case LFUN_CELL_FORWARD:
- cur.inset()->idxNext(cur);
+ cur.inset().idxNext(cur);
break;
case LFUN_CELL_BACKWARD:
- cur.inset()->idxPrev(cur);
+ cur.inset().idxPrev(cur);
break;
case LFUN_DELETE_WORD_BACKWARD:
case LFUN_BACKSPACE:
- //recordUndo(cur, Undo::ATOMIC);
+ if (cur.pos() == 0)
+ // delete whole cell
+ recordUndoInset(cur, Undo::ATOMIC);
+ else
+ recordUndo(cur, Undo::ATOMIC);
cur.backspace();
break;
case LFUN_DELETE_WORD_FORWARD:
case LFUN_DELETE:
- //recordUndo(cur, Undo::ATOMIC);
+ recordUndo(cur);
cur.erase();
- cur.dispatched(FINISHED_LEFT);
break;
case LFUN_ESCAPE:
- if (cur.selection())
- cur.selClear();
- else
- cur.dispatched(FINISHED_LEFT);
+ if (cur.selection())
+ cur.clearSelection();
+ else {
+ cmd = FuncRequest(LFUN_FINISHED_RIGHT);
+ cur.undispatched();
+ }
break;
case LFUN_INSET_TOGGLE:
- cur.lockToggle();
+ recordUndo(cur);
+ //lockToggle();
+ if (cur.pos() != cur.lastpos()) {
+ // toggle previous inset ...
+ cur.nextAtom().nucleus()->lock(!cur.nextAtom()->lock());
+ } else if (cur.popLeft() && cur.pos() != cur.lastpos()) {
+ // ... or enclosing inset if we are in the last inset position
+ cur.nextAtom().nucleus()->lock(!cur.nextAtom()->lock());
+ ++cur.pos();
+ }
break;
case LFUN_SELFINSERT:
- if (cmd.argument.empty()) {
- cur.dispatched(FINISHED_RIGHT);
- break;
- }
- //recordUndo(cur, Undo::ATOMIC);
if (cmd.argument.size() != 1) {
+ recordUndo(cur);
cur.insert(cmd.argument);
break;
}
- if (!cur.interpret(cmd.argument[0]))
- cur.dispatched(FINISHED_RIGHT);
+ // Don't record undo steps if we are in macro mode and
+ // 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. LCursor::macroModeClose replaces in this case
+ // the MathUnknownInset with name "frac" by an empty
+ // MathFracInset -> 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.
+ if (!cur.inMacroMode())
+ recordUndo(cur);
+
+ // 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()) {
+ MathAtom const atom = cur.prevAtom();
+ if (atom->asNestInset() && atom->nargs() > 0) {
+ cur.posLeft();
+ cur.pushLeft(*cur.nextInset());
+ }
+ } else if (!interpret(cur, cmd.argument[0])) {
+ cmd = FuncRequest(LFUN_FINISHED_RIGHT);
+ cur.undispatched();
+ }
break;
-#if 0
-//
-// this needs to be incorporated
-//
- // delete empty mathbox (LFUN_BACKSPACE and LFUN_DELETE)
- bool remove_inset = false;
-
- DispatchResult result(true);
- bool was_macro = cur.inMacroMode();
-
- cur.normalize();
- cur.touch();
-#endif
+ //case LFUN_GETXY:
+ // sprintf(dispatch_buffer, "%d %d",);
+ // break;
- // case LFUN_GETXY:
- // sprintf(dispatch_buffer, "%d %d",);
- // DispatchResult= dispatch_buffer;
- // break;
case LFUN_SETXY: {
lyxerr << "LFUN_SETXY broken!" << endl;
int x = 0;
int y = 0;
- istringstream is(cmd.argument.c_str());
+ istringstream is(cmd.argument);
is >> x >> y;
cur.setScreenPos(x, y);
break;
}
- case LFUN_CUT:
- //recordUndo(cur, Undo::DELETE);
- cur.selCut();
- break;
-
- case LFUN_COPY:
- cur.selCopy();
- break;
-
// Special casing for superscript in case of LyX handling
// dead-keys:
case LFUN_CIRCUMFLEX:
if (cmd.argument.empty()) {
// do superscript if LyX handles
// deadkeys
- //recordUndo(cur, Undo::ATOMIC);
- cur.script(true);
+ recordUndo(cur, Undo::ATOMIC);
+ script(cur, true);
}
break;
break;
case LFUN_BOLD:
- handleFont(cur, cmd.argument, "mathbf");
+ if (currentMode() == TEXT_MODE)
+ handleFont(cur, cmd.argument, "textbf");
+ else
+ handleFont(cur, cmd.argument, "mathbf");
break;
case LFUN_SANS:
- handleFont(cur, cmd.argument, "mathsf");
+ if (currentMode() == TEXT_MODE)
+ handleFont(cur, cmd.argument, "textsf");
+ else
+ handleFont(cur, cmd.argument, "mathsf");
break;
case LFUN_EMPH:
- handleFont(cur, cmd.argument, "mathcal");
+ if (currentMode() == TEXT_MODE)
+ handleFont(cur, cmd.argument, "emph");
+ else
+ handleFont(cur, cmd.argument, "mathcal");
break;
case LFUN_ROMAN:
- handleFont(cur, cmd.argument, "mathrm");
+ if (currentMode() == TEXT_MODE)
+ handleFont(cur, cmd.argument, "textrm");
+ else
+ handleFont(cur, cmd.argument, "mathrm");
break;
case LFUN_CODE:
- handleFont(cur, cmd.argument, "texttt");
+ if (currentMode() == TEXT_MODE)
+ handleFont(cur, cmd.argument, "texttt");
+ else
+ handleFont(cur, cmd.argument, "mathtt");
break;
case LFUN_FRAK:
handleFont(cur, cmd.argument, "mathfrak");
break;
case LFUN_ITAL:
- handleFont(cur, cmd.argument, "mathit");
+ if (currentMode() == TEXT_MODE)
+ handleFont(cur, cmd.argument, "textit");
+ else
+ handleFont(cur, cmd.argument, "mathit");
break;
case LFUN_NOUN:
- handleFont(cur, cmd.argument, "mathbb");
+ if (currentMode() == TEXT_MODE)
+ // FIXME: should be "noun"
+ handleFont(cur, cmd.argument, "textsc");
+ else
+ handleFont(cur, cmd.argument, "mathbb");
break;
//case LFUN_FREEFONT_APPLY:
handleFont(cur, cmd.argument, "textrm");
case LFUN_MATH_MODE:
#if 1
+ // ignore math-mode on when already in math mode
+ if (currentMode() == InsetBase::MATH_MODE && cmd.argument == "on")
+ break;
cur.macroModeClose();
- cur.selClearOrDel();
- cur.plainInsert(MathAtom(new MathMBoxInset(cur.bv())));
+ selClearOrDel(cur);
+ //cur.plainInsert(MathAtom(new MathMBoxInset(cur.bv())));
+ cur.plainInsert(MathAtom(new MathBoxInset("mbox")));
cur.posLeft();
- cur.pushLeft(cur.nextInset());
+ cur.pushLeft(*cur.nextInset());
#else
- if (cur.currentMode() == InsetBase::TEXT_MODE)
+ if (currentMode() == InsetBase::TEXT_MODE) {
cur.niceInsert(MathAtom(new MathHullInset("simple")));
- else
+ cur.message(_("create new math text environment ($...$)"));
+ } else {
handleFont(cur, cmd.argument, "textrm");
- //cur.owner()->message(_("math text mode toggled"));
+ cur.message(_("entered math text mode (textrm)"));
+ }
#endif
break;
case LFUN_MATH_SIZE:
#if 0
- if (!arg.empty()) {
- //recordUndo(cur, Undo::ATOMIC);
- cur.setSize(arg);
- }
+ recordUndo(cur);
+ cur.setSize(arg);
#endif
break;
case LFUN_INSERT_MATRIX: {
- //recordUndo(cur, Undo::ATOMIC);
+ recordUndo(cur, Undo::ATOMIC);
unsigned int m = 1;
unsigned int n = 1;
string v_align;
ls = '(';
if (rs.empty())
rs = ')';
- //recordUndo(cur, Undo::ATOMIC);
- cur.handleNest(MathAtom(new MathDelimInset(ls, rs)));
+ recordUndo(cur, Undo::ATOMIC);
+ // Don't do this with multi-cell selections
+ if (cur.selBegin().idx() == cur.selEnd().idx())
+ cur.handleNest(MathAtom(new MathDelimInset(ls, rs)));
break;
}
case LFUN_SPACE_INSERT:
case LFUN_MATH_SPACE:
- //recordUndo(cur, Undo::ATOMIC);
+ recordUndo(cur, Undo::ATOMIC);
cur.insert(MathAtom(new MathSpaceInset(",")));
break;
- case LFUN_UNDO:
-#warning look here
- //cur.bv().owner()->message(_("Invalid action in math mode!"));
- break;
-
case LFUN_INSET_ERT:
// interpret this as if a backslash was typed
- //recordUndo(cur, Undo::ATOMIC);
- cur.interpret('\\');
+ recordUndo(cur, Undo::ATOMIC);
+ interpret(cur, '\\');
+ break;
+
+ case LFUN_SUBSCRIPT:
+ // interpret this as if a _ was typed
+ recordUndo(cur, Undo::ATOMIC);
+ interpret(cur, '_');
+ break;
+
+ case LFUN_SUPERSCRIPT:
+ // interpret this as if a ^ was typed
+ recordUndo(cur, Undo::ATOMIC);
+ interpret(cur, '^');
break;
// FIXME: We probably should swap parts of "math-insert" and "self-insert"
// handling such that "self-insert" works on "arbitrary stuff" too, and
// math-insert only handles special math things like "matrix".
- case LFUN_INSERT_MATH:
- //recordUndo(cur, Undo::ATOMIC);
- cur.niceInsert(cmd.argument);
+ case LFUN_INSERT_MATH: {
+ recordUndo(cur, Undo::ATOMIC);
+ MathArray ar;
+ asArray(cmd.argument, ar);
+ int cell(0);
+ if (cmd.argument == "\\root")
+ cell = 1;
+ // math macros are nest insets and may have 0 cells.
+ // handleNest would crash in this case.
+ if (ar.size() == 1 && (ar[0].nucleus()->asNestInset()) &&
+ ar[0].nucleus()->nargs() > MathInset::idx_type(cell)) {
+ cur.handleNest(ar[0], cell);
+ } else
+ cur.niceInsert(cmd.argument);
break;
+ }
case LFUN_DIALOG_SHOW_NEW_INSET: {
string const & name = cmd.argument;
string data;
-#if 0
if (name == "ref") {
RefInset tmp(name);
data = tmp.createDialogStr(name);
}
-#endif
cur.bv().owner()->getDialogs().show(name, data, 0);
break;
}
- case LFUN_INSET_APPLY: {
- string const name = cmd.getArg(0);
- InsetBase * base = cur.bv().owner()->getDialogs().getOpenInset(name);
+ default:
+ MathDimInset::doDispatch(cur, cmd);
+ break;
+ }
+}
- if (base) {
- base->dispatch(cur, FuncRequest(LFUN_INSET_MODIFY, cmd.argument));
+
+bool MathNestInset::getStatus(LCursor & /*cur*/, FuncRequest const & cmd,
+ FuncStatus & flag) const
+{
+ // the font related toggles
+ //string tc = "mathnormal";
+ bool ret = true;
+ string const arg = cmd.argument;
+ switch (cmd.action) {
+ case LFUN_TABULAR_FEATURE:
+ flag.enabled(false);
+ break;
+#if 0
+ case LFUN_TABULAR_FEATURE:
+ // FIXME: check temporarily disabled
+ // valign code
+ char align = mathcursor::valign();
+ if (align == '\0') {
+ enable = false;
break;
}
- MathArray ar;
- if (createMathInset_fromDialogStr(cmd.argument, ar)) {
- cur.insert(ar);
+ if (cmd.argument.empty()) {
+ flag.clear();
+ break;
+ }
+ if (!contains("tcb", cmd.argument[0])) {
+ enable = false;
break;
}
- cur.notdispatched();
+ flag.setOnOff(cmd.argument[0] == align);
break;
- }
-
-#warning look here
-#if 0
-
- case LFUN_WORD_REPLACE:
- case LFUN_WORD_FIND:
- if (!searchForward(&cur.bv(), cmd.getArg(0), false, false))
- cur.notdispatched();
+#endif
+ /// We have to handle them since 1.4 blocks all unhandled actions
+ case LFUN_ITAL:
+ case LFUN_BOLD:
+ case LFUN_SANS:
+ case LFUN_EMPH:
+ case LFUN_CODE:
+ case LFUN_NOUN:
+ case LFUN_ROMAN:
+ case LFUN_DEFAULT:
+ flag.enabled(true);
+ break;
+ case LFUN_MATH_MUTATE:
+ //flag.setOnOff(mathcursor::formula()->hullType() == cmd.argument);
+ flag.setOnOff(false);
break;
- cur.normalize();
- cur.touch();
+ // we just need to be in math mode to enable that
+ case LFUN_MATH_SIZE:
+ case LFUN_MATH_SPACE:
+ case LFUN_MATH_LIMITS:
+ case LFUN_MATH_NONUMBER:
+ case LFUN_MATH_NUMBER:
+ case LFUN_MATH_EXTERN:
+ flag.enabled(true);
+ break;
- BOOST_ASSERT(cur.inMathed());
+ case LFUN_FRAK:
+ flag.enabled(currentMode() != TEXT_MODE);
+ break;
- if (result.dispatched()) {
- revealCodes(cur);
- cur.bv().stuffClipboard(cur.grabSelection());
- } else {
- if (remove_inset)
- cur.bv().owner()->dispatch(FuncRequest(LFUN_DELETE));
+ case LFUN_INSERT_MATH: {
+ bool const textarg =
+ arg == "\\textbf" || arg == "\\textsf" ||
+ arg == "\\textrm" || arg == "\\textmd" ||
+ arg == "\\textit" || arg == "\\textsc" ||
+ arg == "\\textsl" || arg == "\\textup" ||
+ arg == "\\texttt" || arg == "\\textbb" ||
+ arg == "\\textnormal";
+ flag.enabled(currentMode() != TEXT_MODE || textarg);
+ break;
}
- break;
-#endif
+ case LFUN_INSERT_MATRIX:
+ flag.enabled(currentMode() == MATH_MODE);
+ break;
default:
- MathDimInset::priv_dispatch(cur, cmd);
+ ret = false;
break;
}
+ return ret;
}
void MathNestInset::edit(LCursor & cur, bool left)
{
- cur.push(this);
+ cur.push(*this);
cur.idx() = left ? 0 : cur.lastidx();
cur.pos() = left ? 0 : cur.lastpos();
cur.resetAnchor();
+ //lyxerr << "MathNestInset::edit, cur:\n" << cur << endl;
}
{
int idx_min = 0;
int dist_min = 1000000;
- for (idx_type i = 0; i < nargs(); ++i) {
- int d = cell(i).dist(x, y);
+ for (idx_type i = 0, n = nargs(); i != n; ++i) {
+ int const d = cell(i).dist(x, y);
if (d < dist_min) {
dist_min = d;
idx_min = i;
}
}
MathArray & ar = cell(idx_min);
- cur.push(this);
+ cur.push(*this);
cur.idx() = idx_min;
cur.pos() = ar.x2pos(x - ar.xo());
- lyxerr << "found cell : " << idx_min << " pos: " << cur.pos() << endl;
+ //lyxerr << "found cell : " << idx_min << " pos: " << cur.pos() << endl;
if (dist_min == 0) {
// hit inside cell
for (pos_type i = 0, n = ar.size(); i < n; ++i)
}
-void MathNestInset::lfunMouseRelease(LCursor & cur, FuncRequest const & cmd)
+void MathNestInset::lfunMousePress(LCursor & cur, FuncRequest & cmd)
{
- //lyxerr << "lfunMouseRelease: buttons: " << cmd.button() << endl;
+ //lyxerr << "## lfunMousePress: buttons: " << cmd.button() << endl;
+ if (cmd.button() == mouse_button::button1) {
+ //lyxerr << "## lfunMousePress: setting cursor to: " << cur << endl;
+ cur.bv().mouseSetCursor(cur);
+ }
+
+ if (cmd.button() == mouse_button::button2) {
+ cur.dispatch(FuncRequest(LFUN_PASTESELECTION));
+ }
+}
+
+
+void MathNestInset::lfunMouseMotion(LCursor & cur, FuncRequest & cmd)
+{
+ // only select with button 1
+ if (cmd.button() == mouse_button::button1) {
+ LCursor & bvcur = cur.bv().cursor();
+ if (bvcur.anchor_.hasPart(cur)) {
+ //lyxerr << "## lfunMouseMotion: cursor: " << cur << endl;
+ bvcur.setCursor(cur);
+ bvcur.selection() = true;
+ //lyxerr << "MOTION " << bvcur << endl;
+ }
+ else {
+ cur.undispatched();
+ }
+ }
+}
+
+
+void MathNestInset::lfunMouseRelease(LCursor & cur, FuncRequest & cmd)
+{
+ //lyxerr << "## lfunMouseRelease: buttons: " << cmd.button() << endl;
if (cmd.button() == mouse_button::button1) {
- // try to dispatch to enclosed insets first
//cur.bv().stuffClipboard(cur.grabSelection());
return;
}
if (cmd.button() == mouse_button::button2) {
MathArray ar;
asArray(cur.bv().getClipboard(), ar);
- cur.selClear();
+ cur.clearSelection();
cur.setScreenPos(cmd.x, cmd.y);
cur.insert(ar);
cur.bv().update();
return;
}
- cur.notdispatched();
+ cur.undispatched();
}
-void MathNestInset::lfunMousePress(LCursor & cur, FuncRequest const & cmd)
+bool MathNestInset::interpret(LCursor & cur, char c)
{
- lyxerr << "lfunMousePress: buttons: " << cmd.button() << endl;
- if (cmd.button() == mouse_button::button1) {
- first_x = cmd.x;
- first_y = cmd.y;
- cur.selClear();
- //cur.setScreenPos(cmd.x + xo_, cmd.y + yo_);
- lyxerr << "lfunMousePress: setting cursor to: " << cur << endl;
- cur.bv().cursor() = cur;
+ //lyxerr << "interpret 2: '" << c << "'" << endl;
+ cur.clearTargetX();
+
+ // handle macroMode
+ if (cur.inMacroMode()) {
+ string name = cur.macroName();
+
+ /// are we currently typing '#1' or '#2' or...?
+ if (name == "\\#") {
+ cur.backspace();
+ int n = c - '0';
+ if (n >= 1 && n <= 9)
+ cur.insert(new MathMacroArgument(n));
+ return true;
+ }
+
+ if (isalpha(c)) {
+ cur.activeMacro()->setName(name + c);
+ return true;
+ }
+
+ // handle 'special char' macros
+ if (name == "\\") {
+ // remove the '\\'
+ if (c == '\\') {
+ cur.backspace();
+ if (currentMode() == MathInset::TEXT_MODE)
+ cur.niceInsert(createMathInset("textbackslash"));
+ else
+ cur.niceInsert(createMathInset("backslash"));
+ } else if (c == '{') {
+ cur.backspace();
+ cur.niceInsert(MathAtom(new MathBraceInset));
+ } else if (c == '%') {
+ cur.backspace();
+ cur.niceInsert(MathAtom(new MathCommentInset));
+ } else if (c == '#') {
+ BOOST_ASSERT(cur.activeMacro());
+ cur.activeMacro()->setName(name + c);
+ } else {
+ cur.backspace();
+ cur.niceInsert(createMathInset(string(1, c)));
+ }
+ return true;
+ }
+
+ // leave macro mode and try again if necessary
+ cur.macroModeClose();
+ if (c == '{')
+ cur.niceInsert(MathAtom(new MathBraceInset));
+ else if (c != ' ')
+ interpret(cur, c);
+ return true;
}
- if (cmd.button() == mouse_button::button2) {
- priv_dispatch(cur, FuncRequest(LFUN_PASTESELECTION));
+ // This is annoying as one has to press <space> far too often.
+ // Disable it.
+
+#if 0
+ // leave autocorrect mode if necessary
+ if (autocorrect() && c == ' ') {
+ autocorrect() = false;
+ return true;
+ }
+#endif
+
+ // just clear selection on pressing the space bar
+ if (cur.selection() && c == ' ') {
+ cur.selection() = false;
+ return true;
}
-}
+ selClearOrDel(cur);
-void MathNestInset::lfunMouseMotion(LCursor & cur, FuncRequest const & cmd)
-{
- // only select with button 1
- if (cmd.button() != mouse_button::button1)
- return;
+ if (c == '\\') {
+ //lyxerr << "starting with macro" << endl;
+ cur.insert(MathAtom(new MathUnknownInset("\\", false)));
+ return true;
+ }
- if (abs(cmd.x - first_x) < 2 && abs(cmd.y - first_y) < 2)
- return;
+ if (c == '\n') {
+ if (currentMode() == MathInset::TEXT_MODE)
+ cur.insert(c);
+ return true;
+ }
- first_x = cmd.x;
- first_y = cmd.y;
+ if (c == ' ') {
+ if (currentMode() == MathInset::TEXT_MODE) {
+ // insert spaces in text mode,
+ // 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() != ' ')
+ cur.insert(c);
+ return true;
+ }
+ if (cur.pos() != 0 && cur.prevAtom()->asSpaceInset()) {
+ cur.prevAtom().nucleus()->asSpaceInset()->incSpace();
+ return true;
+ }
+ if (cur.popRight())
+ return true;
+ // if are at the very end, leave the formula
+ return cur.pos() != cur.lastpos();
+ }
- if (!cur.selection())
- cur.selBegin();
+ // These shouldn't work in text mode:
+ if (currentMode() != MathInset::TEXT_MODE) {
+ if (c == '_') {
+ script(cur, false);
+ return true;
+ }
+ if (c == '^') {
+ script(cur, true);
+ return true;
+ }
+ if (c == '~') {
+ cur.niceInsert(createMathInset("sim"));
+ return true;
+ }
+ }
+
+ if (c == '{' || c == '}' || c == '&' || c == '$' || c == '#' || c == '%'
+ || c == '_' || c == '^') {
+ cur.niceInsert(createMathInset(string(1, c)));
+ return true;
+ }
- //cur.setScreenPos(cmd.x + xo_, cmd.y + yo_);
- cur.bv().cursor().cursor_ = cur.cursor_;
- cur.bv().cursor().selection() = true;
- return;
+
+ // try auto-correction
+ //if (autocorrect() && hasPrevAtom() && math_autocorrect(prevAtom(), c))
+ // return true;
+
+ // no special circumstances, so insert the character without any fuss
+ cur.insert(c);
+ cur.autocorrect() = true;
+ return true;
+}
+
+
+bool MathNestInset::script(LCursor & cur, bool up)
+{
+ // Hack to get \^ and \_ working
+ lyxerr << "handling script: up: " << up << endl;
+ if (cur.inMacroMode() && cur.macroName() == "\\") {
+ if (up)
+ cur.niceInsert(createMathInset("mathcircumflex"));
+ else
+ interpret(cur, '_');
+ return true;
+ }
+
+ cur.macroModeClose();
+ string safe = grabAndEraseSelection(cur);
+ if (asScriptInset() && cur.idx() == 0) {
+ // we are in a nucleus of a script inset, move to _our_ script
+ MathScriptInset * inset = asScriptInset();
+ lyxerr << " going to cell " << inset->idxOfScript(up) << endl;
+ inset->ensure(up);
+ cur.idx() = inset->idxOfScript(up);
+ cur.pos() = 0;
+ } else if (cur.pos() != 0 && cur.prevAtom()->asScriptInset()) {
+ --cur.pos();
+ MathScriptInset * inset = cur.nextAtom().nucleus()->asScriptInset();
+ cur.push(*inset);
+ inset->ensure(up);
+ cur.idx() = inset->idxOfScript(up);
+ cur.pos() = cur.lastpos();
+ } else {
+ // convert the thing to our left to a scriptinset or create a new
+ // one if in the very first position of the array
+ if (cur.pos() == 0) {
+ //lyxerr << "new scriptinset" << endl;
+ cur.insert(new MathScriptInset(up));
+ } else {
+ //lyxerr << "converting prev atom " << endl;
+ cur.prevAtom() = MathAtom(new MathScriptInset(cur.prevAtom(), up));
+ }
+ --cur.pos();
+ MathScriptInset * inset = cur.nextAtom().nucleus()->asScriptInset();
+ cur.push(*inset);
+ cur.idx() = 1;
+ cur.pos() = 0;
+ }
+ //lyxerr << "pasting 1: safe:\n" << safe << endl;
+ cur.paste(safe);
+ cur.resetAnchor();
+ //lyxerr << "pasting 2: safe:\n" << safe << endl;
+ return true;
}