2003-08-23 00:17:00 +00:00
|
|
|
/**
|
2007-11-29 21:10:35 +00:00
|
|
|
* \file src/Text.cpp
|
2003-08-23 00:17:00 +00:00
|
|
|
* This file is part of LyX, the document processor.
|
|
|
|
* Licence details can be found in the file COPYING.
|
2002-03-21 17:27:08 +00:00
|
|
|
*
|
2003-08-23 00:17:00 +00:00
|
|
|
* \author Asger Alstrup
|
2008-11-14 15:58:50 +00:00
|
|
|
* \author Lars Gullik Bjønnes
|
2007-06-08 14:24:57 +00:00
|
|
|
* \author Dov Feldstern
|
2003-08-23 00:17:00 +00:00
|
|
|
* \author Jean-Marc Lasgouttes
|
|
|
|
* \author John Levon
|
2008-11-14 15:58:50 +00:00
|
|
|
* \author André Pönitz
|
2007-06-04 18:41:56 +00:00
|
|
|
* \author Stefan Schimanski
|
2003-08-23 00:17:00 +00:00
|
|
|
* \author Dekel Tsur
|
2008-11-14 15:58:50 +00:00
|
|
|
* \author Jürgen Vigna
|
2002-03-21 17:27:08 +00:00
|
|
|
*
|
2003-08-23 00:17:00 +00:00
|
|
|
* Full author contact details are available in file CREDITS.
|
|
|
|
*/
|
1999-09-27 18:44:28 +00:00
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
2007-04-29 23:33:02 +00:00
|
|
|
#include "Text.h"
|
2003-09-06 23:36:02 +00:00
|
|
|
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "Author.h"
|
|
|
|
#include "Buffer.h"
|
2005-02-25 11:55:36 +00:00
|
|
|
#include "buffer_funcs.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "BufferParams.h"
|
2003-09-06 23:36:02 +00:00
|
|
|
#include "BufferView.h"
|
2007-10-18 15:29:51 +00:00
|
|
|
#include "Changes.h"
|
2008-03-15 12:22:28 +00:00
|
|
|
#include "CompletionList.h"
|
2007-04-26 14:56:30 +00:00
|
|
|
#include "Cursor.h"
|
2004-03-25 09:16:36 +00:00
|
|
|
#include "CutAndPaste.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "DispatchResult.h"
|
2014-03-09 09:30:20 +00:00
|
|
|
#include "Encoding.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "ErrorList.h"
|
|
|
|
#include "FuncRequest.h"
|
2004-03-25 09:16:36 +00:00
|
|
|
#include "factory.h"
|
2009-08-09 17:30:41 +00:00
|
|
|
#include "InsetList.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "Language.h"
|
2009-08-09 17:30:41 +00:00
|
|
|
#include "Layout.h"
|
2007-04-28 12:58:49 +00:00
|
|
|
#include "Length.h"
|
2007-04-26 11:30:54 +00:00
|
|
|
#include "Lexer.h"
|
2009-05-01 17:06:36 +00:00
|
|
|
#include "lyxfind.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "LyXRC.h"
|
|
|
|
#include "Paragraph.h"
|
2001-06-25 00:06:33 +00:00
|
|
|
#include "ParagraphParameters.h"
|
2008-03-15 12:22:28 +00:00
|
|
|
#include "ParIterator.h"
|
2007-11-07 23:34:45 +00:00
|
|
|
#include "TextClass.h"
|
2007-10-11 09:59:01 +00:00
|
|
|
#include "TextMetrics.h"
|
2002-08-13 14:40:38 +00:00
|
|
|
#include "WordLangTuple.h"
|
2008-03-15 12:22:28 +00:00
|
|
|
#include "WordList.h"
|
2003-09-06 23:36:02 +00:00
|
|
|
|
2007-04-25 01:24:38 +00:00
|
|
|
#include "insets/InsetText.h"
|
|
|
|
#include "insets/InsetBibitem.h"
|
|
|
|
#include "insets/InsetCaption.h"
|
|
|
|
#include "insets/InsetNewline.h"
|
2007-11-23 02:10:00 +00:00
|
|
|
#include "insets/InsetNewpage.h"
|
2010-06-04 22:44:58 +00:00
|
|
|
#include "insets/InsetArgument.h"
|
2012-08-23 15:42:53 +00:00
|
|
|
#include "insets/InsetIPAMacro.h"
|
2007-04-25 01:24:38 +00:00
|
|
|
#include "insets/InsetSpace.h"
|
|
|
|
#include "insets/InsetSpecialChar.h"
|
|
|
|
#include "insets/InsetTabular.h"
|
2001-11-15 00:39:11 +00:00
|
|
|
|
2012-09-19 08:20:19 +00:00
|
|
|
#include "support/convert.h"
|
2007-11-29 21:10:35 +00:00
|
|
|
#include "support/debug.h"
|
2007-11-01 22:17:22 +00:00
|
|
|
#include "support/docstream.h"
|
2007-11-29 21:10:35 +00:00
|
|
|
#include "support/gettext.h"
|
2008-12-21 01:54:04 +00:00
|
|
|
#include "support/lassert.h"
|
2001-11-15 00:39:11 +00:00
|
|
|
#include "support/lstrings.h"
|
2015-05-15 22:05:23 +00:00
|
|
|
#include "support/lyxalgo.h"
|
2016-05-03 19:40:28 +00:00
|
|
|
#include "support/lyxtime.h"
|
2003-09-06 23:36:02 +00:00
|
|
|
#include "support/textutils.h"
|
2016-06-02 20:58:52 +00:00
|
|
|
#include "support/unique_ptr.h"
|
2004-07-24 10:55:30 +00:00
|
|
|
|
|
|
|
#include <sstream>
|
2001-11-15 00:39:11 +00:00
|
|
|
|
2011-11-20 19:44:08 +00:00
|
|
|
|
2007-12-12 10:16:00 +00:00
|
|
|
using namespace std;
|
2007-12-12 18:57:56 +00:00
|
|
|
using namespace lyx::support;
|
2003-09-09 22:13:45 +00:00
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
namespace lyx {
|
2003-06-30 23:56:22 +00:00
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
using cap::cutSelection;
|
|
|
|
using cap::pasteParagraphList;
|
|
|
|
|
2009-08-09 17:30:41 +00:00
|
|
|
static bool moveItem(Paragraph & fromPar, pos_type fromPos,
|
|
|
|
Paragraph & toPar, pos_type toPos, BufferParams const & params)
|
|
|
|
{
|
|
|
|
// Note: moveItem() does not honour change tracking!
|
|
|
|
// Therefore, it should only be used for breaking and merging paragraphs
|
|
|
|
|
|
|
|
// We need a copy here because the character at fromPos is going to be erased.
|
2015-02-28 22:41:20 +00:00
|
|
|
Font const tmpFont = fromPar.getFontSettings(params, fromPos);
|
|
|
|
Change const tmpChange = fromPar.lookupChange(fromPos);
|
2009-08-09 17:30:41 +00:00
|
|
|
|
|
|
|
if (Inset * tmpInset = fromPar.getInset(fromPos)) {
|
|
|
|
fromPar.releaseInset(fromPos);
|
|
|
|
// The inset is not in fromPar any more.
|
|
|
|
if (!toPar.insertInset(toPos, tmpInset, tmpFont, tmpChange)) {
|
|
|
|
delete tmpInset;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
char_type const tmpChar = fromPar.getChar(fromPos);
|
|
|
|
fromPar.eraseChar(fromPos, false);
|
|
|
|
toPar.insertChar(toPos, tmpChar, tmpFont, tmpChange);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void breakParagraphConservative(BufferParams const & bparams,
|
2016-02-29 12:29:06 +00:00
|
|
|
ParagraphList & pars, pit_type pit, pos_type pos)
|
2009-08-09 17:30:41 +00:00
|
|
|
{
|
|
|
|
// create a new paragraph
|
2016-02-29 12:29:06 +00:00
|
|
|
Paragraph & tmp = *pars.insert(lyx::next(pars.begin(), pit + 1),
|
2009-08-09 17:30:41 +00:00
|
|
|
Paragraph());
|
2016-02-29 12:29:06 +00:00
|
|
|
Paragraph & par = pars[pit];
|
2009-08-09 17:30:41 +00:00
|
|
|
|
|
|
|
tmp.setInsetOwner(&par.inInset());
|
|
|
|
tmp.makeSameLayout(par);
|
|
|
|
|
2013-04-25 21:27:10 +00:00
|
|
|
LASSERT(pos <= par.size(), return);
|
2009-08-09 17:30:41 +00:00
|
|
|
|
|
|
|
if (pos < par.size()) {
|
|
|
|
// move everything behind the break position to the new paragraph
|
|
|
|
pos_type pos_end = par.size() - 1;
|
|
|
|
|
|
|
|
for (pos_type i = pos, j = 0; i <= pos_end; ++i) {
|
|
|
|
if (moveItem(par, pos, tmp, j, bparams)) {
|
|
|
|
++j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Move over the end-of-par change information
|
|
|
|
tmp.setChange(tmp.size(), par.lookupChange(par.size()));
|
2014-03-29 22:52:36 +00:00
|
|
|
par.setChange(par.size(), Change(bparams.track_changes ?
|
2009-08-09 17:30:41 +00:00
|
|
|
Change::INSERTED : Change::UNCHANGED));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void mergeParagraph(BufferParams const & bparams,
|
|
|
|
ParagraphList & pars, pit_type par_offset)
|
|
|
|
{
|
|
|
|
Paragraph & next = pars[par_offset + 1];
|
|
|
|
Paragraph & par = pars[par_offset];
|
|
|
|
|
|
|
|
pos_type pos_end = next.size() - 1;
|
|
|
|
pos_type pos_insert = par.size();
|
|
|
|
|
|
|
|
// the imaginary end-of-paragraph character (at par.size()) has to be
|
|
|
|
// marked as unmodified. Otherwise, its change is adopted by the first
|
|
|
|
// character of the next paragraph.
|
|
|
|
if (par.isChanged(par.size())) {
|
|
|
|
LYXERR(Debug::CHANGES,
|
|
|
|
"merging par with inserted/deleted end-of-par character");
|
|
|
|
par.setChange(par.size(), Change(Change::UNCHANGED));
|
|
|
|
}
|
|
|
|
|
|
|
|
Change change = next.lookupChange(next.size());
|
|
|
|
|
|
|
|
// move the content of the second paragraph to the end of the first one
|
|
|
|
for (pos_type i = 0, j = pos_insert; i <= pos_end; ++i) {
|
|
|
|
if (moveItem(next, 0, par, j, bparams)) {
|
|
|
|
++j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// move the change of the end-of-paragraph character
|
|
|
|
par.setChange(par.size(), change);
|
|
|
|
|
2015-05-15 22:05:23 +00:00
|
|
|
pars.erase(lyx::next(pars.begin(), par_offset + 1));
|
2009-08-09 17:30:41 +00:00
|
|
|
}
|
|
|
|
|
2010-01-05 13:16:55 +00:00
|
|
|
|
|
|
|
Text::Text(InsetText * owner, bool use_default_layout)
|
2015-03-30 17:45:01 +00:00
|
|
|
: owner_(owner)
|
2010-01-05 13:16:55 +00:00
|
|
|
{
|
|
|
|
pars_.push_back(Paragraph());
|
|
|
|
Paragraph & par = pars_.back();
|
|
|
|
par.setInsetOwner(owner);
|
|
|
|
DocumentClass const & dc = owner->buffer().params().documentClass();
|
|
|
|
if (use_default_layout)
|
|
|
|
par.setDefaultLayout(dc);
|
|
|
|
else
|
|
|
|
par.setPlainLayout(dc);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Text::Text(InsetText * owner, Text const & text)
|
2016-06-06 19:55:39 +00:00
|
|
|
: owner_(owner), pars_(text.pars_)
|
2010-01-05 13:16:55 +00:00
|
|
|
{
|
|
|
|
ParagraphList::iterator const end = pars_.end();
|
|
|
|
ParagraphList::iterator it = pars_.begin();
|
2010-01-06 09:12:49 +00:00
|
|
|
for (; it != end; ++it)
|
2010-01-05 13:16:55 +00:00
|
|
|
it->setInsetOwner(owner);
|
|
|
|
}
|
|
|
|
|
2009-08-09 17:30:41 +00:00
|
|
|
|
2009-08-09 18:35:39 +00:00
|
|
|
pit_type Text::depthHook(pit_type pit, depth_type depth) const
|
2009-08-09 17:30:41 +00:00
|
|
|
{
|
|
|
|
pit_type newpit = pit;
|
|
|
|
|
|
|
|
if (newpit != 0)
|
|
|
|
--newpit;
|
|
|
|
|
2009-08-09 18:35:39 +00:00
|
|
|
while (newpit != 0 && pars_[newpit].getDepth() > depth)
|
2009-08-09 17:30:41 +00:00
|
|
|
--newpit;
|
|
|
|
|
2009-08-09 18:35:39 +00:00
|
|
|
if (pars_[newpit].getDepth() > depth)
|
2009-08-09 17:30:41 +00:00
|
|
|
return pit;
|
|
|
|
|
|
|
|
return newpit;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-09 18:35:39 +00:00
|
|
|
pit_type Text::outerHook(pit_type par_offset) const
|
2009-08-09 17:30:41 +00:00
|
|
|
{
|
2009-08-09 18:35:39 +00:00
|
|
|
Paragraph const & par = pars_[par_offset];
|
2009-08-09 17:30:41 +00:00
|
|
|
|
|
|
|
if (par.getDepth() == 0)
|
2009-08-09 18:35:39 +00:00
|
|
|
return pars_.size();
|
|
|
|
return depthHook(par_offset, depth_type(par.getDepth() - 1));
|
2009-08-09 17:30:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-09 18:35:39 +00:00
|
|
|
bool Text::isFirstInSequence(pit_type par_offset) const
|
2009-08-09 17:30:41 +00:00
|
|
|
{
|
2009-08-09 18:35:39 +00:00
|
|
|
Paragraph const & par = pars_[par_offset];
|
2009-08-09 17:30:41 +00:00
|
|
|
|
2009-08-09 18:35:39 +00:00
|
|
|
pit_type dhook_offset = depthHook(par_offset, par.getDepth());
|
2009-08-09 17:30:41 +00:00
|
|
|
|
|
|
|
if (dhook_offset == par_offset)
|
|
|
|
return true;
|
|
|
|
|
2009-08-09 18:35:39 +00:00
|
|
|
Paragraph const & dhook = pars_[dhook_offset];
|
2009-08-09 17:30:41 +00:00
|
|
|
|
|
|
|
return dhook.layout() != par.layout()
|
|
|
|
|| dhook.getDepth() != par.getDepth();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-01-08 20:57:02 +00:00
|
|
|
pit_type Text::lastInSequence(pit_type pit) const
|
|
|
|
{
|
|
|
|
depth_type const depth = pars_[pit].getDepth();
|
|
|
|
pit_type newpit = pit;
|
|
|
|
|
|
|
|
while (size_t(newpit + 1) < pars_.size() &&
|
|
|
|
(pars_[newpit + 1].getDepth() > depth ||
|
|
|
|
(pars_[newpit + 1].getDepth() == depth &&
|
|
|
|
pars_[newpit + 1].layout() == pars_[pit].layout())))
|
|
|
|
++newpit;
|
|
|
|
|
|
|
|
return newpit;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-12-15 15:47:57 +00:00
|
|
|
int Text::getTocLevel(pit_type par_offset) const
|
|
|
|
{
|
|
|
|
Paragraph const & par = pars_[par_offset];
|
|
|
|
|
|
|
|
if (par.layout().isEnvironment() && !isFirstInSequence(par_offset))
|
|
|
|
return Layout::NOT_IN_TOC;
|
|
|
|
|
|
|
|
return par.layout().toclevel;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-09 18:35:39 +00:00
|
|
|
Font const Text::outerFont(pit_type par_offset) const
|
2009-08-09 17:30:41 +00:00
|
|
|
{
|
2009-08-09 18:35:39 +00:00
|
|
|
depth_type par_depth = pars_[par_offset].getDepth();
|
2009-08-09 17:30:41 +00:00
|
|
|
FontInfo tmpfont = inherit_font;
|
2012-05-14 12:21:49 +00:00
|
|
|
depth_type prev_par_depth = 0;
|
2009-08-09 17:30:41 +00:00
|
|
|
// Resolve against environment font information
|
2009-08-09 18:35:39 +00:00
|
|
|
while (par_offset != pit_type(pars_.size())
|
2012-05-14 12:21:49 +00:00
|
|
|
&& par_depth != prev_par_depth
|
2009-08-09 17:30:41 +00:00
|
|
|
&& par_depth
|
|
|
|
&& !tmpfont.resolved()) {
|
2012-05-14 12:21:49 +00:00
|
|
|
prev_par_depth = par_depth;
|
2009-08-09 18:35:39 +00:00
|
|
|
par_offset = outerHook(par_offset);
|
|
|
|
if (par_offset != pit_type(pars_.size())) {
|
|
|
|
tmpfont.realize(pars_[par_offset].layout().font);
|
|
|
|
par_depth = pars_[par_offset].getDepth();
|
2009-08-09 17:30:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Font(tmpfont);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-06-01 16:13:54 +00:00
|
|
|
static void acceptOrRejectChanges(ParagraphList & pars,
|
|
|
|
BufferParams const & bparams, Text::ChangeOp op)
|
2009-08-09 17:30:41 +00:00
|
|
|
{
|
|
|
|
pit_type pars_size = static_cast<pit_type>(pars.size());
|
|
|
|
|
2010-06-01 16:13:54 +00:00
|
|
|
// first, accept or reject changes within each individual
|
|
|
|
// paragraph (do not consider end-of-par)
|
2009-08-09 17:30:41 +00:00
|
|
|
for (pit_type pit = 0; pit < pars_size; ++pit) {
|
2010-06-01 16:13:54 +00:00
|
|
|
// prevent assertion failure
|
|
|
|
if (!pars[pit].empty()) {
|
|
|
|
if (op == Text::ACCEPT)
|
|
|
|
pars[pit].acceptChanges(0, pars[pit].size());
|
|
|
|
else
|
|
|
|
pars[pit].rejectChanges(0, pars[pit].size());
|
|
|
|
}
|
2009-08-09 17:30:41 +00:00
|
|
|
}
|
|
|
|
|
2010-06-01 16:13:54 +00:00
|
|
|
// next, accept or reject imaginary end-of-par characters
|
2009-08-09 17:30:41 +00:00
|
|
|
for (pit_type pit = 0; pit < pars_size; ++pit) {
|
|
|
|
pos_type pos = pars[pit].size();
|
2010-06-01 16:13:54 +00:00
|
|
|
if (pars[pit].isChanged(pos)) {
|
|
|
|
// keep the end-of-par char if it is inserted and accepted
|
|
|
|
// or when it is deleted and rejected.
|
|
|
|
if (pars[pit].isInserted(pos) == (op == Text::ACCEPT)) {
|
2009-08-09 17:30:41 +00:00
|
|
|
pars[pit].setChange(pos, Change(Change::UNCHANGED));
|
|
|
|
} else {
|
2010-06-01 16:13:54 +00:00
|
|
|
if (pit == pars_size - 1) {
|
|
|
|
// we cannot remove a par break at the end of the last
|
|
|
|
// paragraph; instead, we mark it unchanged
|
|
|
|
pars[pit].setChange(pos, Change(Change::UNCHANGED));
|
|
|
|
} else {
|
|
|
|
mergeParagraph(bparams, pars, pit);
|
|
|
|
--pit;
|
|
|
|
--pars_size;
|
|
|
|
}
|
2009-08-09 17:30:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-01 16:13:54 +00:00
|
|
|
|
|
|
|
void acceptChanges(ParagraphList & pars, BufferParams const & bparams)
|
|
|
|
{
|
|
|
|
acceptOrRejectChanges(pars, bparams, Text::ACCEPT);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void rejectChanges(ParagraphList & pars, BufferParams const & bparams)
|
|
|
|
{
|
|
|
|
acceptOrRejectChanges(pars, bparams, Text::REJECT);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-09 16:19:43 +00:00
|
|
|
InsetText const & Text::inset() const
|
|
|
|
{
|
|
|
|
return *owner_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-11-20 19:44:08 +00:00
|
|
|
|
2009-08-09 15:29:34 +00:00
|
|
|
void Text::readParToken(Paragraph & par, Lexer & lex,
|
2007-04-29 18:17:15 +00:00
|
|
|
string const & token, Font & font, Change & change, ErrorList & errorList)
|
2004-03-25 09:16:36 +00:00
|
|
|
{
|
2009-11-08 15:53:21 +00:00
|
|
|
Buffer * buf = const_cast<Buffer *>(&owner_->buffer());
|
2015-11-11 21:31:05 +00:00
|
|
|
BufferParams & bp = buf->params();
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
|
|
if (token[0] != '\\') {
|
2006-10-21 00:16:43 +00:00
|
|
|
docstring dstr = lex.getDocString();
|
2007-10-22 13:09:16 +00:00
|
|
|
par.appendString(dstr, font, change);
|
|
|
|
|
2004-03-25 09:16:36 +00:00
|
|
|
} else if (token == "\\begin_layout") {
|
|
|
|
lex.eatLine();
|
2007-07-11 13:39:08 +00:00
|
|
|
docstring layoutname = lex.getDocString();
|
2004-03-25 09:16:36 +00:00
|
|
|
|
2007-10-28 18:51:54 +00:00
|
|
|
font = Font(inherit_font, bp.language);
|
2006-10-11 20:01:32 +00:00
|
|
|
change = Change(Change::UNCHANGED);
|
2004-03-25 09:16:36 +00:00
|
|
|
|
2008-02-28 01:42:02 +00:00
|
|
|
DocumentClass const & tclass = bp.documentClass();
|
2004-03-25 09:16:36 +00:00
|
|
|
|
Fix bug 4037 and related problems. The patch has been cleaned up a bit
from the one posted to the list.
The basic idea has two parts. First, we hard code an "empty layout"
(called PlainLayout, for want of a better name) in TextClass and read it
before doing anything else. It can therefore be customized by classes,
if they want---say, to make it left-aligned. Second, InsetText's are
divided into three types: (i) normal ones, that use the "default" layout
defined by the text class; (ii) highly restrictive ones, such as ERT and
(not quite an inset) table cells, which demand the empty layout; (iii)
middling ones, which default to an empty layout and use the empty layout
in place of the default. (This is so we don't get the same problem we
had with ERT in e.g. footnotes.) The type of inset is signaled by new
methods InsetText::forceEmptyLayout() and InsetText::useEmptyLayout().
(The latter might better be called: useEmptyLayoutInsteadOfDefault(),
but that's silly.) The old InsetText::forceDefaultParagraphs() has been
split into these, plus a new method InsetText::allowParagraphCustomization().
A lot of the changes just adapt to this change.
The other big change is in GuiToolbar: We want to show LyXDefault and
the "default" layout only when they're active.
There are a handful of places where I'm not entirely sure whether we
should be using forceEmptyLayout or !allowParagraphCustomization() or
both. The InsetCaption is one of these. These places, and some others,
are marked with FIXMEs, so I'd appreciate it if people would search
through the patch and let me know whether these need changing. If they
don't, the FIXMEs can be deleted.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22966 a592a061-630c-0410-9148-cb99ea01b6c8
2008-02-12 17:31:07 +00:00
|
|
|
if (layoutname.empty())
|
2004-03-25 09:16:36 +00:00
|
|
|
layoutname = tclass.defaultLayoutName();
|
Fix bug 4037 and related problems. The patch has been cleaned up a bit
from the one posted to the list.
The basic idea has two parts. First, we hard code an "empty layout"
(called PlainLayout, for want of a better name) in TextClass and read it
before doing anything else. It can therefore be customized by classes,
if they want---say, to make it left-aligned. Second, InsetText's are
divided into three types: (i) normal ones, that use the "default" layout
defined by the text class; (ii) highly restrictive ones, such as ERT and
(not quite an inset) table cells, which demand the empty layout; (iii)
middling ones, which default to an empty layout and use the empty layout
in place of the default. (This is so we don't get the same problem we
had with ERT in e.g. footnotes.) The type of inset is signaled by new
methods InsetText::forceEmptyLayout() and InsetText::useEmptyLayout().
(The latter might better be called: useEmptyLayoutInsteadOfDefault(),
but that's silly.) The old InsetText::forceDefaultParagraphs() has been
split into these, plus a new method InsetText::allowParagraphCustomization().
A lot of the changes just adapt to this change.
The other big change is in GuiToolbar: We want to show LyXDefault and
the "default" layout only when they're active.
There are a handful of places where I'm not entirely sure whether we
should be using forceEmptyLayout or !allowParagraphCustomization() or
both. The InsetCaption is one of these. These places, and some others,
are marked with FIXMEs, so I'd appreciate it if people would search
through the patch and let me know whether these need changing. If they
don't, the FIXMEs can be deleted.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22966 a592a061-630c-0410-9148-cb99ea01b6c8
2008-02-12 17:31:07 +00:00
|
|
|
|
2009-08-09 15:29:34 +00:00
|
|
|
if (owner_->forcePlainLayout()) {
|
Fix bug 4037 and related problems. The patch has been cleaned up a bit
from the one posted to the list.
The basic idea has two parts. First, we hard code an "empty layout"
(called PlainLayout, for want of a better name) in TextClass and read it
before doing anything else. It can therefore be customized by classes,
if they want---say, to make it left-aligned. Second, InsetText's are
divided into three types: (i) normal ones, that use the "default" layout
defined by the text class; (ii) highly restrictive ones, such as ERT and
(not quite an inset) table cells, which demand the empty layout; (iii)
middling ones, which default to an empty layout and use the empty layout
in place of the default. (This is so we don't get the same problem we
had with ERT in e.g. footnotes.) The type of inset is signaled by new
methods InsetText::forceEmptyLayout() and InsetText::useEmptyLayout().
(The latter might better be called: useEmptyLayoutInsteadOfDefault(),
but that's silly.) The old InsetText::forceDefaultParagraphs() has been
split into these, plus a new method InsetText::allowParagraphCustomization().
A lot of the changes just adapt to this change.
The other big change is in GuiToolbar: We want to show LyXDefault and
the "default" layout only when they're active.
There are a handful of places where I'm not entirely sure whether we
should be using forceEmptyLayout or !allowParagraphCustomization() or
both. The InsetCaption is one of these. These places, and some others,
are marked with FIXMEs, so I'd appreciate it if people would search
through the patch and let me know whether these need changing. If they
don't, the FIXMEs can be deleted.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22966 a592a061-630c-0410-9148-cb99ea01b6c8
2008-02-12 17:31:07 +00:00
|
|
|
// in this case only the empty layout is allowed
|
2008-08-01 20:57:27 +00:00
|
|
|
layoutname = tclass.plainLayoutName();
|
2008-07-10 17:41:52 +00:00
|
|
|
} else if (par.usePlainLayout()) {
|
2014-03-08 10:59:39 +00:00
|
|
|
// in this case, default layout maps to empty layout
|
Fix bug 4037 and related problems. The patch has been cleaned up a bit
from the one posted to the list.
The basic idea has two parts. First, we hard code an "empty layout"
(called PlainLayout, for want of a better name) in TextClass and read it
before doing anything else. It can therefore be customized by classes,
if they want---say, to make it left-aligned. Second, InsetText's are
divided into three types: (i) normal ones, that use the "default" layout
defined by the text class; (ii) highly restrictive ones, such as ERT and
(not quite an inset) table cells, which demand the empty layout; (iii)
middling ones, which default to an empty layout and use the empty layout
in place of the default. (This is so we don't get the same problem we
had with ERT in e.g. footnotes.) The type of inset is signaled by new
methods InsetText::forceEmptyLayout() and InsetText::useEmptyLayout().
(The latter might better be called: useEmptyLayoutInsteadOfDefault(),
but that's silly.) The old InsetText::forceDefaultParagraphs() has been
split into these, plus a new method InsetText::allowParagraphCustomization().
A lot of the changes just adapt to this change.
The other big change is in GuiToolbar: We want to show LyXDefault and
the "default" layout only when they're active.
There are a handful of places where I'm not entirely sure whether we
should be using forceEmptyLayout or !allowParagraphCustomization() or
both. The InsetCaption is one of these. These places, and some others,
are marked with FIXMEs, so I'd appreciate it if people would search
through the patch and let me know whether these need changing. If they
don't, the FIXMEs can be deleted.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22966 a592a061-630c-0410-9148-cb99ea01b6c8
2008-02-12 17:31:07 +00:00
|
|
|
if (layoutname == tclass.defaultLayoutName())
|
2008-08-01 20:57:27 +00:00
|
|
|
layoutname = tclass.plainLayoutName();
|
2014-03-08 10:59:39 +00:00
|
|
|
} else {
|
Fix bug 4037 and related problems. The patch has been cleaned up a bit
from the one posted to the list.
The basic idea has two parts. First, we hard code an "empty layout"
(called PlainLayout, for want of a better name) in TextClass and read it
before doing anything else. It can therefore be customized by classes,
if they want---say, to make it left-aligned. Second, InsetText's are
divided into three types: (i) normal ones, that use the "default" layout
defined by the text class; (ii) highly restrictive ones, such as ERT and
(not quite an inset) table cells, which demand the empty layout; (iii)
middling ones, which default to an empty layout and use the empty layout
in place of the default. (This is so we don't get the same problem we
had with ERT in e.g. footnotes.) The type of inset is signaled by new
methods InsetText::forceEmptyLayout() and InsetText::useEmptyLayout().
(The latter might better be called: useEmptyLayoutInsteadOfDefault(),
but that's silly.) The old InsetText::forceDefaultParagraphs() has been
split into these, plus a new method InsetText::allowParagraphCustomization().
A lot of the changes just adapt to this change.
The other big change is in GuiToolbar: We want to show LyXDefault and
the "default" layout only when they're active.
There are a handful of places where I'm not entirely sure whether we
should be using forceEmptyLayout or !allowParagraphCustomization() or
both. The InsetCaption is one of these. These places, and some others,
are marked with FIXMEs, so I'd appreciate it if people would search
through the patch and let me know whether these need changing. If they
don't, the FIXMEs can be deleted.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22966 a592a061-630c-0410-9148-cb99ea01b6c8
2008-02-12 17:31:07 +00:00
|
|
|
// otherwise, the empty layout maps to the default
|
2008-08-01 20:57:27 +00:00
|
|
|
if (layoutname == tclass.plainLayoutName())
|
Fix bug 4037 and related problems. The patch has been cleaned up a bit
from the one posted to the list.
The basic idea has two parts. First, we hard code an "empty layout"
(called PlainLayout, for want of a better name) in TextClass and read it
before doing anything else. It can therefore be customized by classes,
if they want---say, to make it left-aligned. Second, InsetText's are
divided into three types: (i) normal ones, that use the "default" layout
defined by the text class; (ii) highly restrictive ones, such as ERT and
(not quite an inset) table cells, which demand the empty layout; (iii)
middling ones, which default to an empty layout and use the empty layout
in place of the default. (This is so we don't get the same problem we
had with ERT in e.g. footnotes.) The type of inset is signaled by new
methods InsetText::forceEmptyLayout() and InsetText::useEmptyLayout().
(The latter might better be called: useEmptyLayoutInsteadOfDefault(),
but that's silly.) The old InsetText::forceDefaultParagraphs() has been
split into these, plus a new method InsetText::allowParagraphCustomization().
A lot of the changes just adapt to this change.
The other big change is in GuiToolbar: We want to show LyXDefault and
the "default" layout only when they're active.
There are a handful of places where I'm not entirely sure whether we
should be using forceEmptyLayout or !allowParagraphCustomization() or
both. The InsetCaption is one of these. These places, and some others,
are marked with FIXMEs, so I'd appreciate it if people would search
through the patch and let me know whether these need changing. If they
don't, the FIXMEs can be deleted.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22966 a592a061-630c-0410-9148-cb99ea01b6c8
2008-02-12 17:31:07 +00:00
|
|
|
layoutname = tclass.defaultLayoutName();
|
2004-03-25 09:16:36 +00:00
|
|
|
}
|
|
|
|
|
2008-07-11 02:43:02 +00:00
|
|
|
// When we apply an unknown layout to a document, we add this layout to the textclass
|
|
|
|
// of this document. For example, when you apply class article to a beamer document,
|
|
|
|
// all unknown layouts such as frame will be added to document class article so that
|
|
|
|
// these layouts can keep their original names.
|
2011-06-08 00:12:57 +00:00
|
|
|
bool const added_one = tclass.addLayoutIfNeeded(layoutname);
|
|
|
|
if (added_one) {
|
|
|
|
// Warn the user.
|
|
|
|
docstring const s = bformat(_("Layout `%1$s' was not found."), layoutname);
|
2016-10-09 19:34:12 +00:00
|
|
|
errorList.push_back(ErrorItem(_("Layout Not Found"), s,
|
|
|
|
{par.id(), 0}, {par.id(), -1}));
|
2011-06-08 00:12:57 +00:00
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
|
2008-02-28 01:42:02 +00:00
|
|
|
par.setLayout(bp.documentClass()[layoutname]);
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
|
|
// Test whether the layout is obsolete.
|
2008-03-06 21:31:27 +00:00
|
|
|
Layout const & layout = par.layout();
|
|
|
|
if (!layout.obsoleted_by().empty())
|
|
|
|
par.setLayout(bp.documentClass()[layout.obsoleted_by()]);
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
|
|
par.params().read(lex);
|
|
|
|
|
|
|
|
} else if (token == "\\end_layout") {
|
2008-04-02 23:06:22 +00:00
|
|
|
LYXERR0("Solitary \\end_layout in line " << lex.lineNumber() << "\n"
|
2007-11-29 21:10:35 +00:00
|
|
|
<< "Missing \\begin_layout ?");
|
2004-03-25 09:16:36 +00:00
|
|
|
} else if (token == "\\end_inset") {
|
2008-04-02 23:06:22 +00:00
|
|
|
LYXERR0("Solitary \\end_inset in line " << lex.lineNumber() << "\n"
|
2007-11-29 21:10:35 +00:00
|
|
|
<< "Missing \\begin_inset ?");
|
2004-03-25 09:16:36 +00:00
|
|
|
} else if (token == "\\begin_inset") {
|
2007-04-29 13:39:47 +00:00
|
|
|
Inset * inset = readInset(lex, buf);
|
2004-03-25 09:16:36 +00:00
|
|
|
if (inset)
|
|
|
|
par.insertInset(par.size(), inset, font, change);
|
|
|
|
else {
|
|
|
|
lex.eatLine();
|
2006-11-22 09:15:38 +00:00
|
|
|
docstring line = lex.getDocString();
|
2006-09-11 08:54:10 +00:00
|
|
|
errorList.push_back(ErrorItem(_("Unknown Inset"), line,
|
2016-10-09 19:34:12 +00:00
|
|
|
{par.id(), 0}, {par.id(), -1}));
|
2004-03-25 09:16:36 +00:00
|
|
|
}
|
|
|
|
} else if (token == "\\family") {
|
|
|
|
lex.next();
|
2007-10-28 18:51:54 +00:00
|
|
|
setLyXFamily(lex.getString(), font.fontInfo());
|
2004-03-25 09:16:36 +00:00
|
|
|
} else if (token == "\\series") {
|
|
|
|
lex.next();
|
2007-10-28 18:51:54 +00:00
|
|
|
setLyXSeries(lex.getString(), font.fontInfo());
|
2004-03-25 09:16:36 +00:00
|
|
|
} else if (token == "\\shape") {
|
|
|
|
lex.next();
|
2007-10-28 18:51:54 +00:00
|
|
|
setLyXShape(lex.getString(), font.fontInfo());
|
2004-03-25 09:16:36 +00:00
|
|
|
} else if (token == "\\size") {
|
|
|
|
lex.next();
|
2007-10-28 18:51:54 +00:00
|
|
|
setLyXSize(lex.getString(), font.fontInfo());
|
2004-03-25 09:16:36 +00:00
|
|
|
} else if (token == "\\lang") {
|
|
|
|
lex.next();
|
|
|
|
string const tok = lex.getString();
|
|
|
|
Language const * lang = languages.getLanguage(tok);
|
|
|
|
if (lang) {
|
|
|
|
font.setLanguage(lang);
|
|
|
|
} else {
|
|
|
|
font.setLanguage(bp.language);
|
|
|
|
lex.printError("Unknown language `$$Token'");
|
|
|
|
}
|
|
|
|
} else if (token == "\\numeric") {
|
|
|
|
lex.next();
|
2009-10-27 16:16:15 +00:00
|
|
|
font.fontInfo().setNumber(setLyXMisc(lex.getString()));
|
2004-03-25 09:16:36 +00:00
|
|
|
} else if (token == "\\emph") {
|
|
|
|
lex.next();
|
2009-10-27 16:16:15 +00:00
|
|
|
font.fontInfo().setEmph(setLyXMisc(lex.getString()));
|
2004-03-25 09:16:36 +00:00
|
|
|
} else if (token == "\\bar") {
|
|
|
|
lex.next();
|
|
|
|
string const tok = lex.getString();
|
|
|
|
|
|
|
|
if (tok == "under")
|
2007-10-28 18:51:54 +00:00
|
|
|
font.fontInfo().setUnderbar(FONT_ON);
|
2004-03-25 09:16:36 +00:00
|
|
|
else if (tok == "no")
|
2007-10-28 18:51:54 +00:00
|
|
|
font.fontInfo().setUnderbar(FONT_OFF);
|
2004-03-25 09:16:36 +00:00
|
|
|
else if (tok == "default")
|
2007-10-28 18:51:54 +00:00
|
|
|
font.fontInfo().setUnderbar(FONT_INHERIT);
|
2004-03-25 09:16:36 +00:00
|
|
|
else
|
|
|
|
lex.printError("Unknown bar font flag "
|
|
|
|
"`$$Token'");
|
2009-05-03 22:45:14 +00:00
|
|
|
} else if (token == "\\strikeout") {
|
|
|
|
lex.next();
|
2009-10-27 16:16:15 +00:00
|
|
|
font.fontInfo().setStrikeout(setLyXMisc(lex.getString()));
|
2017-04-04 22:01:19 +00:00
|
|
|
} else if (token == "\\xout") {
|
|
|
|
lex.next();
|
|
|
|
font.fontInfo().setXout(setLyXMisc(lex.getString()));
|
2009-05-05 09:26:28 +00:00
|
|
|
} else if (token == "\\uuline") {
|
|
|
|
lex.next();
|
2009-10-27 16:16:15 +00:00
|
|
|
font.fontInfo().setUuline(setLyXMisc(lex.getString()));
|
2009-05-05 09:26:28 +00:00
|
|
|
} else if (token == "\\uwave") {
|
|
|
|
lex.next();
|
2009-10-27 16:16:15 +00:00
|
|
|
font.fontInfo().setUwave(setLyXMisc(lex.getString()));
|
2004-03-25 09:16:36 +00:00
|
|
|
} else if (token == "\\noun") {
|
|
|
|
lex.next();
|
2009-10-27 16:16:15 +00:00
|
|
|
font.fontInfo().setNoun(setLyXMisc(lex.getString()));
|
2004-03-25 09:16:36 +00:00
|
|
|
} else if (token == "\\color") {
|
|
|
|
lex.next();
|
2007-10-28 18:51:54 +00:00
|
|
|
setLyXColor(lex.getString(), font.fontInfo());
|
2015-03-01 10:16:57 +00:00
|
|
|
} else if (token == "\\SpecialChar" ||
|
|
|
|
(token == "\\SpecialCharNoPassThru" &&
|
|
|
|
!par.layout().pass_thru && !inset().isPassThru())) {
|
2016-06-02 20:58:52 +00:00
|
|
|
auto inset = make_unique<InsetSpecialChar>();
|
2009-08-29 23:14:38 +00:00
|
|
|
inset->read(lex);
|
2009-11-08 15:53:21 +00:00
|
|
|
inset->setBuffer(*buf);
|
2009-08-29 23:14:38 +00:00
|
|
|
par.insertInset(par.size(), inset.release(), font, change);
|
2015-03-01 10:16:57 +00:00
|
|
|
} else if (token == "\\SpecialCharNoPassThru") {
|
|
|
|
lex.next();
|
|
|
|
docstring const s = ltrim(lex.getDocString(), "\\");
|
|
|
|
par.insert(par.size(), s, font, change);
|
2012-08-23 15:42:53 +00:00
|
|
|
} else if (token == "\\IPAChar") {
|
2016-06-02 20:58:52 +00:00
|
|
|
auto inset = make_unique<InsetIPAChar>();
|
2012-08-23 15:42:53 +00:00
|
|
|
inset->read(lex);
|
|
|
|
inset->setBuffer(*buf);
|
|
|
|
par.insertInset(par.size(), inset.release(), font, change);
|
2015-02-24 20:58:27 +00:00
|
|
|
} else if (token == "\\twohyphens" || token == "\\threehyphens") {
|
|
|
|
// Ideally, this should be done by lyx2lyx, but lyx2lyx does not know the
|
|
|
|
// running font and does not know anything about layouts (and CopyStyle).
|
|
|
|
Layout const & layout(par.layout());
|
|
|
|
FontInfo info = font.fontInfo();
|
|
|
|
info.realize(layout.resfont);
|
2015-02-26 21:55:20 +00:00
|
|
|
if (layout.pass_thru || inset().isPassThru() ||
|
|
|
|
info.family() == TYPEWRITER_FAMILY) {
|
2015-02-24 20:58:27 +00:00
|
|
|
if (token == "\\twohyphens")
|
|
|
|
par.insert(par.size(), from_ascii("--"), font, change);
|
|
|
|
else
|
|
|
|
par.insert(par.size(), from_ascii("---"), font, change);
|
|
|
|
} else {
|
|
|
|
if (token == "\\twohyphens")
|
|
|
|
par.insertChar(par.size(), 0x2013, font, change);
|
|
|
|
else
|
|
|
|
par.insertChar(par.size(), 0x2014, font, change);
|
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
} else if (token == "\\backslash") {
|
2007-10-22 13:09:16 +00:00
|
|
|
par.appendChar('\\', font, change);
|
2004-03-25 09:16:36 +00:00
|
|
|
} else if (token == "\\LyXTable") {
|
2016-06-02 20:58:52 +00:00
|
|
|
auto inset = make_unique<InsetTabular>(buf);
|
2008-02-27 20:43:16 +00:00
|
|
|
inset->read(lex);
|
2004-03-25 09:16:36 +00:00
|
|
|
par.insertInset(par.size(), inset.release(), font, change);
|
|
|
|
} else if (token == "\\change_unchanged") {
|
|
|
|
change = Change(Change::UNCHANGED);
|
2009-07-22 20:36:40 +00:00
|
|
|
} else if (token == "\\change_inserted" || token == "\\change_deleted") {
|
2004-10-05 12:56:22 +00:00
|
|
|
lex.eatLine();
|
2007-12-12 19:28:07 +00:00
|
|
|
istringstream is(lex.getString());
|
2010-11-05 18:25:29 +00:00
|
|
|
int aid;
|
2007-11-29 08:55:43 +00:00
|
|
|
time_t ct;
|
2004-03-25 09:16:36 +00:00
|
|
|
is >> aid >> ct;
|
2015-11-11 21:31:05 +00:00
|
|
|
BufferParams::AuthorMap const & am = bp.author_map_;
|
2009-07-23 20:08:05 +00:00
|
|
|
if (am.find(aid) == am.end()) {
|
2015-11-11 21:31:05 +00:00
|
|
|
errorList.push_back(ErrorItem(
|
|
|
|
_("Change tracking author index missing"),
|
|
|
|
bformat(_("A change tracking author information for index "
|
|
|
|
"%1$d is missing. This can happen after a wrong "
|
|
|
|
"merge by a version control system. In this case, "
|
|
|
|
"either fix the merge, or have this information "
|
|
|
|
"missing until the corresponding tracked changes "
|
|
|
|
"are merged or this user edits the file again.\n"),
|
|
|
|
aid),
|
2016-10-09 19:34:12 +00:00
|
|
|
{par.id(), par.size()}, {par.id(), par.size() + 1}));
|
2015-11-11 21:31:05 +00:00
|
|
|
bp.addAuthor(Author(aid));
|
2009-07-22 20:36:40 +00:00
|
|
|
}
|
2015-11-11 21:31:05 +00:00
|
|
|
if (token == "\\change_inserted")
|
|
|
|
change = Change(Change::INSERTED, am.find(aid)->second, ct);
|
|
|
|
else
|
|
|
|
change = Change(Change::DELETED, am.find(aid)->second, ct);
|
2004-03-25 09:16:36 +00:00
|
|
|
} else {
|
|
|
|
lex.eatLine();
|
2006-09-11 08:54:10 +00:00
|
|
|
errorList.push_back(ErrorItem(_("Unknown token"),
|
2016-10-09 19:34:12 +00:00
|
|
|
bformat(_("Unknown token: %1$s %2$s\n"),
|
|
|
|
from_utf8(token),
|
|
|
|
lex.getDocString()),
|
|
|
|
{par.id(), 0}, {par.id(), -1}));
|
2004-03-25 09:16:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-09 15:29:34 +00:00
|
|
|
void Text::readParagraph(Paragraph & par, Lexer & lex,
|
2006-10-21 00:16:43 +00:00
|
|
|
ErrorList & errorList)
|
2004-03-25 09:16:36 +00:00
|
|
|
{
|
|
|
|
lex.nextToken();
|
|
|
|
string token = lex.getString();
|
2007-04-29 18:17:15 +00:00
|
|
|
Font font;
|
2006-10-11 20:01:32 +00:00
|
|
|
Change change(Change::UNCHANGED);
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
|
|
while (lex.isOK()) {
|
2009-08-09 15:29:34 +00:00
|
|
|
readParToken(par, lex, token, font, change, errorList);
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
|
|
lex.nextToken();
|
|
|
|
token = lex.getString();
|
|
|
|
|
|
|
|
if (token.empty())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (token == "\\end_layout") {
|
|
|
|
//Ok, paragraph finished
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2007-11-15 20:04:51 +00:00
|
|
|
LYXERR(Debug::PARSER, "Handling paragraph token: `" << token << '\'');
|
2004-03-25 09:16:36 +00:00
|
|
|
if (token == "\\begin_layout" || token == "\\end_document"
|
|
|
|
|| token == "\\end_inset" || token == "\\begin_deeper"
|
|
|
|
|| token == "\\end_deeper") {
|
|
|
|
lex.pushToken(token);
|
|
|
|
lyxerr << "Paragraph ended in line "
|
2008-04-02 23:06:22 +00:00
|
|
|
<< lex.lineNumber() << "\n"
|
2004-03-25 09:16:36 +00:00
|
|
|
<< "Missing \\end_layout.\n";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2006-03-11 13:31:41 +00:00
|
|
|
// Final change goes to paragraph break:
|
2006-05-08 20:30:52 +00:00
|
|
|
par.setChange(par.size(), change);
|
2006-04-05 23:56:29 +00:00
|
|
|
|
2005-06-16 15:15:33 +00:00
|
|
|
// Initialize begin_of_body_ on load; redoParagraph maintains
|
|
|
|
par.setBeginOfBody();
|
2014-03-08 10:59:39 +00:00
|
|
|
|
2010-09-14 05:24:04 +00:00
|
|
|
// mark paragraph for spell checking on load
|
|
|
|
// par.requestSpellCheck();
|
2004-03-25 09:16:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-03-15 12:22:28 +00:00
|
|
|
class TextCompletionList : public CompletionList
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
///
|
2016-12-31 14:22:07 +00:00
|
|
|
TextCompletionList(Cursor const & cur, WordList const & list)
|
2013-05-02 16:27:32 +00:00
|
|
|
: buffer_(cur.buffer()), list_(list)
|
2008-11-17 11:46:07 +00:00
|
|
|
{}
|
2008-03-15 12:22:28 +00:00
|
|
|
///
|
|
|
|
virtual ~TextCompletionList() {}
|
2014-03-08 10:59:39 +00:00
|
|
|
|
2008-03-15 12:22:28 +00:00
|
|
|
///
|
|
|
|
virtual bool sorted() const { return true; }
|
|
|
|
///
|
|
|
|
virtual size_t size() const
|
|
|
|
{
|
2016-12-31 14:22:07 +00:00
|
|
|
return list_.size();
|
2008-03-15 12:22:28 +00:00
|
|
|
}
|
|
|
|
///
|
|
|
|
virtual docstring const & data(size_t idx) const
|
|
|
|
{
|
2016-12-31 14:22:07 +00:00
|
|
|
return list_.word(idx);
|
2008-03-15 12:22:28 +00:00
|
|
|
}
|
2014-03-08 10:59:39 +00:00
|
|
|
|
2008-03-15 12:22:28 +00:00
|
|
|
private:
|
|
|
|
///
|
2008-11-17 11:46:07 +00:00
|
|
|
Buffer const * buffer_;
|
2008-03-15 12:22:28 +00:00
|
|
|
///
|
2016-12-31 14:22:07 +00:00
|
|
|
WordList const & list_;
|
2008-03-15 12:22:28 +00:00
|
|
|
};
|
|
|
|
|
2003-10-23 09:06:18 +00:00
|
|
|
|
2007-08-14 16:59:59 +00:00
|
|
|
bool Text::empty() const
|
|
|
|
{
|
|
|
|
return pars_.empty() || (pars_.size() == 1 && pars_[0].empty()
|
2014-03-08 10:59:39 +00:00
|
|
|
// FIXME: Should we consider the labeled type as empty too?
|
2008-03-06 21:31:27 +00:00
|
|
|
&& pars_[0].layout().labeltype == LABEL_NO_LABEL);
|
2007-08-14 16:59:59 +00:00
|
|
|
}
|
|
|
|
|
2003-03-17 16:25:00 +00:00
|
|
|
|
2009-08-09 15:29:34 +00:00
|
|
|
double Text::spacing(Paragraph const & par) const
|
2003-03-17 16:25:00 +00:00
|
|
|
{
|
2004-04-08 15:03:33 +00:00
|
|
|
if (par.params().spacing().isDefault())
|
2009-08-09 15:29:34 +00:00
|
|
|
return owner_->buffer().params().spacing().getValue();
|
2004-04-08 15:03:33 +00:00
|
|
|
return par.params().spacing().getValue();
|
2003-03-17 16:25:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-09 17:30:41 +00:00
|
|
|
/**
|
|
|
|
* This breaks a paragraph at the specified position.
|
|
|
|
* The new paragraph will:
|
|
|
|
* - Decrease depth by one (or change layout to default layout) when
|
2014-03-08 10:59:39 +00:00
|
|
|
* keep_layout == false
|
2009-08-09 17:30:41 +00:00
|
|
|
* - keep current depth and layout when keep_layout == true
|
|
|
|
*/
|
2014-03-08 10:59:39 +00:00
|
|
|
static void breakParagraph(Text & text, pit_type par_offset, pos_type pos,
|
2009-08-09 17:30:41 +00:00
|
|
|
bool keep_layout)
|
|
|
|
{
|
2009-08-09 18:35:39 +00:00
|
|
|
BufferParams const & bparams = text.inset().buffer().params();
|
|
|
|
ParagraphList & pars = text.paragraphs();
|
2009-08-09 17:30:41 +00:00
|
|
|
// create a new paragraph, and insert into the list
|
|
|
|
ParagraphList::iterator tmp =
|
2016-01-10 16:59:42 +00:00
|
|
|
pars.insert(lyx::next(pars.begin(), par_offset + 1),
|
2009-08-09 17:30:41 +00:00
|
|
|
Paragraph());
|
|
|
|
|
|
|
|
Paragraph & par = pars[par_offset];
|
|
|
|
|
|
|
|
// 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());
|
|
|
|
|
|
|
|
if (keep_layout) {
|
|
|
|
tmp->setLayout(par.layout());
|
|
|
|
tmp->setLabelWidthString(par.params().labelWidthString());
|
|
|
|
tmp->params().depth(par.params().depth());
|
|
|
|
} else if (par.params().depth() > 0) {
|
2009-08-09 18:35:39 +00:00
|
|
|
Paragraph const & hook = pars[text.outerHook(par_offset)];
|
2009-08-09 17:30:41 +00:00
|
|
|
tmp->setLayout(hook.layout());
|
|
|
|
// not sure the line below is useful
|
|
|
|
tmp->setLabelWidthString(par.params().labelWidthString());
|
|
|
|
tmp->params().depth(hook.params().depth());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool const isempty = (par.allowEmpty() && par.empty());
|
|
|
|
|
|
|
|
if (!isempty && (par.size() > pos || par.empty())) {
|
|
|
|
tmp->setLayout(par.layout());
|
|
|
|
tmp->params().align(par.params().align());
|
|
|
|
tmp->setLabelWidthString(par.params().labelWidthString());
|
|
|
|
|
|
|
|
tmp->params().depth(par.params().depth());
|
|
|
|
tmp->params().noindent(par.params().noindent());
|
2020-04-08 20:40:39 +00:00
|
|
|
tmp->params().spacing(par.params().spacing());
|
2009-08-09 17:30:41 +00:00
|
|
|
|
|
|
|
// move everything behind the break position
|
|
|
|
// to the new paragraph
|
|
|
|
|
|
|
|
/* Note: if !keepempty, empty() == true, then we reach
|
|
|
|
* here with size() == 0. So pos_end becomes - 1. This
|
|
|
|
* doesn't cause problems because both loops below
|
|
|
|
* enforce pos <= pos_end and 0 <= pos
|
|
|
|
*/
|
|
|
|
pos_type pos_end = par.size() - 1;
|
|
|
|
|
|
|
|
for (pos_type i = pos, j = 0; i <= pos_end; ++i) {
|
|
|
|
if (moveItem(par, pos, *tmp, j, bparams)) {
|
|
|
|
++j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Move over the end-of-par change information
|
|
|
|
tmp->setChange(tmp->size(), par.lookupChange(par.size()));
|
2014-03-29 22:52:36 +00:00
|
|
|
par.setChange(par.size(), Change(bparams.track_changes ?
|
2009-08-09 17:30:41 +00:00
|
|
|
Change::INSERTED : Change::UNCHANGED));
|
|
|
|
|
|
|
|
if (pos) {
|
|
|
|
// Make sure that we keep the language when
|
|
|
|
// breaking paragraph.
|
|
|
|
if (tmp->empty()) {
|
|
|
|
Font changed = tmp->getFirstFontSettings(bparams);
|
|
|
|
Font const & old = par.getFontSettings(bparams, par.size());
|
|
|
|
changed.setLanguage(old.language());
|
|
|
|
tmp->setFont(0, changed);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isempty) {
|
|
|
|
bool const soa = par.params().startOfAppendix();
|
|
|
|
par.params().clear();
|
|
|
|
// do not lose start of appendix marker (bug 4212)
|
|
|
|
par.params().startOfAppendix(soa);
|
|
|
|
par.setPlainOrDefaultLayout(bparams.documentClass());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (keep_layout) {
|
|
|
|
par.setLayout(tmp->layout());
|
|
|
|
par.setLabelWidthString(tmp->params().labelWidthString());
|
|
|
|
par.params().depth(tmp->params().depth());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-10-08 13:17:15 +00:00
|
|
|
void Text::breakParagraph(Cursor & cur, bool inverse_logic)
|
2000-02-10 17:53:36 +00:00
|
|
|
{
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(this == cur.text());
|
2006-03-11 13:31:41 +00:00
|
|
|
|
2004-02-12 16:36:01 +00:00
|
|
|
Paragraph & cpar = cur.paragraph();
|
2004-11-24 21:53:46 +00:00
|
|
|
pit_type cpit = cur.pit();
|
2004-02-12 16:36:01 +00:00
|
|
|
|
2008-11-17 11:46:07 +00:00
|
|
|
DocumentClass const & tclass = cur.buffer()->params().documentClass();
|
2008-03-06 21:31:27 +00:00
|
|
|
Layout const & layout = cpar.layout();
|
2000-09-23 04:57:18 +00:00
|
|
|
|
2010-01-18 22:11:06 +00:00
|
|
|
if (cur.lastpos() == 0 && !cpar.allowEmpty()) {
|
2016-10-13 18:33:57 +00:00
|
|
|
if (changeDepthAllowed(cur, DEC_DEPTH)) {
|
2010-01-18 22:11:06 +00:00
|
|
|
changeDepth(cur, DEC_DEPTH);
|
2016-10-13 18:33:57 +00:00
|
|
|
pit_type const prev = depthHook(cpit, cpar.getDepth());
|
|
|
|
docstring const & lay = pars_[prev].layout().name();
|
|
|
|
if (lay != layout.name())
|
|
|
|
setLayout(cur, lay);
|
|
|
|
} else {
|
2013-06-07 16:15:18 +00:00
|
|
|
docstring const & lay = cur.paragraph().usePlainLayout()
|
|
|
|
? tclass.plainLayoutName() : tclass.defaultLayoutName();
|
2016-08-24 13:29:17 +00:00
|
|
|
if (lay != layout.name())
|
|
|
|
setLayout(cur, lay);
|
2013-06-07 16:15:18 +00:00
|
|
|
}
|
2002-05-29 12:58:23 +00:00
|
|
|
return;
|
2010-01-18 22:11:06 +00:00
|
|
|
}
|
2002-03-21 17:27:08 +00:00
|
|
|
|
2012-07-16 21:39:24 +00:00
|
|
|
cur.recordUndo();
|
2000-02-10 17:53:36 +00:00
|
|
|
|
2002-05-29 12:58:23 +00:00
|
|
|
// Always break behind a space
|
|
|
|
// It is better to erase the space (Dekel)
|
2004-02-12 16:36:01 +00:00
|
|
|
if (cur.pos() != cur.lastpos() && cpar.isLineSeparator(cur.pos()))
|
2014-03-29 22:52:36 +00:00
|
|
|
cpar.eraseChar(cur.pos(), cur.buffer()->params().track_changes);
|
2002-05-29 12:58:23 +00:00
|
|
|
|
2006-11-26 11:53:33 +00:00
|
|
|
// What should the layout for the new paragraph be?
|
2014-03-08 10:59:39 +00:00
|
|
|
bool keep_layout = layout.isEnvironment()
|
2010-11-06 15:06:19 +00:00
|
|
|
|| (layout.isParagraph() && layout.parbreak_is_newline);
|
|
|
|
if (inverse_logic)
|
|
|
|
keep_layout = !keep_layout;
|
2005-01-31 16:29:48 +00:00
|
|
|
|
|
|
|
// We need to remember this before we break the paragraph, because
|
|
|
|
// that invalidates the layout variable
|
2008-03-06 21:31:27 +00:00
|
|
|
bool sensitive = layout.labeltype == LABEL_SENSITIVE;
|
2002-05-29 12:58:23 +00:00
|
|
|
|
2005-01-31 16:29:48 +00:00
|
|
|
// we need to set this before we insert the paragraph.
|
2004-02-12 16:36:01 +00:00
|
|
|
bool const isempty = cpar.allowEmpty() && cpar.empty();
|
2005-01-31 16:29:48 +00:00
|
|
|
|
2009-08-09 18:35:39 +00:00
|
|
|
lyx::breakParagraph(*this, cpit, cur.pos(), keep_layout);
|
2005-01-31 16:29:48 +00:00
|
|
|
|
|
|
|
// After this, neither paragraph contains any rows!
|
2000-09-23 04:57:18 +00:00
|
|
|
|
2004-11-24 21:53:46 +00:00
|
|
|
cpit = cur.pit();
|
|
|
|
pit_type next_par = cpit + 1;
|
2003-08-15 08:03:54 +00:00
|
|
|
|
2002-05-29 12:58:23 +00:00
|
|
|
// well this is the caption hack since one caption is really enough
|
2005-01-31 16:29:48 +00:00
|
|
|
if (sensitive) {
|
2004-08-15 00:01:45 +00:00
|
|
|
if (cur.pos() == 0)
|
2002-05-29 12:58:23 +00:00
|
|
|
// set to standard-layout
|
2008-08-01 20:57:27 +00:00
|
|
|
//FIXME Check if this should be plainLayout() in some cases
|
2004-03-25 09:16:36 +00:00
|
|
|
pars_[cpit].applyLayout(tclass.defaultLayout());
|
2002-05-29 12:58:23 +00:00
|
|
|
else
|
|
|
|
// set to standard-layout
|
2008-08-01 20:57:27 +00:00
|
|
|
//FIXME Check if this should be plainLayout() in some cases
|
2004-03-25 09:16:36 +00:00
|
|
|
pars_[next_par].applyLayout(tclass.defaultLayout());
|
2002-05-29 12:58:23 +00:00
|
|
|
}
|
|
|
|
|
2007-01-07 12:01:26 +00:00
|
|
|
while (!pars_[next_par].empty() && pars_[next_par].isNewline(0)) {
|
2014-03-29 22:52:36 +00:00
|
|
|
if (!pars_[next_par].eraseChar(0, cur.buffer()->params().track_changes))
|
2007-01-07 12:01:26 +00:00
|
|
|
break; // the character couldn't be deleted physically due to change tracking
|
|
|
|
}
|
2003-04-13 02:23:30 +00:00
|
|
|
|
2006-12-29 23:54:48 +00:00
|
|
|
// A singlePar update is not enough in this case.
|
2010-07-08 20:04:35 +00:00
|
|
|
cur.screenUpdateFlags(Update::Force);
|
2010-07-09 14:37:00 +00:00
|
|
|
cur.forceBufferUpdate();
|
2006-11-17 17:42:52 +00:00
|
|
|
|
2002-05-29 12:58:23 +00:00
|
|
|
// This check is necessary. Otherwise the new empty paragraph will
|
|
|
|
// be deleted automatically. And it is more friendly for the user!
|
2004-02-12 16:36:01 +00:00
|
|
|
if (cur.pos() != 0 || isempty)
|
2007-03-30 20:41:38 +00:00
|
|
|
setCursor(cur, cur.pit() + 1, 0);
|
2002-05-29 12:58:23 +00:00
|
|
|
else
|
2007-03-30 20:41:38 +00:00
|
|
|
setCursor(cur, cur.pit(), 0);
|
2000-02-10 17:53:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-09 17:14:41 +00:00
|
|
|
// needed to insert the selection
|
2011-09-24 18:21:41 +00:00
|
|
|
void Text::insertStringAsLines(Cursor & cur, docstring const & str,
|
2009-08-09 17:14:41 +00:00
|
|
|
Font const & font)
|
|
|
|
{
|
|
|
|
BufferParams const & bparams = owner_->buffer().params();
|
2011-09-24 18:21:41 +00:00
|
|
|
pit_type pit = cur.pit();
|
|
|
|
pos_type pos = cur.pos();
|
2009-08-09 17:14:41 +00:00
|
|
|
|
|
|
|
// insert the string, don't insert doublespace
|
|
|
|
bool space_inserted = true;
|
|
|
|
for (docstring::const_iterator cit = str.begin();
|
|
|
|
cit != str.end(); ++cit) {
|
|
|
|
Paragraph & par = pars_[pit];
|
|
|
|
if (*cit == '\n') {
|
2015-03-30 21:12:39 +00:00
|
|
|
if (inset().allowMultiPar() && (!par.empty() || par.allowEmpty())) {
|
2009-08-09 18:35:39 +00:00
|
|
|
lyx::breakParagraph(*this, pit, pos,
|
|
|
|
par.layout().isEnvironment());
|
2009-08-09 17:14:41 +00:00
|
|
|
++pit;
|
|
|
|
pos = 0;
|
|
|
|
space_inserted = true;
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
2015-05-09 10:05:45 +00:00
|
|
|
// do not insert consecutive spaces if !free_spacing
|
2009-08-09 17:14:41 +00:00
|
|
|
} else if ((*cit == ' ' || *cit == '\t') &&
|
|
|
|
space_inserted && !par.isFreeSpacing()) {
|
|
|
|
continue;
|
|
|
|
} else if (*cit == '\t') {
|
|
|
|
if (!par.isFreeSpacing()) {
|
|
|
|
// tabs are like spaces here
|
2014-03-29 22:52:36 +00:00
|
|
|
par.insertChar(pos, ' ', font, bparams.track_changes);
|
2009-08-09 17:14:41 +00:00
|
|
|
++pos;
|
|
|
|
space_inserted = true;
|
|
|
|
} else {
|
2014-03-29 22:52:36 +00:00
|
|
|
par.insertChar(pos, *cit, font, bparams.track_changes);
|
2009-08-09 17:14:41 +00:00
|
|
|
++pos;
|
|
|
|
space_inserted = true;
|
|
|
|
}
|
2020-03-22 10:07:57 +00:00
|
|
|
} else if (!isPrintable(*cit) && *cit != 0x200c) {
|
|
|
|
// Ignore unprintables, except for ZWNJ (0x200c)
|
2009-08-09 17:14:41 +00:00
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
// just insert the character
|
2014-03-29 22:52:36 +00:00
|
|
|
par.insertChar(pos, *cit, font, bparams.track_changes);
|
2009-08-09 17:14:41 +00:00
|
|
|
++pos;
|
|
|
|
space_inserted = (*cit == ' ');
|
|
|
|
}
|
|
|
|
}
|
2011-09-24 18:21:41 +00:00
|
|
|
setCursor(cur, pit, pos);
|
2009-08-09 17:14:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// turn double CR to single CR, others are converted into one
|
|
|
|
// blank. Then insertStringAsLines is called
|
2011-09-24 18:21:41 +00:00
|
|
|
void Text::insertStringAsParagraphs(Cursor & cur, docstring const & str,
|
2009-08-09 17:14:41 +00:00
|
|
|
Font const & font)
|
|
|
|
{
|
|
|
|
docstring linestr = str;
|
|
|
|
bool newline_inserted = false;
|
|
|
|
|
|
|
|
for (string::size_type i = 0, siz = linestr.size(); i < siz; ++i) {
|
|
|
|
if (linestr[i] == '\n') {
|
|
|
|
if (newline_inserted) {
|
|
|
|
// we know that \r will be ignored by
|
|
|
|
// insertStringAsLines. Of course, it is a dirty
|
|
|
|
// trick, but it works...
|
|
|
|
linestr[i - 1] = '\r';
|
|
|
|
linestr[i] = '\n';
|
|
|
|
} else {
|
|
|
|
linestr[i] = ' ';
|
|
|
|
newline_inserted = true;
|
|
|
|
}
|
|
|
|
} else if (isPrintable(linestr[i])) {
|
|
|
|
newline_inserted = false;
|
|
|
|
}
|
|
|
|
}
|
2011-09-24 18:21:41 +00:00
|
|
|
insertStringAsLines(cur, linestr, font);
|
2009-08-09 17:14:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-11 10:15:29 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
bool canInsertChar(Cursor const & cur, char_type c)
|
|
|
|
{
|
|
|
|
Paragraph const & par = cur.paragraph();
|
|
|
|
// If not in free spacing mode, check if there will be two blanks together or a blank at
|
|
|
|
// the beginning of a paragraph.
|
|
|
|
if (!par.isFreeSpacing() && isLineSeparatorChar(c)) {
|
|
|
|
if (cur.pos() == 0) {
|
|
|
|
cur.message(_(
|
|
|
|
"You cannot insert a space at the "
|
|
|
|
"beginning of a paragraph. Please read the Tutorial."));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// LASSERT: Is it safe to continue here?
|
|
|
|
LASSERT(cur.pos() > 0, /**/);
|
|
|
|
if ((par.isLineSeparator(cur.pos() - 1) || par.isNewline(cur.pos() - 1))
|
|
|
|
&& !par.isDeleted(cur.pos() - 1)) {
|
|
|
|
cur.message(_(
|
|
|
|
"You cannot type two spaces this way. "
|
|
|
|
"Please read the Tutorial."));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-24 16:10:42 +00:00
|
|
|
// Prevent to insert uncodable characters in verbatim and ERT.
|
|
|
|
// The encoding is inherited from the context here.
|
|
|
|
if (par.isPassThru() && cur.getEncoding()) {
|
|
|
|
Encoding const * e = cur.getEncoding();
|
2017-07-11 10:15:29 +00:00
|
|
|
if (!e->encodable(c)) {
|
2017-12-24 16:10:42 +00:00
|
|
|
cur.message(_("Character is uncodable in this verbatim context."));
|
2017-07-11 10:15:29 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-07-23 11:11:54 +00:00
|
|
|
} // namespace
|
2017-07-11 10:15:29 +00:00
|
|
|
|
|
|
|
|
2002-05-29 12:58:23 +00:00
|
|
|
// insert a character, moves all the following breaks in the
|
2002-06-24 20:28:12 +00:00
|
|
|
// same Paragraph one to the right and make a rebreak
|
2007-04-29 23:33:02 +00:00
|
|
|
void Text::insertChar(Cursor & cur, char_type c)
|
1999-09-27 18:44:28 +00:00
|
|
|
{
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(this == cur.text());
|
2004-03-08 21:14:45 +00:00
|
|
|
|
2017-07-11 10:15:29 +00:00
|
|
|
if (!canInsertChar(cur,c))
|
|
|
|
return;
|
|
|
|
|
2007-10-18 11:51:17 +00:00
|
|
|
cur.recordUndo(INSERT_UNDO);
|
1999-09-27 18:44:28 +00:00
|
|
|
|
2007-09-02 21:48:49 +00:00
|
|
|
TextMetrics const & tm = cur.bv().textMetrics(this);
|
2008-11-17 11:46:07 +00:00
|
|
|
Buffer const & buffer = *cur.buffer();
|
2004-02-13 07:30:59 +00:00
|
|
|
Paragraph & par = cur.paragraph();
|
|
|
|
// try to remove this
|
2004-11-24 21:53:46 +00:00
|
|
|
pit_type const pit = cur.pit();
|
2000-03-09 03:36:48 +00:00
|
|
|
|
2000-10-09 12:30:52 +00:00
|
|
|
if (lyxrc.auto_number) {
|
2006-10-21 00:16:43 +00:00
|
|
|
static docstring const number_operators = from_ascii("+-/*");
|
|
|
|
static docstring const number_unary_operators = from_ascii("+-");
|
2001-01-15 14:05:45 +00:00
|
|
|
|
2020-07-10 15:17:15 +00:00
|
|
|
// Common Number Separators: comma, dot etc.
|
2018-07-22 10:36:38 +00:00
|
|
|
// European Number Terminators: percent, permille, degree, euro etc.
|
2007-10-28 18:51:54 +00:00
|
|
|
if (cur.current_font.fontInfo().number() == FONT_ON) {
|
2010-12-07 00:13:19 +00:00
|
|
|
if (!isDigitASCII(c) && !contains(number_operators, c) &&
|
2020-07-10 15:17:15 +00:00
|
|
|
!(isCommonNumberSeparator(c) &&
|
2004-02-13 07:30:59 +00:00
|
|
|
cur.pos() != 0 &&
|
|
|
|
cur.pos() != cur.lastpos() &&
|
2008-02-27 23:03:26 +00:00
|
|
|
tm.displayFont(pit, cur.pos()).fontInfo().number() == FONT_ON &&
|
2018-07-22 10:36:38 +00:00
|
|
|
tm.displayFont(pit, cur.pos() - 1).fontInfo().number() == FONT_ON) &&
|
|
|
|
!(isEuropeanNumberTerminator(c) &&
|
|
|
|
cur.pos() != 0 &&
|
|
|
|
tm.displayFont(pit, cur.pos()).fontInfo().number() == FONT_ON &&
|
2008-02-27 23:03:26 +00:00
|
|
|
tm.displayFont(pit, cur.pos() - 1).fontInfo().number() == FONT_ON)
|
2002-02-16 15:59:55 +00:00
|
|
|
)
|
2004-02-13 11:05:29 +00:00
|
|
|
number(cur); // Set current_font.number to OFF
|
2010-12-07 00:13:19 +00:00
|
|
|
} else if (isDigitASCII(c) &&
|
2007-09-02 13:35:48 +00:00
|
|
|
cur.real_current_font.isVisibleRightToLeft()) {
|
2004-02-13 11:05:29 +00:00
|
|
|
number(cur); // Set current_font.number to ON
|
2000-10-09 12:30:52 +00:00
|
|
|
|
2004-02-13 07:30:59 +00:00
|
|
|
if (cur.pos() != 0) {
|
2006-04-09 02:30:40 +00:00
|
|
|
char_type const c = par.getChar(cur.pos() - 1);
|
2001-01-15 14:05:45 +00:00
|
|
|
if (contains(number_unary_operators, c) &&
|
2004-02-13 07:30:59 +00:00
|
|
|
(cur.pos() == 1
|
|
|
|
|| par.isSeparator(cur.pos() - 2)
|
Fix bugs #8546 and #9055, and introduce new separator inset.
The algorithm used for breaking a paragraph in LaTeX export is changed
for avoiding spurious blank lines causing too much vertical space.
This change is tied to the introduction of a new inset (with two
different specializations) helping in either outputing LaTeX paragraph
breaks or separating environments in LyX. Both of the above goals were
previously achieved by the ---Separator--- layout and can now be
accomplished by the new inset in a more natural way. As an example,
after leaving an environment by hitting the Return key for two times,
a third return automatically inserts a parbreak inset, which is
equivalent to the old separator layout, i.e., it also introduces a
blank line in the output. If this blank line is not wanted, the
parbreak separator can be changed to a plain separator by a right
click of the mouse. Of course, an environment can still be separated
by the following one by using the Alt+P+Return shortcut (or the
corresponding menu key), but now the plain separator inset is used
instead of the old separator layout, such that no blank line occurs in
the LaTeX output.
Old documents are converted such that the LaTeX output remains unchanged.
As a result of this conversion, the old separator layout is replaced by
the new parbreak inset, which may also appear in places where the old
algorithm was introducing blank lines while the new one is not.
Note that not all blank lines were actually affecting the LaTeX output,
because a blank line is simply ignored by the TeX engine when it occurs
in the so called "vertical mode" (e.g., after an alignment environment).
The old ---Separator--- layout is now gone and old layout files using it
are also automatically converted.
Round trip conversions between old and new format should leave a document
unchanged. This means that the new behavior about paragraph breaking is
not "carried back" to the old format. Indeed, this would need introducing
special LaTeX commands in ERT that would accumulate in roundtrip
conversions, horribly cluttering the document. So, when converting a
modified document to old formats, the LaTeX output may slightly differ in
vertical spacing if the document is processed by an old version of LyX.
In other words, forward compatibility is guaranteed, but not backwards.
2014-05-10 21:25:11 +00:00
|
|
|
|| par.isEnvSeparator(cur.pos() - 2)
|
2004-02-13 07:30:59 +00:00
|
|
|
|| par.isNewline(cur.pos() - 2))
|
2002-02-16 15:59:55 +00:00
|
|
|
) {
|
2009-08-09 15:29:34 +00:00
|
|
|
setCharFont(pit, cur.pos() - 1, cur.current_font,
|
2007-09-02 21:48:49 +00:00
|
|
|
tm.font_);
|
2020-07-10 15:17:15 +00:00
|
|
|
} else if (isCommonNumberSeparator(c)
|
2004-02-13 07:30:59 +00:00
|
|
|
&& cur.pos() >= 2
|
2008-02-27 23:03:26 +00:00
|
|
|
&& tm.displayFont(pit, cur.pos() - 2).fontInfo().number() == FONT_ON) {
|
2009-08-09 15:29:34 +00:00
|
|
|
setCharFont(pit, cur.pos() - 1, cur.current_font,
|
2007-09-02 21:48:49 +00:00
|
|
|
tm.font_);
|
2000-10-09 12:30:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-06-08 14:24:57 +00:00
|
|
|
// In Bidi text, we want spaces to be treated in a special way: spaces
|
2014-03-08 10:59:39 +00:00
|
|
|
// which are between words in different languages should get the
|
|
|
|
// paragraph's language; otherwise, spaces should keep the language
|
2007-06-08 14:24:57 +00:00
|
|
|
// they were originally typed in. This is only in effect while typing;
|
|
|
|
// after the text is already typed in, the user can always go back and
|
|
|
|
// explicitly set the language of a space as desired. But 99.9% of the
|
|
|
|
// time, what we're doing here is what the user actually meant.
|
2014-03-08 10:59:39 +00:00
|
|
|
//
|
2007-06-08 14:24:57 +00:00
|
|
|
// The following cases are the ones in which the language of the space
|
|
|
|
// should be changed to match that of the containing paragraph. In the
|
2014-03-08 10:59:39 +00:00
|
|
|
// depictions, lowercase is LTR, uppercase is RTL, underscore (_)
|
2007-06-08 14:24:57 +00:00
|
|
|
// represents a space, pipe (|) represents the cursor position (so the
|
|
|
|
// character before it is the one just typed in). The different cases
|
|
|
|
// are depicted logically (not visually), from left to right:
|
2014-03-08 10:59:39 +00:00
|
|
|
//
|
2007-06-08 14:24:57 +00:00
|
|
|
// 1. A_a|
|
|
|
|
// 2. a_A|
|
|
|
|
//
|
|
|
|
// Theoretically, there are other situations that we should, perhaps, deal
|
2014-03-08 10:59:39 +00:00
|
|
|
// with (e.g.: a|_A, A|_a). In practice, though, there really isn't any
|
2007-06-08 14:24:57 +00:00
|
|
|
// point (to understand why, just try to create this situation...).
|
|
|
|
|
|
|
|
if ((cur.pos() >= 2) && (par.isLineSeparator(cur.pos() - 1))) {
|
2014-03-08 10:59:39 +00:00
|
|
|
// get font in front and behind the space in question. But do NOT
|
2007-06-08 14:24:57 +00:00
|
|
|
// use getFont(cur.pos()) because the character c is not inserted yet
|
2008-02-27 23:03:26 +00:00
|
|
|
Font const pre_space_font = tm.displayFont(cur.pit(), cur.pos() - 2);
|
2007-09-02 13:35:48 +00:00
|
|
|
Font const & post_space_font = cur.real_current_font;
|
2007-06-08 14:24:57 +00:00
|
|
|
bool pre_space_rtl = pre_space_font.isVisibleRightToLeft();
|
|
|
|
bool post_space_rtl = post_space_font.isVisibleRightToLeft();
|
2014-03-08 10:59:39 +00:00
|
|
|
|
2007-06-08 14:24:57 +00:00
|
|
|
if (pre_space_rtl != post_space_rtl) {
|
2014-03-08 10:59:39 +00:00
|
|
|
// Set the space's language to match the language of the
|
2007-06-08 14:24:57 +00:00
|
|
|
// adjacent character whose direction is the paragraph's
|
|
|
|
// direction; don't touch other properties of the font
|
2014-03-08 10:59:39 +00:00
|
|
|
Language const * lang =
|
2007-09-04 10:27:55 +00:00
|
|
|
(pre_space_rtl == par.isRTL(buffer.params())) ?
|
2007-06-08 14:24:57 +00:00
|
|
|
pre_space_font.language() : post_space_font.language();
|
|
|
|
|
2008-02-27 23:03:26 +00:00
|
|
|
Font space_font = tm.displayFont(cur.pit(), cur.pos() - 1);
|
2007-06-08 14:24:57 +00:00
|
|
|
space_font.setLanguage(lang);
|
|
|
|
par.setFont(cur.pos() - 1, space_font);
|
|
|
|
}
|
|
|
|
}
|
2014-03-08 10:59:39 +00:00
|
|
|
|
2015-02-24 20:58:27 +00:00
|
|
|
pos_type pos = cur.pos();
|
|
|
|
if (!cur.paragraph().isPassThru() && owner_->lyxCode() != IPA_CODE &&
|
2016-02-25 14:00:13 +00:00
|
|
|
cur.real_current_font.fontInfo().family() != TYPEWRITER_FAMILY &&
|
2015-02-24 20:58:27 +00:00
|
|
|
c == '-' && pos > 0) {
|
|
|
|
if (par.getChar(pos - 1) == '-') {
|
|
|
|
// convert "--" to endash
|
|
|
|
par.eraseChar(pos - 1, cur.buffer()->params().track_changes);
|
|
|
|
c = 0x2013;
|
|
|
|
pos--;
|
|
|
|
} else if (par.getChar(pos - 1) == 0x2013) {
|
|
|
|
// convert "---" to emdash
|
|
|
|
par.eraseChar(pos - 1, cur.buffer()->params().track_changes);
|
|
|
|
c = 0x2014;
|
|
|
|
pos--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
par.insertChar(pos, c, cur.current_font,
|
2014-03-29 22:52:36 +00:00
|
|
|
cur.buffer()->params().track_changes);
|
2008-01-12 21:38:51 +00:00
|
|
|
cur.checkBufferStructure();
|
2006-11-12 14:47:20 +00:00
|
|
|
|
2010-07-08 20:04:35 +00:00
|
|
|
// cur.screenUpdateFlags(Update::Force);
|
2009-10-29 21:02:19 +00:00
|
|
|
bool boundary = cur.boundary()
|
2015-02-24 20:58:27 +00:00
|
|
|
|| tm.isRTLBoundary(cur.pit(), pos + 1);
|
|
|
|
setCursor(cur, cur.pit(), pos + 1, false, boundary);
|
2007-10-18 11:51:17 +00:00
|
|
|
charInserted(cur);
|
1999-12-13 00:05:34 +00:00
|
|
|
}
|
2002-03-21 17:27:08 +00:00
|
|
|
|
1999-12-13 00:05:34 +00:00
|
|
|
|
2007-10-18 11:51:17 +00:00
|
|
|
void Text::charInserted(Cursor & cur)
|
1999-12-13 00:05:34 +00:00
|
|
|
{
|
2008-02-21 19:42:34 +00:00
|
|
|
Paragraph & par = cur.paragraph();
|
|
|
|
|
|
|
|
// register word if a non-letter was entered
|
|
|
|
if (cur.pos() > 1
|
2009-06-21 14:30:57 +00:00
|
|
|
&& !par.isWordSeparator(cur.pos() - 2)
|
|
|
|
&& par.isWordSeparator(cur.pos() - 1)) {
|
2008-02-21 19:42:34 +00:00
|
|
|
// get the word in front of cursor
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(this == cur.text());
|
2009-05-01 09:56:20 +00:00
|
|
|
cur.paragraph().updateWords();
|
2008-02-21 19:42:34 +00:00
|
|
|
}
|
1999-09-27 18:44:28 +00:00
|
|
|
}
|
1999-12-13 00:05:34 +00:00
|
|
|
|
2000-06-28 13:35:52 +00:00
|
|
|
|
2002-05-29 12:58:23 +00:00
|
|
|
// the cursor set functions have a special mechanism. When they
|
|
|
|
// realize, that you left an empty paragraph, they will delete it.
|
1999-09-27 18:44:28 +00:00
|
|
|
|
2007-10-28 20:35:57 +00:00
|
|
|
bool Text::cursorForwardOneWord(Cursor & cur)
|
1999-09-27 18:44:28 +00:00
|
|
|
{
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(this == cur.text());
|
2005-02-08 02:06:39 +00:00
|
|
|
|
2008-06-24 09:19:52 +00:00
|
|
|
pos_type const lastpos = cur.lastpos();
|
|
|
|
pit_type pit = cur.pit();
|
|
|
|
pos_type pos = cur.pos();
|
|
|
|
Paragraph const & par = cur.paragraph();
|
|
|
|
|
2008-06-30 12:45:27 +00:00
|
|
|
// Paragraph boundary is a word boundary
|
2016-04-21 21:44:14 +00:00
|
|
|
if (pos == lastpos || (pos + 1 == lastpos && par.isEnvSeparator(pos))) {
|
2008-07-01 14:42:32 +00:00
|
|
|
if (pit != cur.lastpit())
|
|
|
|
return setCursor(cur, pit + 1, 0);
|
|
|
|
else
|
|
|
|
return false;
|
2008-07-01 15:12:28 +00:00
|
|
|
}
|
2008-07-01 14:42:32 +00:00
|
|
|
|
2013-05-25 16:02:21 +00:00
|
|
|
if (lyxrc.mac_like_cursor_movement) {
|
2008-07-01 14:42:32 +00:00
|
|
|
// Skip through trailing punctuation and spaces.
|
2008-07-08 09:19:41 +00:00
|
|
|
while (pos != lastpos && (par.isChar(pos) || par.isSpace(pos)))
|
2015-05-17 15:27:12 +00:00
|
|
|
++pos;
|
2008-06-30 12:45:27 +00:00
|
|
|
|
2008-07-01 14:42:32 +00:00
|
|
|
// Skip over either a non-char inset or a full word
|
2009-06-21 14:30:57 +00:00
|
|
|
if (pos != lastpos && par.isWordSeparator(pos))
|
2008-07-01 14:42:32 +00:00
|
|
|
++pos;
|
2009-06-21 14:30:57 +00:00
|
|
|
else while (pos != lastpos && !par.isWordSeparator(pos))
|
2008-07-01 14:42:32 +00:00
|
|
|
++pos;
|
|
|
|
} else {
|
2013-04-25 21:27:10 +00:00
|
|
|
LASSERT(pos < lastpos, return false); // see above
|
2009-06-21 14:30:57 +00:00
|
|
|
if (!par.isWordSeparator(pos))
|
|
|
|
while (pos != lastpos && !par.isWordSeparator(pos))
|
2008-07-01 14:42:32 +00:00
|
|
|
++pos;
|
|
|
|
else if (par.isChar(pos))
|
|
|
|
while (pos != lastpos && par.isChar(pos))
|
|
|
|
++pos;
|
|
|
|
else if (!par.isSpace(pos)) // non-char inset
|
2008-06-24 09:19:52 +00:00
|
|
|
++pos;
|
|
|
|
|
2008-07-01 14:42:32 +00:00
|
|
|
// Skip over white space
|
|
|
|
while (pos != lastpos && par.isSpace(pos))
|
2014-03-08 10:59:39 +00:00
|
|
|
++pos;
|
2008-07-01 14:42:32 +00:00
|
|
|
}
|
2008-06-24 09:19:52 +00:00
|
|
|
|
2016-04-21 21:44:14 +00:00
|
|
|
// Don't skip a separator inset at the end of a paragraph
|
|
|
|
if (pos == lastpos && pos && par.isEnvSeparator(pos - 1))
|
|
|
|
--pos;
|
|
|
|
|
2008-06-24 09:19:52 +00:00
|
|
|
return setCursor(cur, pit, pos);
|
1999-09-27 18:44:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-10-28 20:35:57 +00:00
|
|
|
bool Text::cursorBackwardOneWord(Cursor & cur)
|
1999-09-27 18:44:28 +00:00
|
|
|
{
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(this == cur.text());
|
2005-02-08 02:06:39 +00:00
|
|
|
|
2008-06-24 09:19:52 +00:00
|
|
|
pit_type pit = cur.pit();
|
|
|
|
pos_type pos = cur.pos();
|
|
|
|
Paragraph & par = cur.paragraph();
|
|
|
|
|
2008-06-30 12:45:27 +00:00
|
|
|
// Paragraph boundary is a word boundary
|
2016-04-21 21:44:14 +00:00
|
|
|
if (pos == 0 && pit != 0) {
|
|
|
|
Paragraph & prevpar = getPar(pit - 1);
|
|
|
|
pos = prevpar.size();
|
|
|
|
// Don't stop after an environment separator
|
|
|
|
if (pos && prevpar.isEnvSeparator(pos - 1))
|
|
|
|
--pos;
|
|
|
|
return setCursor(cur, pit - 1, pos);
|
|
|
|
}
|
2008-06-30 12:45:27 +00:00
|
|
|
|
2013-05-25 16:02:21 +00:00
|
|
|
if (lyxrc.mac_like_cursor_movement) {
|
2008-07-08 09:24:28 +00:00
|
|
|
// Skip through punctuation and spaces.
|
2008-07-08 09:19:41 +00:00
|
|
|
while (pos != 0 && (par.isChar(pos - 1) || par.isSpace(pos - 1)))
|
2008-07-01 14:42:32 +00:00
|
|
|
--pos;
|
2005-02-08 02:06:39 +00:00
|
|
|
|
2008-07-01 14:42:32 +00:00
|
|
|
// Skip over either a non-char inset or a full word
|
2009-06-21 14:30:57 +00:00
|
|
|
if (pos != 0 && par.isWordSeparator(pos - 1) && !par.isChar(pos - 1))
|
2008-07-01 14:42:32 +00:00
|
|
|
--pos;
|
2009-06-21 14:30:57 +00:00
|
|
|
else while (pos != 0 && !par.isWordSeparator(pos - 1))
|
2008-07-01 14:42:32 +00:00
|
|
|
--pos;
|
|
|
|
} else {
|
|
|
|
// Skip over white space
|
|
|
|
while (pos != 0 && par.isSpace(pos - 1))
|
|
|
|
--pos;
|
|
|
|
|
2009-06-21 14:30:57 +00:00
|
|
|
if (pos != 0 && !par.isWordSeparator(pos - 1))
|
|
|
|
while (pos != 0 && !par.isWordSeparator(pos - 1))
|
2008-07-01 14:42:32 +00:00
|
|
|
--pos;
|
|
|
|
else if (pos != 0 && par.isChar(pos - 1))
|
|
|
|
while (pos != 0 && par.isChar(pos - 1))
|
|
|
|
--pos;
|
|
|
|
else if (pos != 0 && !par.isSpace(pos - 1)) // non-char inset
|
2008-06-24 09:19:52 +00:00
|
|
|
--pos;
|
2008-07-01 14:42:32 +00:00
|
|
|
}
|
2008-06-24 09:19:52 +00:00
|
|
|
|
|
|
|
return setCursor(cur, pit, pos);
|
1999-09-27 18:44:28 +00:00
|
|
|
}
|
|
|
|
|
2002-05-29 12:58:23 +00:00
|
|
|
|
2008-05-04 20:22:19 +00:00
|
|
|
bool Text::cursorVisLeftOneWord(Cursor & cur)
|
|
|
|
{
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(this == cur.text());
|
2008-05-04 20:22:19 +00:00
|
|
|
|
|
|
|
pos_type left_pos, right_pos;
|
|
|
|
|
|
|
|
Cursor temp_cur = cur;
|
|
|
|
|
|
|
|
// always try to move at least once...
|
|
|
|
while (temp_cur.posVisLeft(true /* skip_inset */)) {
|
|
|
|
|
|
|
|
// collect some information about current cursor position
|
|
|
|
temp_cur.getSurroundingPos(left_pos, right_pos);
|
2015-09-20 08:42:35 +00:00
|
|
|
bool left_is_letter =
|
2009-06-21 14:30:57 +00:00
|
|
|
(left_pos > -1 ? !temp_cur.paragraph().isWordSeparator(left_pos) : false);
|
2015-09-20 08:42:35 +00:00
|
|
|
bool right_is_letter =
|
2009-06-21 14:30:57 +00:00
|
|
|
(right_pos > -1 ? !temp_cur.paragraph().isWordSeparator(right_pos) : false);
|
2008-05-04 20:22:19 +00:00
|
|
|
|
|
|
|
// if we're not at a letter/non-letter boundary, continue moving
|
|
|
|
if (left_is_letter == right_is_letter)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// we should stop when we have an LTR word on our right or an RTL word
|
|
|
|
// on our left
|
|
|
|
if ((left_is_letter && temp_cur.paragraph().getFontSettings(
|
2008-11-17 11:46:07 +00:00
|
|
|
temp_cur.buffer()->params(), left_pos).isRightToLeft())
|
2008-05-04 20:22:19 +00:00
|
|
|
|| (right_is_letter && !temp_cur.paragraph().getFontSettings(
|
2008-11-17 11:46:07 +00:00
|
|
|
temp_cur.buffer()->params(), right_pos).isRightToLeft()))
|
2008-05-04 20:22:19 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-03-08 10:59:39 +00:00
|
|
|
return setCursor(cur, temp_cur.pit(), temp_cur.pos(),
|
2008-05-04 20:22:19 +00:00
|
|
|
true, temp_cur.boundary());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Text::cursorVisRightOneWord(Cursor & cur)
|
|
|
|
{
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(this == cur.text());
|
2008-05-04 20:22:19 +00:00
|
|
|
|
|
|
|
pos_type left_pos, right_pos;
|
|
|
|
|
|
|
|
Cursor temp_cur = cur;
|
|
|
|
|
|
|
|
// always try to move at least once...
|
|
|
|
while (temp_cur.posVisRight(true /* skip_inset */)) {
|
|
|
|
|
|
|
|
// collect some information about current cursor position
|
|
|
|
temp_cur.getSurroundingPos(left_pos, right_pos);
|
2015-09-20 08:42:35 +00:00
|
|
|
bool left_is_letter =
|
2009-06-21 14:30:57 +00:00
|
|
|
(left_pos > -1 ? !temp_cur.paragraph().isWordSeparator(left_pos) : false);
|
2015-09-20 08:42:35 +00:00
|
|
|
bool right_is_letter =
|
2009-06-21 14:30:57 +00:00
|
|
|
(right_pos > -1 ? !temp_cur.paragraph().isWordSeparator(right_pos) : false);
|
2008-05-04 20:22:19 +00:00
|
|
|
|
|
|
|
// if we're not at a letter/non-letter boundary, continue moving
|
|
|
|
if (left_is_letter == right_is_letter)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// we should stop when we have an LTR word on our right or an RTL word
|
|
|
|
// on our left
|
|
|
|
if ((left_is_letter && temp_cur.paragraph().getFontSettings(
|
2014-03-08 10:59:39 +00:00
|
|
|
temp_cur.buffer()->params(),
|
2008-05-04 20:22:19 +00:00
|
|
|
left_pos).isRightToLeft())
|
|
|
|
|| (right_is_letter && !temp_cur.paragraph().getFontSettings(
|
2014-03-08 10:59:39 +00:00
|
|
|
temp_cur.buffer()->params(),
|
2008-05-04 20:22:19 +00:00
|
|
|
right_pos).isRightToLeft()))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-03-08 10:59:39 +00:00
|
|
|
return setCursor(cur, temp_cur.pit(), temp_cur.pos(),
|
2008-05-04 20:22:19 +00:00
|
|
|
true, temp_cur.boundary());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-29 23:33:02 +00:00
|
|
|
void Text::selectWord(Cursor & cur, word_location loc)
|
2001-06-27 14:10:35 +00:00
|
|
|
{
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(this == cur.text());
|
2004-03-01 17:12:09 +00:00
|
|
|
CursorSlice from = cur.top();
|
2015-12-19 02:58:22 +00:00
|
|
|
CursorSlice to;
|
2003-11-11 10:08:35 +00:00
|
|
|
getWord(from, to, loc);
|
2004-03-01 17:12:09 +00:00
|
|
|
if (cur.top() != from)
|
2004-11-24 21:53:46 +00:00
|
|
|
setCursor(cur, from.pit(), from.pos());
|
2001-07-27 16:07:33 +00:00
|
|
|
if (to == from)
|
|
|
|
return;
|
2009-12-18 14:18:10 +00:00
|
|
|
if (!cur.selection())
|
|
|
|
cur.resetAnchor();
|
2004-11-24 21:53:46 +00:00
|
|
|
setCursor(cur, to.pit(), to.pos());
|
2004-02-13 07:30:59 +00:00
|
|
|
cur.setSelection();
|
2009-12-18 14:18:10 +00:00
|
|
|
cur.setWordSelection(true);
|
1999-09-27 18:44:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-02-07 15:24:47 +00:00
|
|
|
void Text::selectAll(Cursor & cur)
|
|
|
|
{
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(this == cur.text());
|
2009-02-07 15:24:47 +00:00
|
|
|
if (cur.lastpos() == 0 && cur.lastpit() == 0)
|
|
|
|
return;
|
|
|
|
// If the cursor is at the beginning, make sure the cursor ends there
|
|
|
|
if (cur.pit() == 0 && cur.pos() == 0) {
|
|
|
|
setCursor(cur, cur.lastpit(), getPar(cur.lastpit()).size());
|
|
|
|
cur.resetAnchor();
|
2014-03-08 10:59:39 +00:00
|
|
|
setCursor(cur, 0, 0);
|
2009-02-07 15:24:47 +00:00
|
|
|
} else {
|
|
|
|
setCursor(cur, 0, 0);
|
|
|
|
cur.resetAnchor();
|
|
|
|
setCursor(cur, cur.lastpit(), getPar(cur.lastpit()).size());
|
|
|
|
}
|
|
|
|
cur.setSelection();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-05-29 12:58:23 +00:00
|
|
|
// Select the word currently under the cursor when no
|
2002-06-24 20:28:12 +00:00
|
|
|
// selection is currently set
|
2007-04-29 23:33:02 +00:00
|
|
|
bool Text::selectWordWhenUnderCursor(Cursor & cur, word_location loc)
|
1999-09-27 18:44:28 +00:00
|
|
|
{
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(this == cur.text());
|
2004-02-13 07:30:59 +00:00
|
|
|
if (cur.selection())
|
2004-01-20 14:25:24 +00:00
|
|
|
return false;
|
2004-02-13 07:30:59 +00:00
|
|
|
selectWord(cur, loc);
|
|
|
|
return cur.selection();
|
1999-09-27 18:44:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-29 23:33:02 +00:00
|
|
|
void Text::acceptOrRejectChanges(Cursor & cur, ChangeOp op)
|
2003-02-08 19:18:01 +00:00
|
|
|
{
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(this == cur.text());
|
2006-11-25 23:30:58 +00:00
|
|
|
|
2009-05-01 17:06:36 +00:00
|
|
|
if (!cur.selection()) {
|
2014-10-14 07:58:40 +00:00
|
|
|
if (!selectChange(cur))
|
2009-05-01 17:06:36 +00:00
|
|
|
return;
|
|
|
|
}
|
2003-02-08 19:18:01 +00:00
|
|
|
|
2007-10-18 11:51:17 +00:00
|
|
|
cur.recordUndoSelection();
|
2006-11-25 23:30:58 +00:00
|
|
|
|
2007-01-23 21:53:16 +00:00
|
|
|
pit_type begPit = cur.selectionBegin().pit();
|
|
|
|
pit_type endPit = cur.selectionEnd().pit();
|
2006-04-05 23:56:29 +00:00
|
|
|
|
2007-01-23 21:53:16 +00:00
|
|
|
pos_type begPos = cur.selectionBegin().pos();
|
|
|
|
pos_type endPos = cur.selectionEnd().pos();
|
|
|
|
|
|
|
|
// keep selection info, because endPos becomes invalid after the first loop
|
|
|
|
bool endsBeforeEndOfPar = (endPos < pars_[endPit].size());
|
|
|
|
|
|
|
|
// first, accept/reject changes within each individual paragraph (do not consider end-of-par)
|
|
|
|
for (pit_type pit = begPit; pit <= endPit; ++pit) {
|
|
|
|
pos_type parSize = pars_[pit].size();
|
|
|
|
|
|
|
|
// ignore empty paragraphs; otherwise, an assertion will fail for
|
|
|
|
// acceptChanges(bparams, 0, 0) or rejectChanges(bparams, 0, 0)
|
|
|
|
if (parSize == 0)
|
2007-01-21 21:20:51 +00:00
|
|
|
continue;
|
2006-11-26 14:49:35 +00:00
|
|
|
|
2007-01-21 21:20:51 +00:00
|
|
|
// do not consider first paragraph if the cursor starts at pos size()
|
2007-01-23 21:53:16 +00:00
|
|
|
if (pit == begPit && begPos == parSize)
|
2007-01-21 21:20:51 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
// do not consider last paragraph if the cursor ends at pos 0
|
2007-05-28 22:27:45 +00:00
|
|
|
if (pit == endPit && endPos == 0)
|
2007-01-21 21:20:51 +00:00
|
|
|
break; // last iteration anyway
|
|
|
|
|
2007-01-23 21:53:16 +00:00
|
|
|
pos_type left = (pit == begPit ? begPos : 0);
|
|
|
|
pos_type right = (pit == endPit ? endPos : parSize);
|
2014-03-08 10:59:39 +00:00
|
|
|
|
2011-06-29 14:22:04 +00:00
|
|
|
if (left == right)
|
|
|
|
// there is no change here
|
|
|
|
continue;
|
2014-03-08 10:59:39 +00:00
|
|
|
|
2007-01-25 21:18:36 +00:00
|
|
|
if (op == ACCEPT) {
|
2009-07-16 08:37:32 +00:00
|
|
|
pars_[pit].acceptChanges(left, right);
|
2007-01-23 21:53:16 +00:00
|
|
|
} else {
|
2009-07-16 08:37:32 +00:00
|
|
|
pars_[pit].rejectChanges(left, right);
|
2007-01-23 21:53:16 +00:00
|
|
|
}
|
2007-01-21 21:20:51 +00:00
|
|
|
}
|
2007-05-28 22:27:45 +00:00
|
|
|
|
2007-01-23 21:53:16 +00:00
|
|
|
// next, accept/reject imaginary end-of-par characters
|
2007-05-28 22:27:45 +00:00
|
|
|
|
2007-01-23 21:53:16 +00:00
|
|
|
for (pit_type pit = begPit; pit <= endPit; ++pit) {
|
2007-01-21 21:20:51 +00:00
|
|
|
pos_type pos = pars_[pit].size();
|
|
|
|
|
2007-01-23 21:53:16 +00:00
|
|
|
// skip if the selection ends before the end-of-par
|
|
|
|
if (pit == endPit && endsBeforeEndOfPar)
|
|
|
|
break; // last iteration anyway
|
2007-01-21 21:20:51 +00:00
|
|
|
|
2007-01-23 21:53:16 +00:00
|
|
|
// skip if this is not the last paragraph of the document
|
|
|
|
// note: the user should be able to accept/reject the par break of the last par!
|
2007-04-29 13:39:47 +00:00
|
|
|
if (pit == endPit && pit + 1 != int(pars_.size()))
|
2007-01-23 21:53:16 +00:00
|
|
|
break; // last iteration anway
|
2007-01-21 21:20:51 +00:00
|
|
|
|
2007-01-25 21:18:36 +00:00
|
|
|
if (op == ACCEPT) {
|
2007-01-21 21:20:51 +00:00
|
|
|
if (pars_[pit].isInserted(pos)) {
|
|
|
|
pars_[pit].setChange(pos, Change(Change::UNCHANGED));
|
2007-01-23 21:53:16 +00:00
|
|
|
} else if (pars_[pit].isDeleted(pos)) {
|
2007-04-29 13:39:47 +00:00
|
|
|
if (pit + 1 == int(pars_.size())) {
|
2007-01-23 21:53:16 +00:00
|
|
|
// we cannot remove a par break at the end of the last paragraph;
|
2007-01-21 21:20:51 +00:00
|
|
|
// instead, we mark it unchanged
|
|
|
|
pars_[pit].setChange(pos, Change(Change::UNCHANGED));
|
|
|
|
} else {
|
2008-11-17 11:46:07 +00:00
|
|
|
mergeParagraph(cur.buffer()->params(), pars_, pit);
|
2007-01-23 21:53:16 +00:00
|
|
|
--endPit;
|
|
|
|
--pit;
|
2007-01-21 21:20:51 +00:00
|
|
|
}
|
2007-01-04 23:02:37 +00:00
|
|
|
}
|
2007-01-23 21:53:16 +00:00
|
|
|
} else {
|
2007-01-23 13:25:50 +00:00
|
|
|
if (pars_[pit].isDeleted(pos)) {
|
|
|
|
pars_[pit].setChange(pos, Change(Change::UNCHANGED));
|
2007-01-23 21:53:16 +00:00
|
|
|
} else if (pars_[pit].isInserted(pos)) {
|
2007-04-29 13:39:47 +00:00
|
|
|
if (pit + 1 == int(pars_.size())) {
|
2007-01-23 21:53:16 +00:00
|
|
|
// we mark the par break at the end of the last paragraph unchanged
|
2007-01-23 13:25:50 +00:00
|
|
|
pars_[pit].setChange(pos, Change(Change::UNCHANGED));
|
|
|
|
} else {
|
2008-11-17 11:46:07 +00:00
|
|
|
mergeParagraph(cur.buffer()->params(), pars_, pit);
|
2007-01-23 21:53:16 +00:00
|
|
|
--endPit;
|
|
|
|
--pit;
|
2007-01-23 13:25:50 +00:00
|
|
|
}
|
2007-01-04 23:02:37 +00:00
|
|
|
}
|
|
|
|
}
|
2006-03-11 13:31:41 +00:00
|
|
|
}
|
2007-01-23 13:25:50 +00:00
|
|
|
|
|
|
|
// finally, invoke the DEPM
|
2014-03-29 22:52:36 +00:00
|
|
|
deleteEmptyParagraphMechanism(begPit, endPit, cur.buffer()->params().track_changes);
|
2007-01-25 21:18:36 +00:00
|
|
|
|
2007-10-18 11:51:17 +00:00
|
|
|
cur.finishUndo();
|
2006-03-11 13:31:41 +00:00
|
|
|
cur.clearSelection();
|
2007-01-23 21:53:16 +00:00
|
|
|
setCursorIntern(cur, begPit, begPos);
|
2010-07-08 20:04:35 +00:00
|
|
|
cur.screenUpdateFlags(Update::Force);
|
2010-07-09 14:37:00 +00:00
|
|
|
cur.forceBufferUpdate();
|
2003-02-08 19:18:01 +00:00
|
|
|
}
|
|
|
|
|
2003-03-03 23:19:01 +00:00
|
|
|
|
2009-08-09 15:52:33 +00:00
|
|
|
void Text::acceptChanges()
|
2007-01-25 22:28:15 +00:00
|
|
|
{
|
2009-08-09 15:52:33 +00:00
|
|
|
BufferParams const & bparams = owner_->buffer().params();
|
2007-06-26 14:11:42 +00:00
|
|
|
lyx::acceptChanges(pars_, bparams);
|
2014-03-29 22:52:36 +00:00
|
|
|
deleteEmptyParagraphMechanism(0, pars_.size() - 1, bparams.track_changes);
|
2007-01-25 22:28:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-09 15:52:33 +00:00
|
|
|
void Text::rejectChanges()
|
2007-01-25 22:28:15 +00:00
|
|
|
{
|
2009-08-09 15:52:33 +00:00
|
|
|
BufferParams const & bparams = owner_->buffer().params();
|
2007-02-20 17:27:06 +00:00
|
|
|
pit_type pars_size = static_cast<pit_type>(pars_.size());
|
2007-01-25 22:28:15 +00:00
|
|
|
|
|
|
|
// first, reject changes within each individual paragraph
|
2007-05-28 22:27:45 +00:00
|
|
|
// (do not consider end-of-par)
|
2007-01-25 22:28:15 +00:00
|
|
|
for (pit_type pit = 0; pit < pars_size; ++pit) {
|
2007-05-28 22:27:45 +00:00
|
|
|
if (!pars_[pit].empty()) // prevent assertion failure
|
2009-07-16 08:37:32 +00:00
|
|
|
pars_[pit].rejectChanges(0, pars_[pit].size());
|
2007-01-25 22:28:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// next, reject imaginary end-of-par characters
|
|
|
|
for (pit_type pit = 0; pit < pars_size; ++pit) {
|
|
|
|
pos_type pos = pars_[pit].size();
|
|
|
|
|
|
|
|
if (pars_[pit].isDeleted(pos)) {
|
|
|
|
pars_[pit].setChange(pos, Change(Change::UNCHANGED));
|
|
|
|
} else if (pars_[pit].isInserted(pos)) {
|
|
|
|
if (pit == pars_size - 1) {
|
|
|
|
// we mark the par break at the end of the last
|
|
|
|
// paragraph unchanged
|
|
|
|
pars_[pit].setChange(pos, Change(Change::UNCHANGED));
|
|
|
|
} else {
|
|
|
|
mergeParagraph(bparams, pars_, pit);
|
|
|
|
--pit;
|
|
|
|
--pars_size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// finally, invoke the DEPM
|
2014-03-29 22:52:36 +00:00
|
|
|
deleteEmptyParagraphMechanism(0, pars_size - 1, bparams.track_changes);
|
2007-01-25 22:28:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-18 18:12:55 +00:00
|
|
|
void Text::deleteWordForward(Cursor & cur, bool const force)
|
1999-09-27 18:44:28 +00:00
|
|
|
{
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(this == cur.text());
|
2004-02-12 16:36:01 +00:00
|
|
|
if (cur.lastpos() == 0)
|
2007-10-22 22:18:52 +00:00
|
|
|
cursorForward(cur);
|
1999-09-27 18:44:28 +00:00
|
|
|
else {
|
2004-01-20 14:25:24 +00:00
|
|
|
cur.resetAnchor();
|
2016-02-28 16:36:29 +00:00
|
|
|
cur.selection(true);
|
2007-10-28 20:35:57 +00:00
|
|
|
cursorForwardOneWord(cur);
|
2004-01-20 14:25:24 +00:00
|
|
|
cur.setSelection();
|
2017-02-18 18:12:55 +00:00
|
|
|
if (force || !cur.confirmDeletion()) {
|
2018-07-22 20:18:50 +00:00
|
|
|
cutSelection(cur, false);
|
2017-02-18 18:12:55 +00:00
|
|
|
cur.checkBufferStructure();
|
|
|
|
}
|
1999-09-27 18:44:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-18 18:12:55 +00:00
|
|
|
void Text::deleteWordBackward(Cursor & cur, bool const force)
|
1999-09-27 18:44:28 +00:00
|
|
|
{
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(this == cur.text());
|
2004-02-12 16:36:01 +00:00
|
|
|
if (cur.lastpos() == 0)
|
2007-10-22 22:18:52 +00:00
|
|
|
cursorBackward(cur);
|
2002-05-29 12:58:23 +00:00
|
|
|
else {
|
2004-01-20 14:25:24 +00:00
|
|
|
cur.resetAnchor();
|
2016-02-28 16:36:29 +00:00
|
|
|
cur.selection(true);
|
2007-10-28 20:35:57 +00:00
|
|
|
cursorBackwardOneWord(cur);
|
2004-01-20 14:25:24 +00:00
|
|
|
cur.setSelection();
|
2017-02-18 18:12:55 +00:00
|
|
|
if (force || !cur.confirmDeletion()) {
|
2018-07-22 20:18:50 +00:00
|
|
|
cutSelection(cur, false);
|
2017-02-18 18:12:55 +00:00
|
|
|
cur.checkBufferStructure();
|
|
|
|
}
|
2002-05-29 12:58:23 +00:00
|
|
|
}
|
1999-09-27 18:44:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-06-24 20:28:12 +00:00
|
|
|
// Kill to end of line.
|
2015-05-28 13:43:12 +00:00
|
|
|
void Text::changeCase(Cursor & cur, TextCase action, bool partial)
|
2000-02-10 17:53:36 +00:00
|
|
|
{
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(this == cur.text());
|
2004-01-13 14:13:51 +00:00
|
|
|
CursorSlice from;
|
|
|
|
CursorSlice to;
|
2001-03-09 00:56:42 +00:00
|
|
|
|
2015-05-28 13:43:12 +00:00
|
|
|
bool const gotsel = cur.selection();
|
|
|
|
if (gotsel) {
|
2004-01-26 10:13:15 +00:00
|
|
|
from = cur.selBegin();
|
2004-01-20 14:25:24 +00:00
|
|
|
to = cur.selEnd();
|
2001-06-27 14:10:35 +00:00
|
|
|
} else {
|
2004-03-18 13:28:49 +00:00
|
|
|
from = cur.top();
|
2015-05-28 13:43:12 +00:00
|
|
|
getWord(from, to, partial ? PARTIAL_WORD : WHOLE_WORD);
|
2007-10-28 20:35:57 +00:00
|
|
|
cursorForwardOneWord(cur);
|
2001-06-27 14:10:35 +00:00
|
|
|
}
|
2001-03-09 00:56:42 +00:00
|
|
|
|
2007-10-18 11:51:17 +00:00
|
|
|
cur.recordUndoSelection();
|
2001-06-27 15:33:55 +00:00
|
|
|
|
2007-03-25 17:11:30 +00:00
|
|
|
pit_type begPit = from.pit();
|
|
|
|
pit_type endPit = to.pit();
|
2001-06-27 18:29:18 +00:00
|
|
|
|
2007-03-25 17:11:30 +00:00
|
|
|
pos_type begPos = from.pos();
|
|
|
|
pos_type endPos = to.pos();
|
|
|
|
|
2007-03-27 12:51:47 +00:00
|
|
|
pos_type right = 0; // needed after the for loop
|
2007-03-25 17:11:30 +00:00
|
|
|
|
|
|
|
for (pit_type pit = begPit; pit <= endPit; ++pit) {
|
2007-10-24 07:32:25 +00:00
|
|
|
Paragraph & par = pars_[pit];
|
2007-10-24 08:32:20 +00:00
|
|
|
pos_type const pos = (pit == begPit ? begPos : 0);
|
|
|
|
right = (pit == endPit ? endPos : par.size());
|
2008-11-17 11:46:07 +00:00
|
|
|
par.changeCase(cur.buffer()->params(), pos, right, action);
|
2001-06-27 15:33:55 +00:00
|
|
|
}
|
2007-03-25 17:11:30 +00:00
|
|
|
|
|
|
|
// the selection may have changed due to logically-only deleted chars
|
2008-01-28 10:50:24 +00:00
|
|
|
if (gotsel) {
|
|
|
|
setCursor(cur, begPit, begPos);
|
|
|
|
cur.resetAnchor();
|
|
|
|
setCursor(cur, endPit, right);
|
|
|
|
cur.setSelection();
|
|
|
|
} else
|
|
|
|
setCursor(cur, endPit, right);
|
2007-03-25 17:11:30 +00:00
|
|
|
|
2008-01-12 21:38:51 +00:00
|
|
|
cur.checkBufferStructure();
|
2000-02-10 17:53:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-05-14 09:24:17 +00:00
|
|
|
bool Text::handleBibitems(Cursor & cur)
|
|
|
|
{
|
2008-03-06 21:31:27 +00:00
|
|
|
if (cur.paragraph().layout().labeltype != LABEL_BIBLIO)
|
2007-05-14 09:24:17 +00:00
|
|
|
return false;
|
Fix bug 4037 and related problems. The patch has been cleaned up a bit
from the one posted to the list.
The basic idea has two parts. First, we hard code an "empty layout"
(called PlainLayout, for want of a better name) in TextClass and read it
before doing anything else. It can therefore be customized by classes,
if they want---say, to make it left-aligned. Second, InsetText's are
divided into three types: (i) normal ones, that use the "default" layout
defined by the text class; (ii) highly restrictive ones, such as ERT and
(not quite an inset) table cells, which demand the empty layout; (iii)
middling ones, which default to an empty layout and use the empty layout
in place of the default. (This is so we don't get the same problem we
had with ERT in e.g. footnotes.) The type of inset is signaled by new
methods InsetText::forceEmptyLayout() and InsetText::useEmptyLayout().
(The latter might better be called: useEmptyLayoutInsteadOfDefault(),
but that's silly.) The old InsetText::forceDefaultParagraphs() has been
split into these, plus a new method InsetText::allowParagraphCustomization().
A lot of the changes just adapt to this change.
The other big change is in GuiToolbar: We want to show LyXDefault and
the "default" layout only when they're active.
There are a handful of places where I'm not entirely sure whether we
should be using forceEmptyLayout or !allowParagraphCustomization() or
both. The InsetCaption is one of these. These places, and some others,
are marked with FIXMEs, so I'd appreciate it if people would search
through the patch and let me know whether these need changing. If they
don't, the FIXMEs can be deleted.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22966 a592a061-630c-0410-9148-cb99ea01b6c8
2008-02-12 17:31:07 +00:00
|
|
|
|
|
|
|
if (cur.pos() != 0)
|
|
|
|
return false;
|
|
|
|
|
2008-11-17 11:46:07 +00:00
|
|
|
BufferParams const & bufparams = cur.buffer()->params();
|
Fix bug 4037 and related problems. The patch has been cleaned up a bit
from the one posted to the list.
The basic idea has two parts. First, we hard code an "empty layout"
(called PlainLayout, for want of a better name) in TextClass and read it
before doing anything else. It can therefore be customized by classes,
if they want---say, to make it left-aligned. Second, InsetText's are
divided into three types: (i) normal ones, that use the "default" layout
defined by the text class; (ii) highly restrictive ones, such as ERT and
(not quite an inset) table cells, which demand the empty layout; (iii)
middling ones, which default to an empty layout and use the empty layout
in place of the default. (This is so we don't get the same problem we
had with ERT in e.g. footnotes.) The type of inset is signaled by new
methods InsetText::forceEmptyLayout() and InsetText::useEmptyLayout().
(The latter might better be called: useEmptyLayoutInsteadOfDefault(),
but that's silly.) The old InsetText::forceDefaultParagraphs() has been
split into these, plus a new method InsetText::allowParagraphCustomization().
A lot of the changes just adapt to this change.
The other big change is in GuiToolbar: We want to show LyXDefault and
the "default" layout only when they're active.
There are a handful of places where I'm not entirely sure whether we
should be using forceEmptyLayout or !allowParagraphCustomization() or
both. The InsetCaption is one of these. These places, and some others,
are marked with FIXMEs, so I'd appreciate it if people would search
through the patch and let me know whether these need changing. If they
don't, the FIXMEs can be deleted.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22966 a592a061-630c-0410-9148-cb99ea01b6c8
2008-02-12 17:31:07 +00:00
|
|
|
Paragraph const & par = cur.paragraph();
|
|
|
|
Cursor prevcur = cur;
|
|
|
|
if (cur.pit() > 0) {
|
|
|
|
--prevcur.pit();
|
|
|
|
prevcur.pos() = prevcur.lastpos();
|
|
|
|
}
|
|
|
|
Paragraph const & prevpar = prevcur.paragraph();
|
|
|
|
|
2007-05-14 09:24:17 +00:00
|
|
|
// if a bibitem is deleted, merge with previous paragraph
|
|
|
|
// if this is a bibliography item as well
|
Fix bug 4037 and related problems. The patch has been cleaned up a bit
from the one posted to the list.
The basic idea has two parts. First, we hard code an "empty layout"
(called PlainLayout, for want of a better name) in TextClass and read it
before doing anything else. It can therefore be customized by classes,
if they want---say, to make it left-aligned. Second, InsetText's are
divided into three types: (i) normal ones, that use the "default" layout
defined by the text class; (ii) highly restrictive ones, such as ERT and
(not quite an inset) table cells, which demand the empty layout; (iii)
middling ones, which default to an empty layout and use the empty layout
in place of the default. (This is so we don't get the same problem we
had with ERT in e.g. footnotes.) The type of inset is signaled by new
methods InsetText::forceEmptyLayout() and InsetText::useEmptyLayout().
(The latter might better be called: useEmptyLayoutInsteadOfDefault(),
but that's silly.) The old InsetText::forceDefaultParagraphs() has been
split into these, plus a new method InsetText::allowParagraphCustomization().
A lot of the changes just adapt to this change.
The other big change is in GuiToolbar: We want to show LyXDefault and
the "default" layout only when they're active.
There are a handful of places where I'm not entirely sure whether we
should be using forceEmptyLayout or !allowParagraphCustomization() or
both. The InsetCaption is one of these. These places, and some others,
are marked with FIXMEs, so I'd appreciate it if people would search
through the patch and let me know whether these need changing. If they
don't, the FIXMEs can be deleted.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22966 a592a061-630c-0410-9148-cb99ea01b6c8
2008-02-12 17:31:07 +00:00
|
|
|
if (cur.pit() > 0 && par.layout() == prevpar.layout()) {
|
2015-03-12 14:57:29 +00:00
|
|
|
cur.recordUndo(prevcur.pit());
|
Fix bug 4037 and related problems. The patch has been cleaned up a bit
from the one posted to the list.
The basic idea has two parts. First, we hard code an "empty layout"
(called PlainLayout, for want of a better name) in TextClass and read it
before doing anything else. It can therefore be customized by classes,
if they want---say, to make it left-aligned. Second, InsetText's are
divided into three types: (i) normal ones, that use the "default" layout
defined by the text class; (ii) highly restrictive ones, such as ERT and
(not quite an inset) table cells, which demand the empty layout; (iii)
middling ones, which default to an empty layout and use the empty layout
in place of the default. (This is so we don't get the same problem we
had with ERT in e.g. footnotes.) The type of inset is signaled by new
methods InsetText::forceEmptyLayout() and InsetText::useEmptyLayout().
(The latter might better be called: useEmptyLayoutInsteadOfDefault(),
but that's silly.) The old InsetText::forceDefaultParagraphs() has been
split into these, plus a new method InsetText::allowParagraphCustomization().
A lot of the changes just adapt to this change.
The other big change is in GuiToolbar: We want to show LyXDefault and
the "default" layout only when they're active.
There are a handful of places where I'm not entirely sure whether we
should be using forceEmptyLayout or !allowParagraphCustomization() or
both. The InsetCaption is one of these. These places, and some others,
are marked with FIXMEs, so I'd appreciate it if people would search
through the patch and let me know whether these need changing. If they
don't, the FIXMEs can be deleted.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22966 a592a061-630c-0410-9148-cb99ea01b6c8
2008-02-12 17:31:07 +00:00
|
|
|
mergeParagraph(bufparams, cur.text()->paragraphs(),
|
|
|
|
prevcur.pit());
|
2010-07-09 14:37:00 +00:00
|
|
|
cur.forceBufferUpdate();
|
Fix bug 4037 and related problems. The patch has been cleaned up a bit
from the one posted to the list.
The basic idea has two parts. First, we hard code an "empty layout"
(called PlainLayout, for want of a better name) in TextClass and read it
before doing anything else. It can therefore be customized by classes,
if they want---say, to make it left-aligned. Second, InsetText's are
divided into three types: (i) normal ones, that use the "default" layout
defined by the text class; (ii) highly restrictive ones, such as ERT and
(not quite an inset) table cells, which demand the empty layout; (iii)
middling ones, which default to an empty layout and use the empty layout
in place of the default. (This is so we don't get the same problem we
had with ERT in e.g. footnotes.) The type of inset is signaled by new
methods InsetText::forceEmptyLayout() and InsetText::useEmptyLayout().
(The latter might better be called: useEmptyLayoutInsteadOfDefault(),
but that's silly.) The old InsetText::forceDefaultParagraphs() has been
split into these, plus a new method InsetText::allowParagraphCustomization().
A lot of the changes just adapt to this change.
The other big change is in GuiToolbar: We want to show LyXDefault and
the "default" layout only when they're active.
There are a handful of places where I'm not entirely sure whether we
should be using forceEmptyLayout or !allowParagraphCustomization() or
both. The InsetCaption is one of these. These places, and some others,
are marked with FIXMEs, so I'd appreciate it if people would search
through the patch and let me know whether these need changing. If they
don't, the FIXMEs can be deleted.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22966 a592a061-630c-0410-9148-cb99ea01b6c8
2008-02-12 17:31:07 +00:00
|
|
|
setCursorIntern(cur, prevcur.pit(), prevcur.pos());
|
2010-07-08 20:04:35 +00:00
|
|
|
cur.screenUpdateFlags(Update::Force);
|
2007-05-14 09:24:17 +00:00
|
|
|
return true;
|
2014-03-08 10:59:39 +00:00
|
|
|
}
|
Fix bug 4037 and related problems. The patch has been cleaned up a bit
from the one posted to the list.
The basic idea has two parts. First, we hard code an "empty layout"
(called PlainLayout, for want of a better name) in TextClass and read it
before doing anything else. It can therefore be customized by classes,
if they want---say, to make it left-aligned. Second, InsetText's are
divided into three types: (i) normal ones, that use the "default" layout
defined by the text class; (ii) highly restrictive ones, such as ERT and
(not quite an inset) table cells, which demand the empty layout; (iii)
middling ones, which default to an empty layout and use the empty layout
in place of the default. (This is so we don't get the same problem we
had with ERT in e.g. footnotes.) The type of inset is signaled by new
methods InsetText::forceEmptyLayout() and InsetText::useEmptyLayout().
(The latter might better be called: useEmptyLayoutInsteadOfDefault(),
but that's silly.) The old InsetText::forceDefaultParagraphs() has been
split into these, plus a new method InsetText::allowParagraphCustomization().
A lot of the changes just adapt to this change.
The other big change is in GuiToolbar: We want to show LyXDefault and
the "default" layout only when they're active.
There are a handful of places where I'm not entirely sure whether we
should be using forceEmptyLayout or !allowParagraphCustomization() or
both. The InsetCaption is one of these. These places, and some others,
are marked with FIXMEs, so I'd appreciate it if people would search
through the patch and let me know whether these need changing. If they
don't, the FIXMEs can be deleted.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22966 a592a061-630c-0410-9148-cb99ea01b6c8
2008-02-12 17:31:07 +00:00
|
|
|
|
|
|
|
// otherwise reset to default
|
2008-07-10 17:41:52 +00:00
|
|
|
cur.paragraph().setPlainOrDefaultLayout(bufparams.documentClass());
|
2008-02-28 14:58:22 +00:00
|
|
|
return true;
|
2007-05-14 09:24:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-29 23:33:02 +00:00
|
|
|
bool Text::erase(Cursor & cur)
|
2000-02-10 17:53:36 +00:00
|
|
|
{
|
2008-11-17 11:46:07 +00:00
|
|
|
LASSERT(this == cur.text(), return false);
|
2005-12-19 12:30:34 +00:00
|
|
|
bool needsUpdate = false;
|
2006-11-07 23:02:44 +00:00
|
|
|
Paragraph & par = cur.paragraph();
|
2005-07-17 16:12:48 +00:00
|
|
|
|
2004-04-13 06:27:29 +00:00
|
|
|
if (cur.pos() != cur.lastpos()) {
|
2006-11-07 23:02:44 +00:00
|
|
|
// this is the code for a normal delete, not pasting
|
|
|
|
// any paragraphs
|
2007-10-18 11:51:17 +00:00
|
|
|
cur.recordUndo(DELETE_UNDO);
|
2008-02-13 14:16:51 +00:00
|
|
|
bool const was_inset = cur.paragraph().isInset(cur.pos());
|
2014-03-29 22:52:36 +00:00
|
|
|
if(!par.eraseChar(cur.pos(), cur.buffer()->params().track_changes))
|
2007-01-20 16:59:13 +00:00
|
|
|
// the character has been logically deleted only => skip it
|
2007-08-13 13:36:19 +00:00
|
|
|
cur.top().forwardPos();
|
2008-02-13 14:16:51 +00:00
|
|
|
|
|
|
|
if (was_inset)
|
2010-07-09 14:37:00 +00:00
|
|
|
cur.forceBufferUpdate();
|
2008-02-13 14:16:51 +00:00
|
|
|
else
|
|
|
|
cur.checkBufferStructure();
|
2006-11-07 23:02:44 +00:00
|
|
|
needsUpdate = true;
|
2006-11-26 00:04:25 +00:00
|
|
|
} else {
|
|
|
|
if (cur.pit() == cur.lastpit())
|
|
|
|
return dissolveInset(cur);
|
|
|
|
|
2014-03-29 22:52:36 +00:00
|
|
|
if (!par.isMergedOnEndOfParDeletion(cur.buffer()->params().track_changes)) {
|
2016-07-05 14:22:49 +00:00
|
|
|
cur.recordUndo(DELETE_UNDO);
|
2006-11-07 23:02:44 +00:00
|
|
|
par.setChange(cur.pos(), Change(Change::DELETED));
|
|
|
|
cur.forwardPos();
|
|
|
|
needsUpdate = true;
|
2005-07-17 16:12:48 +00:00
|
|
|
} else {
|
2006-11-07 23:02:44 +00:00
|
|
|
setCursorIntern(cur, cur.pit() + 1, 0);
|
|
|
|
needsUpdate = backspacePos0(cur);
|
2005-07-17 16:12:48 +00:00
|
|
|
}
|
2006-11-24 22:12:04 +00:00
|
|
|
}
|
2006-09-09 15:27:44 +00:00
|
|
|
|
2007-05-14 09:24:17 +00:00
|
|
|
needsUpdate |= handleBibitems(cur);
|
|
|
|
|
2006-11-13 16:59:10 +00:00
|
|
|
if (needsUpdate) {
|
2006-11-17 17:42:52 +00:00
|
|
|
// Make sure the cursor is correct. Is this really needed?
|
|
|
|
// No, not really... at least not here!
|
2016-02-29 12:47:23 +00:00
|
|
|
cur.top().setPitPos(cur.pit(), cur.pos());
|
2008-01-12 21:38:51 +00:00
|
|
|
cur.checkBufferStructure();
|
2006-11-13 16:59:10 +00:00
|
|
|
}
|
2007-05-28 22:27:45 +00:00
|
|
|
|
2005-12-19 12:30:34 +00:00
|
|
|
return needsUpdate;
|
2000-02-10 17:53:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-29 23:33:02 +00:00
|
|
|
bool Text::backspacePos0(Cursor & cur)
|
2000-02-10 17:53:36 +00:00
|
|
|
{
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(this == cur.text());
|
2006-11-07 23:02:44 +00:00
|
|
|
if (cur.pit() == 0)
|
|
|
|
return false;
|
|
|
|
|
2005-12-19 12:30:34 +00:00
|
|
|
bool needsUpdate = false;
|
2002-03-21 17:27:08 +00:00
|
|
|
|
2008-11-17 11:46:07 +00:00
|
|
|
BufferParams const & bufparams = cur.buffer()->params();
|
2008-02-28 01:42:02 +00:00
|
|
|
DocumentClass const & tclass = bufparams.documentClass();
|
2006-11-07 23:02:44 +00:00
|
|
|
ParagraphList & plist = cur.text()->paragraphs();
|
|
|
|
Paragraph const & par = cur.paragraph();
|
2007-04-26 14:56:30 +00:00
|
|
|
Cursor prevcur = cur;
|
2006-11-07 23:02:44 +00:00
|
|
|
--prevcur.pit();
|
|
|
|
prevcur.pos() = prevcur.lastpos();
|
|
|
|
Paragraph const & prevpar = prevcur.paragraph();
|
2006-04-05 23:56:29 +00:00
|
|
|
|
2006-11-07 23:02:44 +00:00
|
|
|
// is it an empty paragraph?
|
2007-05-28 22:27:45 +00:00
|
|
|
if (cur.lastpos() == 0
|
2006-11-07 23:02:44 +00:00
|
|
|
|| (cur.lastpos() == 1 && par.isSeparator(0))) {
|
2015-03-12 14:57:29 +00:00
|
|
|
cur.recordUndo(prevcur.pit());
|
2016-01-10 16:59:42 +00:00
|
|
|
plist.erase(lyx::next(plist.begin(), cur.pit()));
|
2006-11-07 23:02:44 +00:00
|
|
|
needsUpdate = true;
|
2006-03-11 13:31:41 +00:00
|
|
|
}
|
2006-11-07 23:02:44 +00:00
|
|
|
// is previous par empty?
|
2007-05-28 22:27:45 +00:00
|
|
|
else if (prevcur.lastpos() == 0
|
2006-11-07 23:02:44 +00:00
|
|
|
|| (prevcur.lastpos() == 1 && prevpar.isSeparator(0))) {
|
2015-03-12 14:57:29 +00:00
|
|
|
cur.recordUndo(prevcur.pit());
|
2016-01-10 16:59:42 +00:00
|
|
|
plist.erase(lyx::next(plist.begin(), prevcur.pit()));
|
2006-11-07 23:02:44 +00:00
|
|
|
needsUpdate = true;
|
2006-03-11 13:31:41 +00:00
|
|
|
}
|
|
|
|
// Pasting is not allowed, if the paragraphs have different
|
2006-11-24 22:12:04 +00:00
|
|
|
// layouts. I think it is a real bug of all other
|
2006-03-11 13:31:41 +00:00
|
|
|
// word processors to allow it. It confuses the user.
|
|
|
|
// Correction: Pasting is always allowed with standard-layout
|
Fix bug 4037 and related problems. The patch has been cleaned up a bit
from the one posted to the list.
The basic idea has two parts. First, we hard code an "empty layout"
(called PlainLayout, for want of a better name) in TextClass and read it
before doing anything else. It can therefore be customized by classes,
if they want---say, to make it left-aligned. Second, InsetText's are
divided into three types: (i) normal ones, that use the "default" layout
defined by the text class; (ii) highly restrictive ones, such as ERT and
(not quite an inset) table cells, which demand the empty layout; (iii)
middling ones, which default to an empty layout and use the empty layout
in place of the default. (This is so we don't get the same problem we
had with ERT in e.g. footnotes.) The type of inset is signaled by new
methods InsetText::forceEmptyLayout() and InsetText::useEmptyLayout().
(The latter might better be called: useEmptyLayoutInsteadOfDefault(),
but that's silly.) The old InsetText::forceDefaultParagraphs() has been
split into these, plus a new method InsetText::allowParagraphCustomization().
A lot of the changes just adapt to this change.
The other big change is in GuiToolbar: We want to show LyXDefault and
the "default" layout only when they're active.
There are a handful of places where I'm not entirely sure whether we
should be using forceEmptyLayout or !allowParagraphCustomization() or
both. The InsetCaption is one of these. These places, and some others,
are marked with FIXMEs, so I'd appreciate it if people would search
through the patch and let me know whether these need changing. If they
don't, the FIXMEs can be deleted.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22966 a592a061-630c-0410-9148-cb99ea01b6c8
2008-02-12 17:31:07 +00:00
|
|
|
// or the empty layout.
|
2006-11-07 23:02:44 +00:00
|
|
|
else if (par.layout() == prevpar.layout()
|
2008-03-06 21:31:27 +00:00
|
|
|
|| tclass.isDefaultLayout(par.layout())
|
2008-07-10 17:41:52 +00:00
|
|
|
|| tclass.isPlainLayout(par.layout())) {
|
2015-03-12 14:57:29 +00:00
|
|
|
cur.recordUndo(prevcur.pit());
|
2006-11-07 23:02:44 +00:00
|
|
|
mergeParagraph(bufparams, plist, prevcur.pit());
|
2006-03-11 13:31:41 +00:00
|
|
|
needsUpdate = true;
|
2006-11-07 23:02:44 +00:00
|
|
|
}
|
2003-08-11 09:09:01 +00:00
|
|
|
|
2006-11-07 23:02:44 +00:00
|
|
|
if (needsUpdate) {
|
2010-07-09 14:37:00 +00:00
|
|
|
cur.forceBufferUpdate();
|
2006-11-07 23:02:44 +00:00
|
|
|
setCursorIntern(cur, prevcur.pit(), prevcur.pos());
|
2006-03-11 13:31:41 +00:00
|
|
|
}
|
2006-11-07 23:02:44 +00:00
|
|
|
|
2006-03-11 13:31:41 +00:00
|
|
|
return needsUpdate;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-29 23:33:02 +00:00
|
|
|
bool Text::backspace(Cursor & cur)
|
2006-03-11 13:31:41 +00:00
|
|
|
{
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(this == cur.text());
|
2006-03-11 13:31:41 +00:00
|
|
|
bool needsUpdate = false;
|
|
|
|
if (cur.pos() == 0) {
|
2006-09-08 14:08:26 +00:00
|
|
|
if (cur.pit() == 0)
|
|
|
|
return dissolveInset(cur);
|
|
|
|
|
2013-04-24 16:31:34 +00:00
|
|
|
Cursor prev_cur = cur;
|
|
|
|
--prev_cur.pit();
|
2006-03-11 13:31:41 +00:00
|
|
|
|
2014-03-29 22:52:36 +00:00
|
|
|
if (!prev_cur.paragraph().isMergedOnEndOfParDeletion(cur.buffer()->params().track_changes)) {
|
2015-03-12 14:57:29 +00:00
|
|
|
cur.recordUndo(prev_cur.pit(), prev_cur.pit());
|
2013-04-24 16:31:34 +00:00
|
|
|
prev_cur.paragraph().setChange(prev_cur.lastpos(), Change(Change::DELETED));
|
|
|
|
setCursorIntern(cur, prev_cur.pit(), prev_cur.lastpos());
|
2006-11-25 00:31:44 +00:00
|
|
|
return true;
|
|
|
|
}
|
2006-11-07 23:02:44 +00:00
|
|
|
// The cursor is at the beginning of a paragraph, so
|
|
|
|
// the backspace will collapse two paragraphs into one.
|
2006-03-11 13:31:41 +00:00
|
|
|
needsUpdate = backspacePos0(cur);
|
|
|
|
|
2000-02-10 17:53:36 +00:00
|
|
|
} else {
|
2002-05-29 12:58:23 +00:00
|
|
|
// this is the code for a normal backspace, not pasting
|
2002-06-24 20:28:12 +00:00
|
|
|
// any paragraphs
|
2007-10-18 11:51:17 +00:00
|
|
|
cur.recordUndo(DELETE_UNDO);
|
2007-10-22 22:18:52 +00:00
|
|
|
// We used to do cursorBackwardIntern() here, but it is
|
2000-03-28 16:18:02 +00:00
|
|
|
// not a good idea since it triggers the auto-delete
|
2007-10-22 22:18:52 +00:00
|
|
|
// mechanism. So we do a cursorBackwardIntern()-lite,
|
2000-03-28 16:18:02 +00:00
|
|
|
// without the dreaded mechanism. (JMarc)
|
2004-11-24 21:53:46 +00:00
|
|
|
setCursorIntern(cur, cur.pit(), cur.pos() - 1,
|
2004-02-12 16:36:01 +00:00
|
|
|
false, cur.boundary());
|
2008-02-13 14:16:51 +00:00
|
|
|
bool const was_inset = cur.paragraph().isInset(cur.pos());
|
2014-03-29 22:52:36 +00:00
|
|
|
cur.paragraph().eraseChar(cur.pos(), cur.buffer()->params().track_changes);
|
2008-02-13 14:16:51 +00:00
|
|
|
if (was_inset)
|
2010-07-09 14:37:00 +00:00
|
|
|
cur.forceBufferUpdate();
|
2008-02-13 14:16:51 +00:00
|
|
|
else
|
|
|
|
cur.checkBufferStructure();
|
2000-02-10 17:53:36 +00:00
|
|
|
}
|
2000-05-30 19:31:11 +00:00
|
|
|
|
2004-02-12 16:36:01 +00:00
|
|
|
if (cur.pos() == cur.lastpos())
|
2007-09-02 13:35:48 +00:00
|
|
|
cur.setCurrentFont();
|
2002-03-21 17:27:08 +00:00
|
|
|
|
2007-05-14 09:24:17 +00:00
|
|
|
needsUpdate |= handleBibitems(cur);
|
|
|
|
|
2006-12-29 23:54:48 +00:00
|
|
|
// A singlePar update is not enough in this case.
|
2010-07-08 20:04:35 +00:00
|
|
|
// cur.screenUpdateFlags(Update::Force);
|
2016-02-29 12:47:23 +00:00
|
|
|
cur.top().setPitPos(cur.pit(), cur.pos());
|
2005-12-19 12:30:34 +00:00
|
|
|
|
|
|
|
return needsUpdate;
|
2000-02-10 17:53:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-09-24 21:27:41 +00:00
|
|
|
bool Text::dissolveInset(Cursor & cur)
|
|
|
|
{
|
|
|
|
LASSERT(this == cur.text(), return false);
|
2006-09-08 14:08:26 +00:00
|
|
|
|
2009-08-09 15:29:34 +00:00
|
|
|
if (isMainText() || cur.inset().nargs() != 1)
|
2006-09-08 14:08:26 +00:00
|
|
|
return false;
|
|
|
|
|
2007-10-18 11:51:17 +00:00
|
|
|
cur.recordUndoInset();
|
2008-09-24 21:27:41 +00:00
|
|
|
cur.setMark(false);
|
2006-09-08 14:08:26 +00:00
|
|
|
cur.selHandle(false);
|
2017-06-15 09:37:40 +00:00
|
|
|
// save position inside inset
|
2006-10-21 00:16:43 +00:00
|
|
|
pos_type spos = cur.pos();
|
|
|
|
pit_type spit = cur.pit();
|
2006-09-08 14:08:26 +00:00
|
|
|
ParagraphList plist;
|
2006-09-09 15:27:44 +00:00
|
|
|
if (cur.lastpit() != 0 || cur.lastpos() != 0)
|
2006-09-08 14:08:26 +00:00
|
|
|
plist = paragraphs();
|
2007-11-05 19:41:16 +00:00
|
|
|
cur.popBackward();
|
2017-06-15 09:37:40 +00:00
|
|
|
// update cursor offset
|
2006-09-08 14:08:26 +00:00
|
|
|
if (spit == 0)
|
|
|
|
spos += cur.pos();
|
|
|
|
spit += cur.pit();
|
2017-06-15 09:37:40 +00:00
|
|
|
// remember position outside inset to delete inset later
|
|
|
|
// we do not do it now to avoid memory reuse issues (see #10667).
|
|
|
|
DocIterator inset_it = cur;
|
|
|
|
// jump over inset
|
|
|
|
++cur.pos();
|
2011-05-09 23:23:24 +00:00
|
|
|
|
2017-06-15 09:37:40 +00:00
|
|
|
Buffer & b = *cur.buffer();
|
2006-09-08 14:08:26 +00:00
|
|
|
if (!plist.empty()) {
|
2011-05-09 23:23:24 +00:00
|
|
|
// see bug 7319
|
|
|
|
// we clear the cache so that we won't get conflicts with labels
|
|
|
|
// that get pasted into the buffer. we should update this before
|
|
|
|
// its being empty matters. if not (i.e., if we encounter bugs),
|
|
|
|
// then this should instead be:
|
|
|
|
// cur.buffer().updateBuffer();
|
|
|
|
// but we'll try the cheaper solution here.
|
|
|
|
cur.buffer()->clearReferenceCache();
|
|
|
|
|
2006-12-28 10:24:45 +00:00
|
|
|
// ERT paragraphs have the Language latex_language.
|
|
|
|
// This is invalid outside of ERT, so we need to
|
|
|
|
// change it to the buffer language.
|
|
|
|
ParagraphList::iterator it = plist.begin();
|
|
|
|
ParagraphList::iterator it_end = plist.end();
|
2012-05-28 20:41:32 +00:00
|
|
|
for (; it != it_end; ++it)
|
2007-10-20 10:03:45 +00:00
|
|
|
it->changeLanguage(b.params(), latex_language, b.language());
|
2006-11-12 16:11:51 +00:00
|
|
|
|
2008-02-28 01:42:02 +00:00
|
|
|
pasteParagraphList(cur, plist, b.params().documentClassPtr(),
|
2006-09-08 14:08:26 +00:00
|
|
|
b.errorList("Paste"));
|
2011-05-09 21:50:35 +00:00
|
|
|
}
|
|
|
|
|
2017-06-15 09:37:40 +00:00
|
|
|
// delete the inset now
|
|
|
|
inset_it.paragraph().eraseChar(inset_it.pos(), b.params().track_changes);
|
|
|
|
|
2017-06-23 17:44:54 +00:00
|
|
|
// restore position
|
|
|
|
cur.pit() = min(cur.lastpit(), spit);
|
|
|
|
cur.pos() = min(cur.lastpos(), spos);
|
2009-10-29 13:36:22 +00:00
|
|
|
// Ensure the current language is set correctly (bug 6292)
|
|
|
|
cur.text()->setCursor(cur, cur.pit(), cur.pos());
|
2006-09-08 14:08:26 +00:00
|
|
|
cur.clearSelection();
|
|
|
|
cur.resetAnchor();
|
2017-06-23 17:44:54 +00:00
|
|
|
cur.forceBufferUpdate();
|
|
|
|
|
2006-09-08 14:08:26 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-29 23:33:02 +00:00
|
|
|
void Text::getWord(CursorSlice & from, CursorSlice & to,
|
2008-02-26 00:38:30 +00:00
|
|
|
word_location const loc) const
|
2003-11-11 10:08:35 +00:00
|
|
|
{
|
|
|
|
to = from;
|
2009-05-01 07:26:12 +00:00
|
|
|
pars_[to.pit()].locateWord(from.pos(), to.pos(), loc);
|
2003-11-11 10:08:35 +00:00
|
|
|
}
|
2003-12-02 12:39:14 +00:00
|
|
|
|
|
|
|
|
2009-08-09 15:29:34 +00:00
|
|
|
void Text::write(ostream & os) const
|
2003-12-02 12:39:14 +00:00
|
|
|
{
|
2009-08-09 15:29:34 +00:00
|
|
|
Buffer const & buf = owner_->buffer();
|
2003-12-02 12:39:14 +00:00
|
|
|
ParagraphList::const_iterator pit = paragraphs().begin();
|
|
|
|
ParagraphList::const_iterator end = paragraphs().end();
|
2006-10-21 00:16:43 +00:00
|
|
|
depth_type dth = 0;
|
2003-12-02 12:39:14 +00:00
|
|
|
for (; pit != end; ++pit)
|
2008-02-27 20:43:16 +00:00
|
|
|
pit->write(os, buf.params(), dth);
|
2007-08-12 14:56:49 +00:00
|
|
|
|
|
|
|
// Close begin_deeper
|
|
|
|
for(; dth > 0; --dth)
|
|
|
|
os << "\n\\end_deeper";
|
2003-12-02 12:39:14 +00:00
|
|
|
}
|
2003-12-03 15:27:16 +00:00
|
|
|
|
|
|
|
|
2014-03-08 10:59:39 +00:00
|
|
|
bool Text::read(Lexer & lex,
|
2008-02-19 03:46:11 +00:00
|
|
|
ErrorList & errorList, InsetText * insetPtr)
|
2004-01-28 16:21:29 +00:00
|
|
|
{
|
2009-08-09 15:29:34 +00:00
|
|
|
Buffer const & buf = owner_->buffer();
|
2006-10-21 00:16:43 +00:00
|
|
|
depth_type depth = 0;
|
2009-02-04 11:28:29 +00:00
|
|
|
bool res = true;
|
2003-12-03 15:27:16 +00:00
|
|
|
|
|
|
|
while (lex.isOK()) {
|
|
|
|
lex.nextToken();
|
2004-07-25 00:04:42 +00:00
|
|
|
string const token = lex.getString();
|
2003-12-03 15:27:16 +00:00
|
|
|
|
|
|
|
if (token.empty())
|
|
|
|
continue;
|
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
if (token == "\\end_inset")
|
2004-03-18 12:53:43 +00:00
|
|
|
break;
|
2003-12-03 15:27:16 +00:00
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
if (token == "\\end_body")
|
2004-08-16 00:16:17 +00:00
|
|
|
continue;
|
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
if (token == "\\begin_body")
|
2004-08-16 00:16:17 +00:00
|
|
|
continue;
|
|
|
|
|
2009-02-04 10:46:54 +00:00
|
|
|
if (token == "\\end_document") {
|
2009-02-04 11:28:29 +00:00
|
|
|
res = false;
|
|
|
|
break;
|
2009-02-04 10:46:54 +00:00
|
|
|
}
|
2003-12-03 15:27:16 +00:00
|
|
|
|
|
|
|
if (token == "\\begin_layout") {
|
|
|
|
lex.pushToken(token);
|
|
|
|
|
|
|
|
Paragraph par;
|
2008-09-13 20:19:32 +00:00
|
|
|
par.setInsetOwner(insetPtr);
|
2003-12-03 15:27:16 +00:00
|
|
|
par.params().depth(depth);
|
2007-10-28 18:51:54 +00:00
|
|
|
par.setFont(0, Font(inherit_font, buf.params().language));
|
2004-03-25 09:16:36 +00:00
|
|
|
pars_.push_back(par);
|
2009-08-09 15:29:34 +00:00
|
|
|
readParagraph(pars_.back(), lex, errorList);
|
2003-12-03 15:27:16 +00:00
|
|
|
|
2008-02-26 13:07:59 +00:00
|
|
|
// register the words in the global word list
|
2009-05-01 09:56:20 +00:00
|
|
|
pars_.back().updateWords();
|
2003-12-03 15:27:16 +00:00
|
|
|
} else if (token == "\\begin_deeper") {
|
|
|
|
++depth;
|
|
|
|
} else if (token == "\\end_deeper") {
|
2007-11-29 21:10:35 +00:00
|
|
|
if (!depth)
|
2003-12-03 15:27:16 +00:00
|
|
|
lex.printError("\\end_deeper: " "depth is already null");
|
2007-11-29 21:10:35 +00:00
|
|
|
else
|
2003-12-03 15:27:16 +00:00
|
|
|
--depth;
|
|
|
|
} else {
|
2007-11-29 21:10:35 +00:00
|
|
|
LYXERR0("Handling unknown body token: `" << token << '\'');
|
2003-12-03 15:27:16 +00:00
|
|
|
}
|
|
|
|
}
|
2009-02-04 11:28:29 +00:00
|
|
|
|
|
|
|
// avoid a crash on weird documents (bug 4859)
|
|
|
|
if (pars_.empty()) {
|
|
|
|
Paragraph par;
|
|
|
|
par.setInsetOwner(insetPtr);
|
|
|
|
par.params().depth(depth);
|
2014-03-08 10:59:39 +00:00
|
|
|
par.setFont(0, Font(inherit_font,
|
2009-02-04 11:28:29 +00:00
|
|
|
buf.params().language));
|
2009-02-04 14:23:16 +00:00
|
|
|
par.setPlainOrDefaultLayout(buf.params().documentClass());
|
2009-02-04 11:28:29 +00:00
|
|
|
pars_.push_back(par);
|
|
|
|
}
|
2014-03-08 10:59:39 +00:00
|
|
|
|
2009-02-04 11:28:29 +00:00
|
|
|
return res;
|
2003-12-03 15:27:16 +00:00
|
|
|
}
|
2003-12-10 09:45:32 +00:00
|
|
|
|
2012-10-27 13:45:27 +00:00
|
|
|
|
2004-02-13 07:30:59 +00:00
|
|
|
// Returns the current font and depth as a message.
|
2017-07-23 22:21:43 +00:00
|
|
|
docstring Text::currentState(Cursor const & cur, bool devel_mode) const
|
2004-02-13 07:30:59 +00:00
|
|
|
{
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(this == cur.text());
|
2008-11-17 11:46:07 +00:00
|
|
|
Buffer & buf = *cur.buffer();
|
2004-02-13 07:30:59 +00:00
|
|
|
Paragraph const & par = cur.paragraph();
|
2006-12-21 13:58:28 +00:00
|
|
|
odocstringstream os;
|
2004-02-13 07:30:59 +00:00
|
|
|
|
2014-03-29 22:52:36 +00:00
|
|
|
if (buf.params().track_changes)
|
2007-01-08 22:38:49 +00:00
|
|
|
os << _("[Change Tracking] ");
|
2006-03-11 13:31:41 +00:00
|
|
|
|
2006-10-22 16:47:42 +00:00
|
|
|
Change change = par.lookupChange(cur.pos());
|
|
|
|
|
2009-08-04 23:01:06 +00:00
|
|
|
if (change.changed()) {
|
2016-05-03 19:40:28 +00:00
|
|
|
docstring const author =
|
|
|
|
buf.params().authors().get(change.author).nameAndEmail();
|
|
|
|
docstring const date = formatted_datetime(change.changetime);
|
|
|
|
os << bformat(_("Changed by %1$s[[author]] on %2$s[[date]]. "),
|
|
|
|
author, date);
|
2004-02-13 07:30:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// I think we should only show changes from the default
|
|
|
|
// font. (Asger)
|
2006-02-05 13:20:16 +00:00
|
|
|
// No, from the document font (MV)
|
2007-09-02 13:35:48 +00:00
|
|
|
Font font = cur.real_current_font;
|
2007-10-28 18:51:54 +00:00
|
|
|
font.fontInfo().reduce(buf.params().getFont().fontInfo());
|
2004-02-13 07:30:59 +00:00
|
|
|
|
2006-12-21 14:31:19 +00:00
|
|
|
os << bformat(_("Font: %1$s"), font.stateText(&buf.params()));
|
2004-02-13 07:30:59 +00:00
|
|
|
|
|
|
|
// The paragraph depth
|
2004-03-18 13:28:49 +00:00
|
|
|
int depth = cur.paragraph().getDepth();
|
2004-02-13 07:30:59 +00:00
|
|
|
if (depth > 0)
|
2006-12-21 13:58:28 +00:00
|
|
|
os << bformat(_(", Depth: %1$d"), depth);
|
2004-02-13 07:30:59 +00:00
|
|
|
|
|
|
|
// The paragraph spacing, but only if different from
|
|
|
|
// buffer spacing.
|
|
|
|
Spacing const & spacing = par.params().spacing();
|
|
|
|
if (!spacing.isDefault()) {
|
2006-12-21 13:58:28 +00:00
|
|
|
os << _(", Spacing: ");
|
2004-02-13 07:30:59 +00:00
|
|
|
switch (spacing.getSpace()) {
|
|
|
|
case Spacing::Single:
|
2006-12-21 13:58:28 +00:00
|
|
|
os << _("Single");
|
2004-02-13 07:30:59 +00:00
|
|
|
break;
|
|
|
|
case Spacing::Onehalf:
|
2006-12-21 13:58:28 +00:00
|
|
|
os << _("OneHalf");
|
2004-02-13 07:30:59 +00:00
|
|
|
break;
|
|
|
|
case Spacing::Double:
|
2006-12-21 13:58:28 +00:00
|
|
|
os << _("Double");
|
2004-02-13 07:30:59 +00:00
|
|
|
break;
|
|
|
|
case Spacing::Other:
|
2006-12-21 13:58:28 +00:00
|
|
|
os << _("Other (") << from_ascii(spacing.getValueAsString()) << ')';
|
2004-02-13 07:30:59 +00:00
|
|
|
break;
|
|
|
|
case Spacing::Default:
|
|
|
|
// should never happen, do nothing
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2004-08-13 20:26:26 +00:00
|
|
|
|
2017-07-23 22:21:43 +00:00
|
|
|
if (devel_mode) {
|
|
|
|
os << _(", Inset: ") << &cur.inset();
|
|
|
|
os << _(", Paragraph: ") << cur.pit();
|
|
|
|
os << _(", Id: ") << par.id();
|
|
|
|
os << _(", Position: ") << cur.pos();
|
|
|
|
// FIXME: Why is the check for par.size() needed?
|
|
|
|
// We are called with cur.pos() == par.size() quite often.
|
|
|
|
if (!par.empty() && cur.pos() < par.size()) {
|
|
|
|
// Force output of code point, not character
|
|
|
|
size_t const c = par.getChar(cur.pos());
|
|
|
|
os << _(", Char: 0x") << hex << c;
|
|
|
|
}
|
|
|
|
os << _(", Boundary: ") << cur.boundary();
|
|
|
|
// Row & row = cur.textRow();
|
|
|
|
// os << bformat(_(", Row b:%1$d e:%2$d"), row.pos(), row.endpos());
|
2007-01-29 09:40:04 +00:00
|
|
|
}
|
2004-02-13 07:30:59 +00:00
|
|
|
return os.str();
|
|
|
|
}
|
2004-02-13 13:51:12 +00:00
|
|
|
|
|
|
|
|
2008-08-13 13:46:19 +00:00
|
|
|
docstring Text::getPossibleLabel(Cursor const & cur) const
|
2004-02-13 13:51:12 +00:00
|
|
|
{
|
2017-04-16 02:47:11 +00:00
|
|
|
pit_type textpit = cur.pit();
|
|
|
|
Layout const * layout = &(pars_[textpit].layout());
|
|
|
|
|
|
|
|
// Will contain the label prefix.
|
|
|
|
docstring name;
|
2004-02-13 13:51:12 +00:00
|
|
|
|
2017-04-16 02:47:11 +00:00
|
|
|
// For captions, we just take the caption type
|
|
|
|
Inset * caption_inset = cur.innerInsetOfType(CAPTION_CODE);
|
|
|
|
if (caption_inset) {
|
|
|
|
string const & ftype = static_cast<InsetCaption *>(caption_inset)->floattype();
|
|
|
|
FloatList const & fl = cur.buffer()->params().documentClass().floats();
|
|
|
|
if (fl.typeExist(ftype)) {
|
|
|
|
Floating const & flt = fl.getType(ftype);
|
|
|
|
name = from_utf8(flt.refPrefix());
|
|
|
|
}
|
|
|
|
if (name.empty())
|
|
|
|
name = from_utf8(ftype.substr(0,3));
|
|
|
|
} else {
|
|
|
|
// For section, subsection, etc...
|
|
|
|
if (layout->latextype == LATEX_PARAGRAPH && textpit != 0) {
|
|
|
|
Layout const * layout2 = &(pars_[textpit - 1].layout());
|
|
|
|
if (layout2->latextype != LATEX_PARAGRAPH) {
|
|
|
|
--textpit;
|
|
|
|
layout = layout2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (layout->latextype != LATEX_PARAGRAPH)
|
|
|
|
name = layout->refprefix;
|
|
|
|
|
|
|
|
// If none of the above worked, see if the inset knows.
|
|
|
|
if (name.empty()) {
|
|
|
|
InsetLayout const & il = cur.inset().getLayout();
|
|
|
|
name = il.refprefix();
|
|
|
|
}
|
|
|
|
}
|
2004-02-13 13:51:12 +00:00
|
|
|
|
2007-03-16 11:36:36 +00:00
|
|
|
docstring text;
|
2017-04-16 02:47:11 +00:00
|
|
|
docstring par_text = pars_[textpit].asString(AS_STR_SKIPDELETE);
|
2010-03-17 12:50:58 +00:00
|
|
|
|
|
|
|
// The return string of math matrices might contain linebreaks
|
2007-11-10 11:05:38 +00:00
|
|
|
par_text = subst(par_text, '\n', '-');
|
2010-03-17 12:50:58 +00:00
|
|
|
int const numwords = 3;
|
|
|
|
for (int i = 0; i < numwords; ++i) {
|
2004-02-13 13:51:12 +00:00
|
|
|
if (par_text.empty())
|
|
|
|
break;
|
2006-11-11 11:27:47 +00:00
|
|
|
docstring head;
|
2004-02-13 13:51:12 +00:00
|
|
|
par_text = split(par_text, head, ' ');
|
|
|
|
// Is it legal to use spaces in labels ?
|
|
|
|
if (i > 0)
|
|
|
|
text += '-';
|
|
|
|
text += head;
|
|
|
|
}
|
2014-03-08 10:59:39 +00:00
|
|
|
|
2010-03-17 12:50:58 +00:00
|
|
|
// Make sure it isn't too long
|
|
|
|
unsigned int const max_label_length = 32;
|
|
|
|
if (text.size() > max_label_length)
|
|
|
|
text.resize(max_label_length);
|
2004-02-13 13:51:12 +00:00
|
|
|
|
2007-03-16 11:36:36 +00:00
|
|
|
if (!name.empty())
|
2010-03-17 12:23:24 +00:00
|
|
|
text = name + ':' + text;
|
2007-03-16 11:36:36 +00:00
|
|
|
|
2012-09-19 08:20:19 +00:00
|
|
|
// We need a unique label
|
|
|
|
docstring label = text;
|
|
|
|
int i = 1;
|
2019-01-09 08:06:53 +00:00
|
|
|
while (cur.buffer()->activeLabel(label)) {
|
2012-09-19 08:20:19 +00:00
|
|
|
label = text + '-' + convert<docstring>(i);
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return label;
|
2004-02-13 13:51:12 +00:00
|
|
|
}
|
2004-02-16 11:58:51 +00:00
|
|
|
|
|
|
|
|
2008-09-28 17:10:16 +00:00
|
|
|
docstring Text::asString(int options) const
|
|
|
|
{
|
|
|
|
return asString(0, pars_.size(), options);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
docstring Text::asString(pit_type beg, pit_type end, int options) const
|
|
|
|
{
|
|
|
|
size_t i = size_t(beg);
|
|
|
|
docstring str = pars_[i].asString(options);
|
|
|
|
for (++i; i != size_t(end); ++i) {
|
|
|
|
str += '\n';
|
|
|
|
str += pars_[i].asString(options);
|
|
|
|
}
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-04 18:38:47 +00:00
|
|
|
void Text::shortenForOutliner(docstring & str, size_t const maxlen)
|
2010-12-20 21:55:09 +00:00
|
|
|
{
|
2015-10-04 18:38:47 +00:00
|
|
|
support::truncateWithEllipsis(str, maxlen);
|
2017-01-08 20:57:02 +00:00
|
|
|
for (char_type & c : str)
|
|
|
|
if (c == L'\n' || c == L'\t')
|
|
|
|
c = L' ';
|
2015-10-04 18:38:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Text::forOutliner(docstring & os, size_t const maxlen,
|
2017-01-08 20:57:02 +00:00
|
|
|
bool const shorten) const
|
|
|
|
{
|
|
|
|
pit_type end = pars_.size() - 1;
|
|
|
|
if (0 <= end && !pars_[0].labelString().empty())
|
|
|
|
os += pars_[0].labelString() + ' ';
|
|
|
|
forOutliner(os, maxlen, 0, end, shorten);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Text::forOutliner(docstring & os, size_t const maxlen,
|
|
|
|
pit_type pit_start, pit_type pit_end,
|
|
|
|
bool const shorten) const
|
2015-10-04 18:38:47 +00:00
|
|
|
{
|
|
|
|
size_t tmplen = shorten ? maxlen + 1 : maxlen;
|
2017-01-08 20:57:02 +00:00
|
|
|
pit_type end = min(size_t(pit_end), pars_.size() - 1);
|
|
|
|
bool first = true;
|
|
|
|
for (pit_type i = pit_start; i <= end && os.length() < tmplen; ++i) {
|
|
|
|
if (!first)
|
|
|
|
os += ' ';
|
|
|
|
// This function lets the first label be treated separately
|
|
|
|
pars_[i].forOutliner(os, tmplen, false, !first);
|
|
|
|
first = false;
|
|
|
|
}
|
2015-10-04 18:38:47 +00:00
|
|
|
if (shorten)
|
|
|
|
shortenForOutliner(os, maxlen);
|
2010-12-20 21:55:09 +00:00
|
|
|
}
|
|
|
|
|
2008-09-28 17:10:16 +00:00
|
|
|
|
2007-04-29 23:33:02 +00:00
|
|
|
void Text::charsTranspose(Cursor & cur)
|
2006-11-17 09:03:30 +00:00
|
|
|
{
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(this == cur.text());
|
2006-11-17 09:03:30 +00:00
|
|
|
|
|
|
|
pos_type pos = cur.pos();
|
|
|
|
|
|
|
|
// If cursor is at beginning or end of paragraph, do nothing.
|
|
|
|
if (pos == cur.lastpos() || pos == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Paragraph & par = cur.paragraph();
|
|
|
|
|
2007-05-28 22:27:45 +00:00
|
|
|
// Get the positions of the characters to be transposed.
|
2006-11-17 09:03:30 +00:00
|
|
|
pos_type pos1 = pos - 1;
|
|
|
|
pos_type pos2 = pos;
|
|
|
|
|
|
|
|
// In change tracking mode, ignore deleted characters.
|
|
|
|
while (pos2 < cur.lastpos() && par.isDeleted(pos2))
|
|
|
|
++pos2;
|
|
|
|
if (pos2 == cur.lastpos())
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (pos1 >= 0 && par.isDeleted(pos1))
|
|
|
|
--pos1;
|
|
|
|
if (pos1 < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Don't do anything if one of the "characters" is not regular text.
|
|
|
|
if (par.isInset(pos1) || par.isInset(pos2))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Store the characters to be transposed (including font information).
|
2008-10-28 17:55:00 +00:00
|
|
|
char_type const char1 = par.getChar(pos1);
|
2015-02-28 22:41:20 +00:00
|
|
|
Font const font1 =
|
2008-11-17 11:46:07 +00:00
|
|
|
par.getFontSettings(cur.buffer()->params(), pos1);
|
2007-05-28 22:27:45 +00:00
|
|
|
|
2008-10-28 17:55:00 +00:00
|
|
|
char_type const char2 = par.getChar(pos2);
|
2015-02-28 22:41:20 +00:00
|
|
|
Font const font2 =
|
2008-11-17 11:46:07 +00:00
|
|
|
par.getFontSettings(cur.buffer()->params(), pos2);
|
2006-11-17 09:03:30 +00:00
|
|
|
|
|
|
|
// And finally, we are ready to perform the transposition.
|
|
|
|
// Track the changes if Change Tracking is enabled.
|
2014-03-29 22:52:36 +00:00
|
|
|
bool const trackChanges = cur.buffer()->params().track_changes;
|
2006-11-17 09:03:30 +00:00
|
|
|
|
2007-10-18 11:51:17 +00:00
|
|
|
cur.recordUndo();
|
2006-11-17 09:03:30 +00:00
|
|
|
|
|
|
|
par.eraseChar(pos2, trackChanges);
|
|
|
|
par.eraseChar(pos1, trackChanges);
|
|
|
|
par.insertChar(pos1, char2, font2, trackChanges);
|
|
|
|
par.insertChar(pos2, char1, font1, trackChanges);
|
|
|
|
|
2008-01-12 21:38:51 +00:00
|
|
|
cur.checkBufferStructure();
|
2007-03-12 11:23:41 +00:00
|
|
|
|
2006-11-17 09:03:30 +00:00
|
|
|
// After the transposition, move cursor to after the transposition.
|
|
|
|
setCursor(cur, cur.pit(), pos2);
|
|
|
|
cur.forwardPos();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-12-21 20:42:46 +00:00
|
|
|
DocIterator Text::macrocontextPosition() const
|
|
|
|
{
|
|
|
|
return macrocontext_position_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Text::setMacrocontextPosition(DocIterator const & pos)
|
|
|
|
{
|
|
|
|
macrocontext_position_ = pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-03-15 12:22:28 +00:00
|
|
|
docstring Text::previousWord(CursorSlice const & sl) const
|
|
|
|
{
|
|
|
|
CursorSlice from = sl;
|
|
|
|
CursorSlice to = sl;
|
|
|
|
getWord(from, to, PREVIOUS_WORD);
|
|
|
|
if (sl == from || to == from)
|
|
|
|
return docstring();
|
2014-03-08 10:59:39 +00:00
|
|
|
|
2008-03-15 12:22:28 +00:00
|
|
|
Paragraph const & par = sl.paragraph();
|
2008-05-22 14:01:33 +00:00
|
|
|
return par.asString(from.pos(), to.pos());
|
2008-03-15 12:22:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Text::completionSupported(Cursor const & cur) const
|
|
|
|
{
|
|
|
|
Paragraph const & par = cur.paragraph();
|
2018-07-03 14:53:15 +00:00
|
|
|
return !cur.selection()
|
|
|
|
&& cur.pos() > 0
|
2009-06-21 14:30:57 +00:00
|
|
|
&& (cur.pos() >= par.size() || par.isWordSeparator(cur.pos()))
|
|
|
|
&& !par.isWordSeparator(cur.pos() - 1);
|
2008-03-15 12:22:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CompletionList const * Text::createCompletionList(Cursor const & cur) const
|
|
|
|
{
|
2016-12-31 14:22:07 +00:00
|
|
|
WordList const & list = theWordList(cur.getFont().language()->lang());
|
2009-12-18 14:48:56 +00:00
|
|
|
return new TextCompletionList(cur, list);
|
2008-03-15 12:22:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Text::insertCompletion(Cursor & cur, docstring const & s, bool /*finished*/)
|
2014-03-08 10:59:39 +00:00
|
|
|
{
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(cur.bv().cursor() == cur);
|
2008-03-15 12:22:28 +00:00
|
|
|
cur.insert(s);
|
|
|
|
cur.bv().cursor() = cur;
|
2010-10-13 17:28:55 +00:00
|
|
|
if (!(cur.result().screenUpdate() & Update::Force))
|
|
|
|
cur.screenUpdateFlags(cur.result().screenUpdate() | Update::SinglePar);
|
2008-03-15 12:22:28 +00:00
|
|
|
return true;
|
|
|
|
}
|
2014-03-08 10:59:39 +00:00
|
|
|
|
|
|
|
|
2008-03-15 12:22:28 +00:00
|
|
|
docstring Text::completionPrefix(Cursor const & cur) const
|
|
|
|
{
|
|
|
|
return previousWord(cur.top());
|
|
|
|
}
|
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
} // namespace lyx
|