#include "Intl.h"
#include "Language.h"
#include "Layout.h"
-#include "Lexer.h"
#include "LyX.h"
#include "LyXAction.h"
#include "lyxfind.h"
#include "support/filetools.h"
#include "support/gettext.h"
#include "support/lassert.h"
+#include "support/Lexer.h"
#include "support/limited_stack.h"
#include "support/lstrings.h"
#include "support/lyxtime.h"
/**
* This breaks a paragraph at the specified position.
* The new paragraph will:
- * - Decrease depth by one (or change layout to default layout) when
- * keep_layout == false
- * - keep current depth and layout when keep_layout == true
+ * - change layout to default layout when keep_layout == false
+ * - keep layout when keep_layout == true
*/
static void breakParagraph(Text & text, pit_type par_offset, pos_type pos,
bool keep_layout)
// remember to set the inset_owner
tmp->setInsetOwner(&par.inInset());
- // without doing that we get a crash when typing <Return> at the
- // end of a paragraph
- tmp->setPlainOrDefaultLayout(bparams.documentClass());
+ tmp->params().depth(par.params().depth());
if (keep_layout) {
tmp->setLayout(par.layout());
tmp->setLabelWidthString(par.params().labelWidthString());
- tmp->params().depth(par.params().depth());
- } else if (par.params().depth() > 0) {
- Paragraph const & hook = pars[text.outerHook(par_offset)];
- tmp->setLayout(hook.layout());
- // not sure the line below is useful
- tmp->setLabelWidthString(par.params().labelWidthString());
- tmp->params().depth(hook.params().depth());
- }
+ } else
+ tmp->setPlainOrDefaultLayout(bparams.documentClass());
bool const isempty = (par.allowEmpty() && par.empty());
if (!cur.paragraph().isPassThru() && owner_->lyxCode() != IPA_CODE &&
cur.real_current_font.fontInfo().family() != TYPEWRITER_FAMILY &&
c == '-' && pos > 0) {
- if (par.getChar(pos - 1) == '-') {
+ pos_type prev_pos = pos - 1;
+ while (prev_pos > 0 && par.isDeleted(prev_pos))
+ --prev_pos;
+ if (!par.isDeleted(prev_pos) && par.getChar(prev_pos) == '-') {
// convert "--" to endash
- par.eraseChar(pos - 1, cur.buffer()->params().track_changes);
+ par.eraseChar(prev_pos, cur.buffer()->params().track_changes);
c = 0x2013;
pos--;
- } else if (par.getChar(pos - 1) == 0x2013) {
+ } else if (!par.isDeleted(prev_pos) && par.getChar(prev_pos) == 0x2013) {
// convert "---" to emdash
par.eraseChar(pos - 1, cur.buffer()->params().track_changes);
c = 0x2014;
Cursor c = cur;
c.selection(false);
c.text()->selectWord(c, WHOLE_WORD);
+ // get selection around anchor too.
+ // FIXME: this cursor is not a proper one. normalAnchor() should
+ // return a DocIterator.
+ Cursor a(cur.bv());
+ a.push_back(cur.normalAnchor());
+ a.text()->selectWord(a, WHOLE_WORD);
// use the correct word boundary, depending on selection direction
- if (cur.top() > cur.normalAnchor())
- cur.pos() = c.selEnd().pos();
- else
- cur.pos() = c.selBegin().pos();
+ if (cur.top() > cur.normalAnchor()) {
+ cur.top() = a.selBegin();
+ cur.resetAnchor();
+ cur.top() = c.selEnd();
+ } else {
+ cur.top() = a.selEnd();
+ cur.resetAnchor();
+ cur.top() = c.selBegin();
+ }
}
cur.resetAnchor();
setCursor(cur, cur.lastpit(), getPar(cur.lastpit()).size());
cur.setSelection();
+ // Remember whether there was something cut that has to be pasted below
+ // (bug #12747)
+ bool const hasCut = cur.selection();
cap::cutSelectionToTemp(cur);
cur.setMark(false);
cur.selHandle(false);
cur.resetAnchor();
cur.text()->selectAll(cur);
cutSelection(cur, false);
- cap::pasteFromTemp(cur, cur.buffer()->errorList("Paste"));
+ // If there was something cut paste it
+ if (hasCut)
+ cap::pasteFromTemp(cur, cur.buffer()->errorList("Paste"));
cur.text()->setCursor(cur, 0, 0);
if (atlastpos && cur.paragraph().isFreeSpacing() && cur.paragraph().empty()) {
// We started from par end, remove extra empty par in free spacing insets
}
-void Text::changeDepth(Cursor const & cur, DEPTH_CHANGE type)
+void Text::changeDepth(Cursor & cur, DEPTH_CHANGE type)
{
LBUFERR(this == cur.text());
pit_type const beg = cur.selBegin().pit();
}
max_depth = par.getMaxDepthAfter();
}
+ cur.setCurrentFont();
// this handles the counter labels, and also fixes up
// depth values for follow-on (child) paragraphs
cur.forceBufferUpdate();
Layout priorlayout;
Cursor c(cur.bv());
c.setCursor(cur.selectionBegin());
- for ( ; c <= cur.selectionEnd() ; ++c.pit()) {
+ pit_type const last_pit = cur.selectionEnd().pit();
+ for ( ; c.pit() <= last_pit ; ++c.pit()) {
Paragraph & par = c.paragraph();
ParagraphParameters params = par.params();
params.read(argument, merge);
Layout priorlayout;
Cursor c(cur.bv());
c.setCursor(cur.selectionBegin());
- for ( ; c < cur.selectionEnd() ; ++c.pit()) {
+ pit_type const last_pit = cur.selectionEnd().pit();
+ for ( ; c.pit() <= last_pit ; ++c.pit()) {
Paragraph & par = c.paragraph();
// Changes to label width string apply to all paragraphs
// with same layout in a sequence.
}
-// this really should just insert the inset and not move the cursor.
-void Text::insertInset(Cursor & cur, Inset * inset)
+// just insert the inset and not move the cursor.
+bool Text::insertInset(Cursor & cur, Inset * inset)
{
LBUFERR(this == cur.text());
LBUFERR(inset);
- cur.paragraph().insertInset(cur.pos(), inset, cur.current_font,
+ return cur.paragraph().insertInset(cur.pos(), inset, cur.current_font,
Change(cur.buffer()->params().track_changes
? Change::INSERTED : Change::UNCHANGED));
}
int pos = from;
while (pos < to && num_spaces > 0) {
Change const & change = par.lookupChange(pos);
- if (change.inserted() && change.currentAuthor()) {
+ if (change.inserted() && !change.currentAuthor()) {
par.eraseChar(pos, trackChanges);
--num_spaces;
--to;
cutSelectionToTemp(cur, pastesel);
/* Move layout information inside the inset if the whole
* paragraph and the inset allows setting layout
- * FIXME: this does not work as expected when change tracking is on
- * However, we do not really know what to do in this case.
* FIXME: figure out a good test in the environment case (see #12251).
*/
if (cur.paragraph().layout().isCommand()
- && cur.paragraph().empty()
+ && (cur.paragraph().empty()
+ || cur.paragraph().isDeleted(0, cur.paragraph().size()))
&& !inset->forcePlainLayout()) {
cur.paragraph().setPlainOrDefaultLayout(bparams.documentClass());
move_layout = true;
}
-void outline(OutlineOp mode, Cursor & cur, Text * text)
+void outline(OutlineOp mode, Cursor & cur, bool local)
{
Buffer & buf = *cur.buffer();
+ Text & text = *cur.text();
pit_type & pit = cur.pit();
- ParagraphList & pars = buf.text().paragraphs();
+ ParagraphList & pars = text.paragraphs();
ParagraphList::iterator const bgn = pars.begin();
// The first paragraph of the area to be copied:
ParagraphList::iterator start = pars.iterator_at(pit);
ParagraphList::iterator const end = pars.end();
depth_type const current_depth = cur.paragraph().params().depth();
- int const thistoclevel = buf.text().getTocLevel(distance(bgn, start));
+ int const thistoclevel = text.getTocLevel(distance(bgn, start));
int toclevel;
// Move out (down) from this section header
if (finish != end)
++finish;
- // Seek the one (on same level) below
- for (; finish != end; ++finish) {
- toclevel = buf.text().getTocLevel(distance(bgn, finish));
- if (toclevel != Layout::NOT_IN_TOC && toclevel <= thistoclevel)
- break;
+ if (!local || (mode != OutlineIn && mode != OutlineOut)) {
+ // Seek the one (on same level) below
+ for (; finish != end; ++finish) {
+ toclevel = text.getTocLevel(distance(bgn, finish));
+ if (toclevel != Layout::NOT_IN_TOC && toclevel <= thistoclevel)
+ break;
+ }
}
switch (mode) {
// Search previous same-level header above
do {
--dest;
- toclevel = buf.text().getTocLevel(distance(bgn, dest));
+ toclevel = text.getTocLevel(distance(bgn, dest));
} while(dest != bgn
&& (toclevel == Layout::NOT_IN_TOC
|| toclevel > thistoclevel));
// Get the parent paragraph (outer in nested context)
pit_type const parent =
before->params().depth() > current_depth
- ? text->depthHook(distance(bgn, before), current_depth)
+ ? text.depthHook(distance(bgn, before), current_depth)
: distance(bgn, before);
// If a environment with same layout preceeds the moved one in the new
// position, and there is no separator yet, insert one.
ParagraphList::iterator dest = next(finish, 1);
// Go further down to find header to insert in front of:
for (; dest != end; ++dest) {
- toclevel = buf.text().getTocLevel(distance(bgn, dest));
+ toclevel = text.getTocLevel(distance(bgn, dest));
if (toclevel != Layout::NOT_IN_TOC
&& toclevel <= thistoclevel)
break;
// Get the parent paragraph (outer in nested context)
pit_type const parent =
before->params().depth() > current_depth
- ? text->depthHook(distance(bgn, before), current_depth)
+ ? text.depthHook(distance(bgn, before), current_depth)
: distance(bgn, before);
// If a environment with same layout preceeds the moved one in the new
// position, and there is no separator yet, insert one.
ParagraphList::iterator cstart = start;
bool strucchange = false;
for (; cstart != finish; ++cstart) {
- toclevel = buf.text().getTocLevel(distance(bgn, cstart));
+ toclevel = text.getTocLevel(distance(bgn, cstart));
if (toclevel == Layout::NOT_IN_TOC)
continue;
DocumentClass const & tc = buf.params().documentClass();
- int const newtoclevel =
- (mode == OutlineIn ? toclevel + 1 : toclevel - 1);
+ int newtoclevel = -1;
+ if (mode == OutlineIn) {
+ if (toclevel == -1 && tc.getTOCLayout().toclevel > 0)
+ // we are at part but don't have a chapter
+ newtoclevel = tc.getTOCLayout().toclevel;
+ else
+ newtoclevel = toclevel + 1;
+ } else {
+ if (tc.getTOCLayout().toclevel == toclevel && tc.min_toclevel() < toclevel)
+ // we are at highest level, but there is still part
+ newtoclevel = tc.min_toclevel();
+ else
+ newtoclevel = toclevel - 1;
+ }
bool found = false;
for (auto const & lay : tc) {
pit_type const len = distance(start, finish);
buf.undo().recordUndo(cur, pit, pit + len - 1);
for (; start != finish; ++start) {
- toclevel = buf.text().getTocLevel(distance(bgn, start));
+ toclevel = text.getTocLevel(distance(bgn, start));
if (toclevel == Layout::NOT_IN_TOC)
continue;
DocumentClass const & tc = buf.params().documentClass();
- int const newtoclevel =
- (mode == OutlineIn ? toclevel + 1 : toclevel - 1);
+ int newtoclevel = -1;
+ if (mode == OutlineIn) {
+ if (toclevel == -1 && tc.getTOCLayout().toclevel > 0)
+ // we are at part but don't have a chapter
+ newtoclevel = tc.getTOCLayout().toclevel;
+ else
+ newtoclevel = toclevel + 1;
+ } else {
+ if (tc.getTOCLayout().toclevel == toclevel && tc.min_toclevel() < toclevel)
+ // we are at highest level, but there is still part
+ newtoclevel = tc.min_toclevel();
+ else
+ newtoclevel = toclevel - 1;
+ }
for (auto const & lay : tc) {
if (lay.toclevel == newtoclevel
if (!atFirstOrLastRow) {
needsUpdate |= cur.selHandle(select);
- cur.upDownInText(up, needsUpdate);
+ needsUpdate |= cur.upDownInText(up);
needsUpdate |= cur.beforeDispatchCursor().inMathed();
} else {
pos_type newpos = up ? 0 : cur.lastpos();
needsUpdate |= cur.selHandle(select);
// we do not reset the targetx of the cursor
cur.pos() = newpos;
- needsUpdate |= bv->checkDepm(cur, bv->cursor());
- cur.updateTextTargetOffset();
- if (needsUpdate)
+ if (bv->checkDepm(cur, bv->cursor())) {
+ needsUpdate = true;
cur.forceBufferUpdate();
+ }
+ cur.updateTextTargetOffset();
break;
}
// the selection right now, but wait for the next dispatch.
if (select)
needsUpdate |= cur.selHandle(select);
- cur.upDownInText(up, needsUpdate);
+ needsUpdate |= cur.upDownInText(up);
cur.undispatched();
}
if (theClipboard().isInternal())
pasteFromStack(cur, bv->buffer().errorList("Paste"), 0);
else if (theClipboard().hasTextContents()) {
- if (pasteClipboardText(cur, bv->buffer().errorList("Paste"),
- !cur.paragraph().parbreakIsNewline(),
+ if (pasteClipboardText(cur, bv->buffer().errorList("Paste"), 0,
Clipboard::AnyTextType))
tryGraphics = false;
}
bvcur.setMark(false);
switch (cmd.button()) {
case mouse_button::button1:
+ bvcur.setClickPos(cmd.x(), cmd.y());
if (!bvcur.selection())
// Set the cursor
bvcur.resetAnchor();
case LFUN_MOUSE_RELEASE:
switch (cmd.button()) {
case mouse_button::button1:
+ // unregister last mouse press position
+ cur.bv().cursor().setClickPos(-1, -1);
// Cursor was set at LFUN_MOUSE_PRESS or LFUN_MOUSE_MOTION time.
// If there is a new selection, update persistent selection;
// otherwise, single click does not clear persistent selection
// invalid after regex_match returns, since it is then
// being given a temporary object. (Thanks to Georg for
// figuring that out.)
- regex const link_re("^(([a-z]+):|www\\.).*");
+ regex const link_re("^(([a-z]+):|www\\.).*");
smatch matches;
string const c = to_utf8(lowercase(content));
from_utf8(N_(" not known")));
}
if (doInsertInset(cur, this, cmd, false, true))
- cur.posForward();
+ // move inside
+ (void) checkAndActivateInset(cur, true);
break;
}
}
case LFUN_NOMENCL_PRINT:
- case LFUN_NEWPAGE_INSERT:
// do nothing fancy
doInsertInset(cur, this, cmd, false, false);
cur.posForward();
break;
+ case LFUN_NEWPAGE_INSERT: {
+ // When we are in a heading, put the page break in a standard
+ // paragraph before the heading (if cur.pos() == 0) or after
+ // (if cur.pos() == cur.lastpos())
+ if (cur.text()->getTocLevel(cur.pit()) != Layout::NOT_IN_TOC) {
+ lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_BREAK));
+ DocumentClass const & tc = bv->buffer().params().documentClass();
+ lyx::dispatch(FuncRequest(LFUN_LAYOUT, from_ascii("\"") + tc.plainLayout().name()
+ + from_ascii("\" ignoreautonests")));
+ }
+ // do nothing fancy
+ doInsertInset(cur, this, cmd, false, false);
+ cur.posForward();
+ break;
+ }
+
case LFUN_SEPARATOR_INSERT: {
doInsertInset(cur, this, cmd, false, false);
cur.posForward();
InsetMathMacroTemplate * inset = new InsetMathMacroTemplate(cur.buffer(),
from_utf8(token(s, ' ', 0)), nargs, false, type);
inset->setBuffer(bv->buffer());
- insertInset(cur, inset);
-
- // enter macro inset and select the name
- cur.push(*inset);
- cur.top().pos() = cur.top().lastpos();
- cur.resetAnchor();
- cur.selection(true);
- cur.top().pos() = 0;
+ if (insertInset(cur, inset)) {
+ // If insertion is successful, enter macro inset and select the name
+ cur.push(*inset);
+ cur.top().pos() = cur.top().lastpos();
+ cur.resetAnchor();
+ cur.selection(true);
+ cur.top().pos() = 0;
+ } else
+ delete inset;
}
break;
// Argument?
if (!arg.empty()) {
if (isStrUnsignedInt(arg)) {
- num = convert<uint>(arg);
+ num = convert<unsigned int>(arg);
if (num >= freeFonts.size()) {
cur.message(_("Invalid argument (number exceeds stack size)!"));
break;
toggleAndShow(cur, this, font, toggleall);
cur.message(bformat(_("Text properties applied: %1$s"), props));
} else
- LYXERR0("Invalid argument of textstyle-update");
+ cur.message(_("Invalid argument of textstyle-update"));
break;
}
case LFUN_OUTLINE_UP: {
pos_type const opos = cur.pos();
- outline(OutlineUp, cur, this);
+ outline(OutlineUp, cur, false);
setCursor(cur, cur.pit(), opos);
cur.forceBufferUpdate();
needsUpdate = true;
case LFUN_OUTLINE_DOWN: {
pos_type const opos = cur.pos();
- outline(OutlineDown, cur, this);
+ outline(OutlineDown, cur, false);
setCursor(cur, cur.pit(), opos);
cur.forceBufferUpdate();
needsUpdate = true;
}
case LFUN_OUTLINE_IN:
- outline(OutlineIn, cur, this);
+ outline(OutlineIn, cur, cmd.getArg(0) == "local");
cur.forceBufferUpdate();
needsUpdate = true;
break;
case LFUN_OUTLINE_OUT:
- outline(OutlineOut, cur, this);
+ outline(OutlineOut, cur, cmd.getArg(0) == "local");
cur.forceBufferUpdate();
needsUpdate = true;
break;
}
}
- // FIXME: The cursor flag is reset two lines below
- // so we need to check here if some of the LFUN did touch that.
- // for now only Text::erase() and Text::backspace() do that.
- // The plan is to verify all the LFUNs and then to remove this
- // singleParUpdate boolean altogether.
- if (cur.result().screenUpdate() & Update::Force) {
- singleParUpdate = false;
- needsUpdate = true;
- }
-
// FIXME: the following code should go in favor of fine grained
// update flag treatment.
- if (singleParUpdate) {
+ if (needsUpdate || cur.result().screenUpdate() & Update::Force)
+ cur.screenUpdateFlags(Update::Force | Update::FitCursor);
+ else if (singleParUpdate || cur.result().screenUpdate() & Update::SinglePar) {
// Inserting characters does not change par height in general. So, try
// to update _only_ this paragraph. BufferView will detect if a full
// metrics update is needed anyway.
cur.screenUpdateFlags(Update::SinglePar | Update::FitCursor);
return;
}
- if (!needsUpdate
- && &oldTopSlice.inset() == &cur.inset()
- && oldTopSlice.idx() == cur.idx()
- && !oldSelection // oldSelection is a backup of cur.selection() at the beginning of the function.
- && !cur.selection())
- // FIXME: it would be better if we could just do this
- //
- //if (cur.result().update() != Update::FitCursor)
- // cur.noScreenUpdate();
- //
- // But some LFUNs do not set Update::FitCursor when needed, so we
- // do it for all. This is not very harmfull as FitCursor will provoke
- // a full redraw only if needed but still, a proper review of all LFUN
- // should be done and this needsUpdate boolean can then be removed.
- cur.screenUpdateFlags(Update::FitCursor);
- else
- cur.screenUpdateFlags(Update::Force | Update::FitCursor);
+ else {
+ // oldSelection is a backup of cur.selection() at the beginning of the function.
+ if (!oldSelection && !cur.selection())
+ // FIXME: it would be better if we could just do this
+ //
+ //if (cur.result().update() != Update::FitCursor)
+ // cur.noScreenUpdate();
+ //
+ // But some LFUNs do not set Update::FitCursor when needed, so we
+ // do it for all. This is not very harmfull as FitCursor will provoke
+ // a full redraw only if needed but still, a proper review of all LFUN
+ // should be done and this needsUpdate boolean can then be removed.
+ cur.screenUpdateFlags(Update::FitCursor);
+ else
+ cur.screenUpdateFlags(Update::ForceDraw | Update::FitCursor);
+ }
}
s = from_ascii("Flex:") + s;
if (!cur.buffer()->params().documentClass().hasInsetLayout(s))
enable = false;
+ else if (!cur.paragraph().allowedInContext(cur, cur.buffer()->params().documentClass().insetLayout(s)))
+ enable = false;
else {
InsetLyXType ilt =
cur.buffer()->params().documentClass().insetLayout(s).lyxtype();
case LFUN_MATH_BIGDELIM:
case LFUN_MATH_DISPLAY:
case LFUN_MATH_MODE:
- case LFUN_MATH_MACRO:
case LFUN_MATH_SUBSCRIPT:
case LFUN_MATH_SUPERSCRIPT:
code = MATH_HULL_CODE;
break;
+ case LFUN_MATH_MACRO:
+ code = MATHMACRO_CODE;
+ break;
+
case LFUN_REGEXP_MODE:
code = MATH_HULL_CODE;
enable = cur.buffer()->isInternal() && !cur.inRegexped();
case LFUN_OUTLINE_UP:
case LFUN_OUTLINE_DOWN:
+ enable = cur.text()->getTocLevel(cur.pit()) != Layout::NOT_IN_TOC;
+ break;
case LFUN_OUTLINE_IN:
+ enable = cur.text()->getTocLevel(cur.pit()) != Layout::NOT_IN_TOC
+ && cur.text()->getTocLevel(cur.pit()) !=
+ cur.buffer()->params().documentClass().max_toclevel();
+ break;
case LFUN_OUTLINE_OUT:
- // FIXME: LyX is not ready for outlining within inset.
- enable = isMainText()
- && cur.buffer()->text().getTocLevel(cur.pit()) != Layout::NOT_IN_TOC;
+ enable = cur.text()->getTocLevel(cur.pit()) != Layout::NOT_IN_TOC
+ && cur.text()->getTocLevel(cur.pit()) !=
+ cur.buffer()->params().documentClass().min_toclevel();
break;
case LFUN_NEWLINE_INSERT:
}
case LFUN_NEWPAGE_INSERT:
- // not allowed in description items
+ // not allowed in description items and in the midst of sections
code = NEWPAGE_CODE;
- enable = !inDescriptionItem(cur);
+ enable = !inDescriptionItem(cur)
+ && (cur.text()->getTocLevel(cur.pit()) == Layout::NOT_IN_TOC
+ || cur.pos() == 0 || cur.pos() == cur.lastpos());
break;
case LFUN_LANGUAGE: