2008-02-24 14:59:23 +00:00
|
|
|
/**
|
2007-04-26 04:41:58 +00:00
|
|
|
* \file CutAndPaste.cpp
|
2003-05-06 09:34:56 +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
|
|
|
*
|
2008-11-14 15:58:50 +00:00
|
|
|
* \author Jürgen Vigna
|
|
|
|
* \author Lars Gullik Bjønnes
|
2003-06-28 01:23:11 +00:00
|
|
|
* \author Alfredo Braunstein
|
2006-11-23 22:20:38 +00:00
|
|
|
* \author Michael Gerz
|
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.
|
2003-05-06 09:34:56 +00:00
|
|
|
*/
|
2000-04-10 14:29:05 +00:00
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include "CutAndPaste.h"
|
2003-09-06 18:38:02 +00:00
|
|
|
|
2009-07-09 09:48:34 +00:00
|
|
|
#include "BranchList.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "Buffer.h"
|
2004-03-25 09:16:36 +00:00
|
|
|
#include "buffer_funcs.h"
|
2008-07-14 08:48:25 +00:00
|
|
|
#include "BufferList.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "BufferParams.h"
|
2007-07-10 12:44:19 +00:00
|
|
|
#include "BufferView.h"
|
2007-10-18 15:29:51 +00:00
|
|
|
#include "Changes.h"
|
2007-04-26 14:56:30 +00:00
|
|
|
#include "Cursor.h"
|
2013-04-12 20:12:47 +00:00
|
|
|
#include "Encoding.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "ErrorList.h"
|
2008-03-15 01:20:36 +00:00
|
|
|
#include "FuncCode.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "FuncRequest.h"
|
|
|
|
#include "InsetIterator.h"
|
2007-10-18 15:29:51 +00:00
|
|
|
#include "InsetList.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "Language.h"
|
2010-02-09 16:11:13 +00:00
|
|
|
#include "LyX.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "LyXRC.h"
|
2007-04-29 23:33:02 +00:00
|
|
|
#include "Text.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "Paragraph.h"
|
2003-09-06 18:38:02 +00:00
|
|
|
#include "ParagraphParameters.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "ParIterator.h"
|
2010-10-31 01:04:03 +00:00
|
|
|
#include "TextClass.h"
|
2003-09-06 18:38:02 +00:00
|
|
|
|
2010-10-25 23:20:05 +00:00
|
|
|
#include "insets/InsetBibitem.h"
|
2009-07-09 09:48:34 +00:00
|
|
|
#include "insets/InsetBranch.h"
|
2017-12-06 08:11:18 +00:00
|
|
|
#include "insets/InsetCitation.h"
|
2008-03-03 09:35:26 +00:00
|
|
|
#include "insets/InsetCommand.h"
|
2009-07-09 09:48:34 +00:00
|
|
|
#include "insets/InsetFlex.h"
|
2008-02-03 10:43:03 +00:00
|
|
|
#include "insets/InsetGraphics.h"
|
|
|
|
#include "insets/InsetGraphicsParams.h"
|
2009-02-19 04:50:25 +00:00
|
|
|
#include "insets/InsetInclude.h"
|
2010-01-27 08:41:19 +00:00
|
|
|
#include "insets/InsetLabel.h"
|
2007-04-25 01:24:38 +00:00
|
|
|
#include "insets/InsetTabular.h"
|
2000-04-10 14:29:05 +00:00
|
|
|
|
2007-04-26 16:06:39 +00:00
|
|
|
#include "mathed/MathData.h"
|
2006-09-17 09:14:18 +00:00
|
|
|
#include "mathed/InsetMath.h"
|
2010-01-27 08:41:19 +00:00
|
|
|
#include "mathed/InsetMathHull.h"
|
2010-01-28 17:37:22 +00:00
|
|
|
#include "mathed/InsetMathRef.h"
|
2006-09-17 09:14:18 +00:00
|
|
|
#include "mathed/MathSupport.h"
|
2004-04-18 07:32:34 +00:00
|
|
|
|
2007-11-29 07:04:28 +00:00
|
|
|
#include "support/debug.h"
|
|
|
|
#include "support/docstream.h"
|
|
|
|
#include "support/gettext.h"
|
2011-07-14 17:00:35 +00:00
|
|
|
#include "support/lassert.h"
|
2007-10-19 14:30:56 +00:00
|
|
|
#include "support/limited_stack.h"
|
2003-05-13 09:48:57 +00:00
|
|
|
#include "support/lstrings.h"
|
2015-05-15 22:05:23 +00:00
|
|
|
#include "support/lyxalgo.h"
|
2014-06-09 11:05:50 +00:00
|
|
|
#include "support/TempFile.h"
|
2016-06-02 22:49:36 +00:00
|
|
|
#include "support/unique_ptr.h"
|
2002-12-01 21:10:37 +00:00
|
|
|
|
2009-07-09 09:48:34 +00:00
|
|
|
#include "frontends/alert.h"
|
2006-07-03 20:19:33 +00:00
|
|
|
#include "frontends/Clipboard.h"
|
2007-01-07 14:44:44 +00:00
|
|
|
#include "frontends/Selection.h"
|
2006-07-03 20:19:33 +00:00
|
|
|
|
2007-01-16 14:31:07 +00:00
|
|
|
#include <string>
|
2016-06-02 19:06:59 +00:00
|
|
|
#include <tuple>
|
2007-01-16 14:31:07 +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;
|
2008-02-03 10:43:03 +00:00
|
|
|
using lyx::frontend::Clipboard;
|
2003-06-17 15:33:49 +00:00
|
|
|
|
2007-01-16 14:45:12 +00:00
|
|
|
namespace lyx {
|
|
|
|
|
2001-03-20 01:22:46 +00:00
|
|
|
namespace {
|
|
|
|
|
2007-12-12 19:28:07 +00:00
|
|
|
typedef pair<pit_type, int> PitPosPair;
|
2004-03-25 09:16:36 +00:00
|
|
|
|
2012-03-13 16:13:31 +00:00
|
|
|
typedef limited_stack<pair<ParagraphList, DocumentClassConstPtr> > CutStack;
|
2004-03-25 09:16:36 +00:00
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
CutStack theCuts(10);
|
2007-02-02 03:10:15 +00:00
|
|
|
// persistent selection, cleared until the next selection
|
|
|
|
CutStack selectionBuffer(1);
|
2016-11-09 14:13:41 +00:00
|
|
|
// temporary scratch area
|
|
|
|
CutStack tempCut(1);
|
2000-04-10 14:29:05 +00:00
|
|
|
|
2005-09-06 17:39:39 +00:00
|
|
|
// store whether the tabular stack is newer than the normal copy stack
|
2006-04-05 23:56:29 +00:00
|
|
|
// FIXME: this is a workaround for bug 1919. Should be removed for 1.5,
|
2005-09-06 17:39:39 +00:00
|
|
|
// when we (hopefully) have a one-for-all paste mechanism.
|
2007-01-04 11:00:09 +00:00
|
|
|
bool dirty_tabular_stack_ = false;
|
2005-09-06 17:39:39 +00:00
|
|
|
|
2000-04-11 22:55:29 +00:00
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
bool checkPastePossible(int index)
|
|
|
|
{
|
|
|
|
return size_t(index) < theCuts.size() && !theCuts[index].first.empty();
|
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
|
2013-04-21 01:07:07 +00:00
|
|
|
struct PasteReturnValue {
|
2016-02-29 12:29:06 +00:00
|
|
|
PasteReturnValue(pit_type r_pit, pos_type r_pos, bool r_nu) :
|
|
|
|
pit(r_pit), pos(r_pos), needupdate(r_nu)
|
2013-04-21 01:07:07 +00:00
|
|
|
{}
|
|
|
|
|
2016-02-29 12:29:06 +00:00
|
|
|
pit_type pit;
|
2013-04-21 01:07:07 +00:00
|
|
|
pos_type pos;
|
|
|
|
bool needupdate;
|
|
|
|
};
|
|
|
|
|
|
|
|
PasteReturnValue
|
|
|
|
pasteSelectionHelper(DocIterator const & cur, ParagraphList const & parlist,
|
2016-11-09 14:13:41 +00:00
|
|
|
DocumentClassConstPtr oldDocClass, cap::BranchAction branchAction,
|
2013-05-01 11:51:39 +00:00
|
|
|
ErrorList & errorlist)
|
2004-03-25 09:16:36 +00:00
|
|
|
{
|
2008-11-17 11:46:07 +00:00
|
|
|
Buffer const & buffer = *cur.buffer();
|
2006-09-19 09:39:17 +00:00
|
|
|
pit_type pit = cur.pit();
|
|
|
|
pos_type pos = cur.pos();
|
2013-04-21 01:07:07 +00:00
|
|
|
bool need_update = false;
|
2014-08-12 17:13:47 +00:00
|
|
|
|
|
|
|
if (parlist.empty())
|
|
|
|
return PasteReturnValue(pit, pos, need_update);
|
|
|
|
|
2019-01-25 13:40:30 +00:00
|
|
|
// Check whether we paste into an inset that does not
|
|
|
|
// produce output (needed for label duplicate check)
|
|
|
|
bool in_active_inset = cur.paragraph().inInset().producesOutput();
|
|
|
|
if (in_active_inset) {
|
|
|
|
for (size_type sl = 0 ; sl < cur.depth() ; ++sl) {
|
|
|
|
Paragraph const & outer_par = cur[sl].paragraph();
|
|
|
|
if (!outer_par.inInset().producesOutput()) {
|
|
|
|
in_active_inset = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-12 13:36:40 +00:00
|
|
|
InsetText * target_inset = cur.inset().asInsetText();
|
2008-09-15 17:03:15 +00:00
|
|
|
if (!target_inset) {
|
|
|
|
InsetTabular * it = cur.inset().asInsetTabular();
|
2019-09-12 02:13:25 +00:00
|
|
|
target_inset = it ? it->cell(cur.idx())->asInsetText() : nullptr;
|
2008-09-15 17:03:15 +00:00
|
|
|
}
|
2013-04-21 01:07:07 +00:00
|
|
|
LASSERT(target_inset, return PasteReturnValue(pit, pos, need_update));
|
2006-09-19 09:39:17 +00:00
|
|
|
|
2014-08-12 17:13:47 +00:00
|
|
|
ParagraphList & pars = target_inset->paragraphs();
|
|
|
|
LASSERT(pos <= pars[pit].size(),
|
|
|
|
return PasteReturnValue(pit, pos, need_update));
|
2005-07-16 15:22:07 +00:00
|
|
|
|
|
|
|
// Make a copy of the CaP paragraphs.
|
2005-11-29 15:08:35 +00:00
|
|
|
ParagraphList insertion = parlist;
|
2005-07-16 15:22:07 +00:00
|
|
|
|
|
|
|
// Now remove all out of the pars which is NOT allowed in the
|
|
|
|
// new environment and set also another font if that is required.
|
|
|
|
|
2018-05-11 11:17:39 +00:00
|
|
|
// Merge paragraphs that are to be pasted into a text inset
|
|
|
|
// that does not allow multiple pars.
|
|
|
|
InsetText * inset_text = target_inset->asInsetText();
|
|
|
|
if (inset_text && !inset_text->allowMultiPar()) {
|
|
|
|
while (insertion.size() > 1)
|
|
|
|
mergeParagraph(buffer.params(), insertion, 0);
|
|
|
|
}
|
|
|
|
|
2010-11-06 15:06:19 +00:00
|
|
|
// Convert newline to paragraph break in ParbreakIsNewline
|
|
|
|
if (target_inset->getLayout().parbreakIsNewline()
|
|
|
|
|| pars[pit].layout().parbreak_is_newline) {
|
2008-02-23 16:45:38 +00:00
|
|
|
for (size_t i = 0; i != insertion.size(); ++i) {
|
|
|
|
for (pos_type j = 0; j != insertion[i].size(); ++j) {
|
2005-07-16 15:22:07 +00:00
|
|
|
if (insertion[i].isNewline(j)) {
|
2006-10-19 17:46:50 +00:00
|
|
|
// do not track deletion of newline
|
2006-10-21 17:05:20 +00:00
|
|
|
insertion[i].eraseChar(j, false);
|
2008-09-13 17:01:54 +00:00
|
|
|
insertion[i].setInsetOwner(target_inset);
|
2005-07-16 15:22:07 +00:00
|
|
|
breakParagraphConservative(
|
|
|
|
buffer.params(),
|
|
|
|
insertion, i, j);
|
2008-07-21 08:11:10 +00:00
|
|
|
break;
|
2005-02-03 17:24:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2005-07-16 15:22:07 +00:00
|
|
|
}
|
2005-02-03 17:24:40 +00:00
|
|
|
|
2017-12-24 16:14:05 +00:00
|
|
|
// Prevent to paste uncodable characters in verbatim and ERT.
|
|
|
|
// The encoding is inherited from the context here.
|
|
|
|
docstring uncodable_content;
|
|
|
|
if (target_inset->getLayout().isPassThru() && cur.getEncoding()) {
|
|
|
|
odocstringstream res;
|
|
|
|
Encoding const * e = cur.getEncoding();
|
|
|
|
for (size_t i = 0; i != insertion.size(); ++i) {
|
|
|
|
pos_type end = insertion[i].size();
|
|
|
|
for (pos_type j = 0; j != end; ++j) {
|
2018-02-17 11:27:26 +00:00
|
|
|
// skip insets
|
|
|
|
if (insertion[i].isInset(j))
|
|
|
|
continue;
|
2017-12-24 16:14:05 +00:00
|
|
|
char_type const c = insertion[i].getChar(j);
|
|
|
|
if (!e->encodable(c)) {
|
|
|
|
// do not track deletion
|
2018-01-25 08:40:36 +00:00
|
|
|
res.put(c);
|
2017-12-24 16:14:05 +00:00
|
|
|
insertion[i].eraseChar(j, false);
|
|
|
|
--end;
|
|
|
|
--j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
docstring const uncodable = res.str();
|
|
|
|
if (!uncodable.empty()) {
|
|
|
|
if (uncodable.size() == 1)
|
|
|
|
uncodable_content = bformat(_("The character \"%1$s\" is uncodable in this verbatim context "
|
|
|
|
"and thus has not been pasted."),
|
|
|
|
uncodable);
|
|
|
|
else
|
|
|
|
uncodable_content = bformat(_("The characters \"%1$s\" are uncodable in this verbatim context "
|
|
|
|
"and thus have not been pasted."),
|
|
|
|
uncodable);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-01 21:58:27 +00:00
|
|
|
// set the paragraphs to plain layout if necessary
|
2014-08-12 17:13:47 +00:00
|
|
|
DocumentClassConstPtr newDocClass = buffer.params().documentClassPtr();
|
2008-07-10 17:41:52 +00:00
|
|
|
if (cur.inset().usePlainLayout()) {
|
2016-06-30 01:23:39 +00:00
|
|
|
bool forcePlainLayout = target_inset->forcePlainLayout();
|
2008-08-01 21:58:27 +00:00
|
|
|
Layout const & plainLayout = newDocClass->plainLayout();
|
2008-05-22 14:06:23 +00:00
|
|
|
Layout const & defaultLayout = newDocClass->defaultLayout();
|
2020-02-28 06:55:05 +00:00
|
|
|
for (auto & par : insertion) {
|
|
|
|
Layout const & parLayout = par.layout();
|
2008-07-10 17:41:52 +00:00
|
|
|
if (forcePlainLayout || parLayout == defaultLayout)
|
2020-02-28 06:55:05 +00:00
|
|
|
par.setLayout(plainLayout);
|
2008-05-22 14:06:23 +00:00
|
|
|
}
|
2014-11-12 00:01:45 +00:00
|
|
|
} else {
|
|
|
|
// check if we need to reset from plain layout
|
2008-05-22 14:06:23 +00:00
|
|
|
Layout const & defaultLayout = newDocClass->defaultLayout();
|
2008-08-01 21:58:27 +00:00
|
|
|
Layout const & plainLayout = newDocClass->plainLayout();
|
2020-02-28 06:55:05 +00:00
|
|
|
for (auto & par : insertion) {
|
|
|
|
Layout const & parLayout = par.layout();
|
2008-08-01 21:58:27 +00:00
|
|
|
if (parLayout == plainLayout)
|
2020-02-28 06:55:05 +00:00
|
|
|
par.setLayout(defaultLayout);
|
2008-05-22 14:06:23 +00:00
|
|
|
}
|
2006-09-19 09:39:17 +00:00
|
|
|
}
|
|
|
|
|
2009-11-08 15:53:21 +00:00
|
|
|
InsetText in(cur.buffer());
|
2005-07-16 15:22:07 +00:00
|
|
|
// Make sure there is no class difference.
|
2008-07-29 12:07:08 +00:00
|
|
|
in.paragraphs().clear();
|
2006-05-21 17:33:03 +00:00
|
|
|
// This works without copying any paragraph data because we have
|
|
|
|
// a specialized swap method for ParagraphList. This is important
|
|
|
|
// since we store pointers to insets at some places and we don't
|
|
|
|
// want to invalidate them.
|
|
|
|
insertion.swap(in.paragraphs());
|
2008-05-22 14:06:23 +00:00
|
|
|
cap::switchBetweenClasses(oldDocClass, newDocClass, in, errorlist);
|
2017-12-24 16:14:05 +00:00
|
|
|
// Do this here since switchBetweenClasses clears the errorlist
|
|
|
|
if (!uncodable_content.empty())
|
|
|
|
errorlist.push_back(ErrorItem(_("Uncodable content"), uncodable_content));
|
2006-05-21 17:33:03 +00:00
|
|
|
insertion.swap(in.paragraphs());
|
2005-07-16 15:22:07 +00:00
|
|
|
|
|
|
|
ParagraphList::iterator tmpbuf = insertion.begin();
|
|
|
|
int depth_delta = pars[pit].params().depth() - tmpbuf->params().depth();
|
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
depth_type max_depth = pars[pit].getMaxDepthAfter();
|
2005-07-16 15:22:07 +00:00
|
|
|
|
|
|
|
for (; tmpbuf != insertion.end(); ++tmpbuf) {
|
|
|
|
// If we have a negative jump so that the depth would
|
|
|
|
// go below 0 depth then we have to redo the delta to
|
|
|
|
// this new max depth level so that subsequent
|
|
|
|
// paragraphs are aligned correctly to this paragraph
|
|
|
|
// at level 0.
|
|
|
|
if (int(tmpbuf->params().depth()) + depth_delta < 0)
|
|
|
|
depth_delta = 0;
|
|
|
|
|
|
|
|
// Set the right depth so that we are not too deep or shallow.
|
|
|
|
tmpbuf->params().depth(tmpbuf->params().depth() + depth_delta);
|
|
|
|
if (tmpbuf->params().depth() > max_depth)
|
|
|
|
tmpbuf->params().depth(max_depth);
|
|
|
|
|
2014-02-22 10:02:50 +00:00
|
|
|
// Set max_depth for the next paragraph
|
|
|
|
max_depth = tmpbuf->getMaxDepthAfter();
|
2005-07-16 15:22:07 +00:00
|
|
|
|
|
|
|
// Set the inset owner of this paragraph.
|
2008-09-12 13:36:40 +00:00
|
|
|
tmpbuf->setInsetOwner(target_inset);
|
2005-07-16 15:22:07 +00:00
|
|
|
for (pos_type i = 0; i < tmpbuf->size(); ++i) {
|
2008-02-09 10:41:49 +00:00
|
|
|
// do not track deletion of invalid insets
|
|
|
|
if (Inset * inset = tmpbuf->getInset(i))
|
2008-09-13 15:32:26 +00:00
|
|
|
if (!target_inset->insetAllowed(inset->lyxCode()))
|
2008-02-09 10:41:49 +00:00
|
|
|
tmpbuf->eraseChar(i--, false);
|
2004-03-25 09:16:36 +00:00
|
|
|
}
|
2006-04-13 16:47:57 +00:00
|
|
|
|
2020-01-13 14:20:48 +00:00
|
|
|
if (lyxrc.ct_markup_copied)
|
|
|
|
tmpbuf->setChange(Change(buffer.params().track_changes ?
|
|
|
|
Change::INSERTED : Change::UNCHANGED));
|
2005-07-16 15:22:07 +00:00
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
|
2005-07-16 15:22:07 +00:00
|
|
|
bool const empty = pars[pit].empty();
|
|
|
|
if (!empty) {
|
|
|
|
// Make the buf exactly the same layout as the cursor
|
|
|
|
// paragraph.
|
|
|
|
insertion.begin()->makeSameLayout(pars[pit]);
|
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
|
2005-07-16 15:22:07 +00:00
|
|
|
// Prepare the paragraphs and insets for insertion.
|
2006-05-21 17:33:03 +00:00
|
|
|
insertion.swap(in.paragraphs());
|
2003-06-17 15:33:49 +00:00
|
|
|
|
2008-03-03 09:35:26 +00:00
|
|
|
InsetIterator const i_end = inset_iterator_end(in);
|
|
|
|
for (InsetIterator it = inset_iterator_begin(in); it != i_end; ++it) {
|
2017-07-03 17:53:14 +00:00
|
|
|
// Even though this will also be done later, it has to be done here
|
2019-09-12 02:13:25 +00:00
|
|
|
// since some inset might try to access the buffer() member.
|
2009-04-04 13:48:30 +00:00
|
|
|
it->setBuffer(const_cast<Buffer &>(buffer));
|
2008-03-03 09:35:26 +00:00
|
|
|
switch (it->lyxCode()) {
|
2009-04-04 13:48:30 +00:00
|
|
|
|
2010-01-27 08:41:19 +00:00
|
|
|
case MATH_HULL_CODE: {
|
|
|
|
// check for equation labels and resolve duplicates
|
2010-10-26 16:45:21 +00:00
|
|
|
InsetMathHull * ins = it->asInsetMath()->asHullInset();
|
|
|
|
std::vector<InsetLabel *> labels = ins->getLabels();
|
2010-01-27 08:41:19 +00:00
|
|
|
for (size_t i = 0; i != labels.size(); ++i) {
|
|
|
|
if (!labels[i])
|
|
|
|
continue;
|
|
|
|
InsetLabel * lab = labels[i];
|
|
|
|
docstring const oldname = lab->getParam("name");
|
2019-01-25 13:40:30 +00:00
|
|
|
lab->updateLabel(oldname, in_active_inset);
|
2010-07-09 14:37:00 +00:00
|
|
|
// We need to update the buffer reference cache.
|
2013-04-21 01:07:07 +00:00
|
|
|
need_update = true;
|
2010-01-27 08:41:19 +00:00
|
|
|
docstring const newname = lab->getParam("name");
|
2010-01-28 17:37:22 +00:00
|
|
|
if (oldname == newname)
|
|
|
|
continue;
|
|
|
|
// adapt the references
|
|
|
|
for (InsetIterator itt = inset_iterator_begin(in);
|
|
|
|
itt != i_end; ++itt) {
|
|
|
|
if (itt->lyxCode() == REF_CODE) {
|
2010-10-26 16:45:21 +00:00
|
|
|
InsetCommand * ref = itt->asInsetCommand();
|
|
|
|
if (ref->getParam("reference") == oldname)
|
|
|
|
ref->setParam("reference", newname);
|
2010-01-28 17:37:22 +00:00
|
|
|
} else if (itt->lyxCode() == MATH_REF_CODE) {
|
2012-03-23 20:15:55 +00:00
|
|
|
InsetMathRef * mi = itt->asInsetMath()->asRefInset();
|
2010-01-28 17:37:22 +00:00
|
|
|
// this is necessary to prevent an uninitialized
|
|
|
|
// buffer when the RefInset is in a MathBox.
|
2010-07-09 14:37:00 +00:00
|
|
|
// FIXME audit setBuffer calls
|
2010-10-26 16:45:21 +00:00
|
|
|
mi->setBuffer(const_cast<Buffer &>(buffer));
|
2012-03-23 20:15:55 +00:00
|
|
|
if (mi->getTarget() == oldname)
|
|
|
|
mi->changeTarget(newname);
|
2010-01-27 08:41:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-03-03 09:35:26 +00:00
|
|
|
case LABEL_CODE: {
|
|
|
|
// check for duplicates
|
Remove unneccessary uses of dynamic_cast from the code.
A dynamic_cast is necessary when:
- the object to be casted is from an external library because we can't add Qxxx::asXxxx() to Qt e.g.:
* QAbstractListModel to GuiIdListModel,
* QValidator to PathValidator,
* QWidget to TabWorkArea,
* QWidget to GuiWorkArea;
- the object is to be casted from an interface to the implementing class, because the Interface does not know by whom it is implemented:
* ProgressInterface to GuiProgress,
* Application to GuiApplication.
A dynamic_cast can be replaced by:
- already existing as***Inset() functions, e.g.:
* asHullInset(),
* asInsetMath()->asMacro(),
* asInsetText();
- a static_cast when we are sure this can't go wrong, e.g.:
* we are sure that CellData::inset->clone() is an InsetTableCell,
* in cases where we explicitly check it->lyxCode().
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@35855 a592a061-630c-0410-9148-cb99ea01b6c8
2010-10-26 15:03:51 +00:00
|
|
|
InsetLabel & lab = static_cast<InsetLabel &>(*it);
|
2008-03-03 09:35:26 +00:00
|
|
|
docstring const oldname = lab.getParam("name");
|
2019-01-25 13:40:30 +00:00
|
|
|
lab.updateLabel(oldname, in_active_inset);
|
2010-07-09 14:37:00 +00:00
|
|
|
// We need to update the buffer reference cache.
|
2013-04-21 01:07:07 +00:00
|
|
|
need_update = true;
|
2008-03-03 09:35:26 +00:00
|
|
|
docstring const newname = lab.getParam("name");
|
2010-01-28 17:37:22 +00:00
|
|
|
if (oldname == newname)
|
|
|
|
break;
|
|
|
|
// adapt the references
|
|
|
|
for (InsetIterator itt = inset_iterator_begin(in); itt != i_end; ++itt) {
|
|
|
|
if (itt->lyxCode() == REF_CODE) {
|
|
|
|
InsetCommand & ref = static_cast<InsetCommand &>(*itt);
|
|
|
|
if (ref.getParam("reference") == oldname)
|
|
|
|
ref.setParam("reference", newname);
|
|
|
|
} else if (itt->lyxCode() == MATH_REF_CODE) {
|
2012-03-23 20:15:55 +00:00
|
|
|
InsetMathRef * mi = itt->asInsetMath()->asRefInset();
|
2010-01-28 17:37:22 +00:00
|
|
|
// this is necessary to prevent an uninitialized
|
|
|
|
// buffer when the RefInset is in a MathBox.
|
2010-07-09 14:37:00 +00:00
|
|
|
// FIXME audit setBuffer calls
|
2012-03-23 20:15:55 +00:00
|
|
|
mi->setBuffer(const_cast<Buffer &>(buffer));
|
|
|
|
if (mi->getTarget() == oldname)
|
|
|
|
mi->changeTarget(newname);
|
2008-03-03 09:35:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2004-04-18 07:32:34 +00:00
|
|
|
|
2009-02-19 04:50:25 +00:00
|
|
|
case INCLUDE_CODE: {
|
|
|
|
InsetInclude & inc = static_cast<InsetInclude &>(*it);
|
|
|
|
inc.updateCommand();
|
2010-07-09 14:37:00 +00:00
|
|
|
// We need to update the list of included files.
|
2013-04-21 01:07:07 +00:00
|
|
|
need_update = true;
|
2009-02-19 04:50:25 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-12-06 08:11:18 +00:00
|
|
|
case CITE_CODE: {
|
|
|
|
InsetCitation & cit = static_cast<InsetCitation &>(*it);
|
|
|
|
// This actually only needs to be done if the cite engine
|
|
|
|
// differs, but we do it in general.
|
|
|
|
cit.redoLabel();
|
|
|
|
// We need to update the list of citations.
|
|
|
|
need_update = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-03-04 06:41:47 +00:00
|
|
|
case BIBITEM_CODE: {
|
|
|
|
// check for duplicates
|
Remove unneccessary uses of dynamic_cast from the code.
A dynamic_cast is necessary when:
- the object to be casted is from an external library because we can't add Qxxx::asXxxx() to Qt e.g.:
* QAbstractListModel to GuiIdListModel,
* QValidator to PathValidator,
* QWidget to TabWorkArea,
* QWidget to GuiWorkArea;
- the object is to be casted from an interface to the implementing class, because the Interface does not know by whom it is implemented:
* ProgressInterface to GuiProgress,
* Application to GuiApplication.
A dynamic_cast can be replaced by:
- already existing as***Inset() functions, e.g.:
* asHullInset(),
* asInsetMath()->asMacro(),
* asInsetText();
- a static_cast when we are sure this can't go wrong, e.g.:
* we are sure that CellData::inset->clone() is an InsetTableCell,
* in cases where we explicitly check it->lyxCode().
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@35855 a592a061-630c-0410-9148-cb99ea01b6c8
2010-10-26 15:03:51 +00:00
|
|
|
InsetBibitem & bib = static_cast<InsetBibitem &>(*it);
|
2008-03-04 06:41:47 +00:00
|
|
|
docstring const oldkey = bib.getParam("key");
|
|
|
|
bib.updateCommand(oldkey, false);
|
2010-07-09 14:37:00 +00:00
|
|
|
// We need to update the buffer reference cache.
|
2013-04-21 01:07:07 +00:00
|
|
|
need_update = true;
|
2008-03-04 06:41:47 +00:00
|
|
|
docstring const newkey = bib.getParam("key");
|
2010-01-28 17:37:22 +00:00
|
|
|
if (oldkey == newkey)
|
|
|
|
break;
|
|
|
|
// adapt the references
|
|
|
|
for (InsetIterator itt = inset_iterator_begin(in);
|
|
|
|
itt != i_end; ++itt) {
|
|
|
|
if (itt->lyxCode() == CITE_CODE) {
|
2010-10-26 16:45:21 +00:00
|
|
|
InsetCommand * ref = itt->asInsetCommand();
|
|
|
|
if (ref->getParam("key") == oldkey)
|
|
|
|
ref->setParam("key", newkey);
|
2008-03-04 06:41:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-07-09 09:48:34 +00:00
|
|
|
case BRANCH_CODE: {
|
|
|
|
// check if branch is known to target buffer
|
|
|
|
// or its master
|
|
|
|
InsetBranch & br = static_cast<InsetBranch &>(*it);
|
|
|
|
docstring const name = br.branch();
|
|
|
|
if (name.empty())
|
|
|
|
break;
|
|
|
|
bool const is_child = (&buffer != buffer.masterBuffer());
|
|
|
|
BranchList branchlist = buffer.params().branchlist();
|
|
|
|
if ((!is_child && branchlist.find(name))
|
|
|
|
|| (is_child && (branchlist.find(name)
|
|
|
|
|| buffer.masterBuffer()->params().branchlist().find(name))))
|
|
|
|
break;
|
2016-11-09 14:13:41 +00:00
|
|
|
switch(branchAction) {
|
|
|
|
case cap::BRANCH_ADD: {
|
2013-05-01 11:51:39 +00:00
|
|
|
// This is for a temporary buffer, so simply create the branch.
|
|
|
|
// Must not use lyx::dispatch(), since tmpbuffer has no view.
|
|
|
|
DispatchResult dr;
|
2016-11-09 14:13:41 +00:00
|
|
|
const_cast<Buffer&>(buffer).dispatch(FuncRequest(LFUN_BRANCH_ADD, name), dr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case cap::BRANCH_ASK: {
|
2013-05-01 11:51:39 +00:00
|
|
|
docstring text = bformat(
|
2009-07-09 09:48:34 +00:00
|
|
|
_("The pasted branch \"%1$s\" is undefined.\n"
|
|
|
|
"Do you want to add it to the document's branch list?"),
|
|
|
|
name);
|
2013-05-01 11:51:39 +00:00
|
|
|
if (frontend::Alert::prompt(_("Unknown branch"),
|
2009-07-09 09:48:34 +00:00
|
|
|
text, 0, 1, _("&Add"), _("&Don't Add")) != 0)
|
2013-05-01 11:51:39 +00:00
|
|
|
break;
|
|
|
|
lyx::dispatch(FuncRequest(LFUN_BRANCH_ADD, name));
|
2016-11-09 14:13:41 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case cap::BRANCH_IGNORE:
|
|
|
|
break;
|
2013-05-01 11:51:39 +00:00
|
|
|
}
|
2010-07-09 14:37:00 +00:00
|
|
|
// We need to update the list of branches.
|
2013-04-21 01:07:07 +00:00
|
|
|
need_update = true;
|
2009-07-09 09:48:34 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-03-03 09:35:26 +00:00
|
|
|
default:
|
|
|
|
break; // nothing
|
|
|
|
}
|
2005-07-16 15:22:07 +00:00
|
|
|
}
|
2006-05-21 17:33:03 +00:00
|
|
|
insertion.swap(in.paragraphs());
|
2005-07-16 15:22:07 +00:00
|
|
|
|
|
|
|
// Split the paragraph for inserting the buf if necessary.
|
|
|
|
if (!empty)
|
|
|
|
breakParagraphConservative(buffer.params(), pars, pit, pos);
|
|
|
|
|
|
|
|
// Paste it!
|
|
|
|
if (empty) {
|
2020-05-11 10:31:25 +00:00
|
|
|
pars.insert(pars.iterator_at(pit),
|
|
|
|
insertion.begin(), insertion.end());
|
2005-07-16 15:22:07 +00:00
|
|
|
|
|
|
|
// merge the empty par with the last par of the insertion
|
|
|
|
mergeParagraph(buffer.params(), pars,
|
2006-04-05 23:56:29 +00:00
|
|
|
pit + insertion.size() - 1);
|
2005-07-16 15:22:07 +00:00
|
|
|
} else {
|
2020-05-11 10:31:25 +00:00
|
|
|
pars.insert(pars.iterator_at(pit + 1),
|
|
|
|
insertion.begin(), insertion.end());
|
2005-07-16 15:22:07 +00:00
|
|
|
|
|
|
|
// merge the first par of the insertion with the current par
|
|
|
|
mergeParagraph(buffer.params(), pars, pit);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store the new cursor position.
|
2009-03-07 11:45:41 +00:00
|
|
|
pit_type last_paste = pit + insertion.size() - 1;
|
|
|
|
pit_type startpit = pit;
|
2005-07-16 15:22:07 +00:00
|
|
|
pit = last_paste;
|
|
|
|
pos = pars[last_paste].size();
|
|
|
|
|
2010-03-03 22:13:45 +00:00
|
|
|
// FIXME Should we do it here, or should we let updateBuffer() do it?
|
2009-03-07 11:45:41 +00:00
|
|
|
// Set paragraph buffers. It's important to do this right away
|
|
|
|
// before something calls Inset::buffer() and causes a crash.
|
|
|
|
for (pit_type p = startpit; p <= pit; ++p)
|
2017-10-16 02:14:08 +00:00
|
|
|
pars[p].setInsetBuffers(const_cast<Buffer &>(buffer));
|
2009-03-07 11:45:41 +00:00
|
|
|
|
2006-03-16 06:54:08 +00:00
|
|
|
// Join (conditionally) last pasted paragraph with next one, i.e.,
|
|
|
|
// the tail of the spliced document paragraph
|
2005-07-16 15:22:07 +00:00
|
|
|
if (!empty && last_paste + 1 != pit_type(pars.size())) {
|
|
|
|
if (pars[last_paste + 1].hasSameLayout(pars[last_paste])) {
|
|
|
|
mergeParagraph(buffer.params(), pars, last_paste);
|
|
|
|
} else if (pars[last_paste + 1].empty()) {
|
|
|
|
pars[last_paste + 1].makeSameLayout(pars[last_paste]);
|
|
|
|
mergeParagraph(buffer.params(), pars, last_paste);
|
|
|
|
} else if (pars[last_paste].empty()) {
|
|
|
|
pars[last_paste].makeSameLayout(pars[last_paste + 1]);
|
|
|
|
mergeParagraph(buffer.params(), pars, last_paste);
|
2004-04-18 07:32:34 +00:00
|
|
|
} else {
|
2014-03-29 22:52:36 +00:00
|
|
|
pars[last_paste + 1].stripLeadingSpaces(buffer.params().track_changes);
|
2005-07-16 15:22:07 +00:00
|
|
|
++last_paste;
|
2004-04-18 07:32:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-21 01:07:07 +00:00
|
|
|
return PasteReturnValue(pit, pos, need_update);
|
2003-06-17 15:33:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
PitPosPair eraseSelectionHelper(BufferParams const & params,
|
|
|
|
ParagraphList & pars,
|
2004-11-24 21:53:46 +00:00
|
|
|
pit_type startpit, pit_type endpit,
|
2007-08-30 12:28:22 +00:00
|
|
|
int startpos, int endpos)
|
2001-04-17 13:48:09 +00:00
|
|
|
{
|
2006-04-05 23:56:29 +00:00
|
|
|
// Start of selection is really invalid.
|
2004-11-24 21:53:46 +00:00
|
|
|
if (startpit == pit_type(pars.size()) ||
|
2004-03-27 01:18:51 +00:00
|
|
|
(startpos > pars[startpit].size()))
|
2003-04-30 10:32:01 +00:00
|
|
|
return PitPosPair(endpit, endpos);
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2006-04-05 23:56:29 +00:00
|
|
|
// Start and end is inside same paragraph
|
2006-10-23 20:08:18 +00:00
|
|
|
if (endpit == pit_type(pars.size()) || startpit == endpit) {
|
2014-03-29 22:52:36 +00:00
|
|
|
endpos -= pars[startpit].eraseChars(startpos, endpos, params.track_changes);
|
2003-04-30 10:32:01 +00:00
|
|
|
return PitPosPair(endpit, endpos);
|
2003-04-29 09:40:49 +00:00
|
|
|
}
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2006-03-11 13:31:41 +00:00
|
|
|
for (pit_type pit = startpit; pit != endpit + 1;) {
|
2006-11-23 21:46:39 +00:00
|
|
|
pos_type const left = (pit == startpit ? startpos : 0);
|
2007-05-14 03:07:00 +00:00
|
|
|
pos_type right = (pit == endpit ? endpos : pars[pit].size() + 1);
|
2014-03-29 22:52:36 +00:00
|
|
|
bool const merge = pars[pit].isMergedOnEndOfParDeletion(params.track_changes);
|
2006-11-24 21:22:57 +00:00
|
|
|
|
2006-11-23 22:20:38 +00:00
|
|
|
// Logically erase only, including the end-of-paragraph character
|
2014-03-29 22:52:36 +00:00
|
|
|
pars[pit].eraseChars(left, right, params.track_changes);
|
2006-11-23 22:20:38 +00:00
|
|
|
|
|
|
|
// Separate handling of paragraph break:
|
2006-03-11 13:31:41 +00:00
|
|
|
if (merge && pit != endpit &&
|
2017-07-03 17:53:14 +00:00
|
|
|
(pit + 1 != endpit
|
2008-02-18 10:16:43 +00:00
|
|
|
|| pars[pit].hasSameLayout(pars[endpit])
|
|
|
|
|| pars[endpit].size() == endpos)) {
|
2007-08-30 12:28:22 +00:00
|
|
|
if (pit + 1 == endpit)
|
|
|
|
endpos += pars[pit].size();
|
2006-03-11 13:31:41 +00:00
|
|
|
mergeParagraph(params, pars, pit);
|
|
|
|
--endpit;
|
2006-04-05 23:56:29 +00:00
|
|
|
} else
|
2006-03-11 13:31:41 +00:00
|
|
|
++pit;
|
2000-05-22 11:08:25 +00:00
|
|
|
}
|
2003-02-08 19:18:01 +00:00
|
|
|
|
2006-03-11 13:31:41 +00:00
|
|
|
// Ensure legal cursor pos:
|
|
|
|
endpit = startpit;
|
|
|
|
endpos = startpos;
|
2003-04-30 10:32:01 +00:00
|
|
|
return PitPosPair(endpit, endpos);
|
2000-04-10 14:29:05 +00:00
|
|
|
}
|
|
|
|
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2015-01-19 20:37:01 +00:00
|
|
|
Buffer * copyToTempBuffer(ParagraphList const & paragraphs, DocumentClassConstPtr docclass)
|
2007-01-13 18:29:50 +00:00
|
|
|
{
|
2012-03-13 16:13:31 +00:00
|
|
|
// This used to need to be static to avoid a memory leak. It no longer needs
|
|
|
|
// to be so, but the alternative is to construct a new one of these (with a
|
|
|
|
// new temporary directory, etc) every time, and then to destroy it. So maybe
|
|
|
|
// it's worth just keeping this one around.
|
2014-06-09 11:05:50 +00:00
|
|
|
static TempFile tempfile("clipboard.internal");
|
|
|
|
tempfile.setAutoRemove(false);
|
2016-07-30 19:28:44 +00:00
|
|
|
// The initialization of staticbuffer is thread-safe. Using a lambda
|
|
|
|
// guarantees that the properties are set only once.
|
|
|
|
static Buffer * staticbuffer = [&](){
|
|
|
|
Buffer * b =
|
|
|
|
theBufferList().newInternalBuffer(tempfile.name().absFileName());
|
|
|
|
b->setUnnamed(true);
|
|
|
|
b->inset().setBuffer(*b);
|
|
|
|
//initialize staticbuffer with b
|
|
|
|
return b;
|
|
|
|
}();
|
2013-04-21 19:15:54 +00:00
|
|
|
// Use a clone for the complicated stuff so that we do not need to clean
|
|
|
|
// up in order to avoid a crash.
|
|
|
|
Buffer * buffer = staticbuffer->cloneBufferOnly();
|
2019-09-12 02:13:25 +00:00
|
|
|
LASSERT(buffer, return nullptr);
|
2013-04-21 01:07:07 +00:00
|
|
|
|
|
|
|
// This needs doing every time.
|
2014-11-11 06:42:54 +00:00
|
|
|
// Since setDocumentClass() causes deletion of the old document class
|
|
|
|
// we need to reset all layout pointers in paragraphs (otherwise they
|
|
|
|
// would be dangling).
|
|
|
|
ParIterator const end = buffer->par_iterator_end();
|
|
|
|
for (ParIterator it = buffer->par_iterator_begin(); it != end; ++it) {
|
|
|
|
docstring const name = it->layout().name();
|
|
|
|
if (docclass->hasLayout(name))
|
|
|
|
it->setLayout((*docclass)[name]);
|
|
|
|
else
|
|
|
|
it->setPlainOrDefaultLayout(*docclass);
|
|
|
|
}
|
2008-07-14 08:48:25 +00:00
|
|
|
buffer->params().setDocumentClass(docclass);
|
2013-04-21 01:07:07 +00:00
|
|
|
|
|
|
|
// we will use pasteSelectionHelper to copy the paragraphs into the
|
|
|
|
// temporary Buffer, since it does a lot of things to fix them up.
|
|
|
|
DocIterator dit = doc_iterator_begin(buffer, &buffer->inset());
|
|
|
|
ErrorList el;
|
2016-11-09 14:13:41 +00:00
|
|
|
pasteSelectionHelper(dit, paragraphs, docclass, cap::BRANCH_ADD, el);
|
2013-04-21 01:07:07 +00:00
|
|
|
|
2015-01-19 20:37:01 +00:00
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-03 17:53:14 +00:00
|
|
|
void putClipboard(ParagraphList const & paragraphs,
|
2020-01-13 14:20:48 +00:00
|
|
|
DocumentClassConstPtr docclass, docstring const & plaintext,
|
2020-06-18 09:17:32 +00:00
|
|
|
BufferParams const & bp)
|
2015-01-19 20:37:01 +00:00
|
|
|
{
|
|
|
|
Buffer * buffer = copyToTempBuffer(paragraphs, docclass);
|
|
|
|
if (!buffer) // already asserted in copyToTempBuffer()
|
|
|
|
return;
|
|
|
|
|
2013-04-21 18:45:56 +00:00
|
|
|
// We don't want to produce images that are not used. Therefore,
|
|
|
|
// output formulas as MathML. Even if this is not understood by all
|
|
|
|
// applications, the number that can parse it should go up in the future.
|
|
|
|
buffer->params().html_math_output = BufferParams::MathML;
|
|
|
|
|
2020-01-13 14:20:48 +00:00
|
|
|
if (lyxrc.ct_markup_copied) {
|
|
|
|
// Copy authors to the params. We need those pointers.
|
|
|
|
for (Author const & a : bp.authors())
|
|
|
|
buffer->params().authors().record(a);
|
|
|
|
}
|
|
|
|
|
2013-04-28 16:47:11 +00:00
|
|
|
// Make sure MarkAsExporting is deleted before buffer is
|
|
|
|
{
|
|
|
|
// The Buffer is being used to export. This is necessary so that the
|
|
|
|
// updateMacros call will record the needed information.
|
|
|
|
MarkAsExporting mex(buffer);
|
|
|
|
|
|
|
|
buffer->updateBuffer(Buffer::UpdateMaster, OutputUpdate);
|
|
|
|
buffer->updateMacros();
|
|
|
|
buffer->updateMacroInstances(OutputUpdate);
|
|
|
|
|
|
|
|
// LyX's own format
|
|
|
|
string lyx;
|
|
|
|
ostringstream oslyx;
|
|
|
|
if (buffer->write(oslyx))
|
|
|
|
lyx = oslyx.str();
|
|
|
|
|
|
|
|
// XHTML format
|
|
|
|
odocstringstream oshtml;
|
|
|
|
OutputParams runparams(encodings.fromLyXName("utf8"));
|
2013-05-04 12:25:21 +00:00
|
|
|
// We do not need to produce images, etc.
|
|
|
|
runparams.dryrun = true;
|
2013-11-12 19:52:35 +00:00
|
|
|
// We are not interested in errors (bug 8866)
|
|
|
|
runparams.silent = true;
|
2013-04-28 16:47:11 +00:00
|
|
|
buffer->writeLyXHTMLSource(oshtml, runparams, Buffer::FullSource);
|
|
|
|
|
|
|
|
theClipboard().put(lyx, oshtml.str(), plaintext);
|
|
|
|
}
|
2013-04-21 01:07:07 +00:00
|
|
|
|
2008-07-09 21:27:15 +00:00
|
|
|
// Save that memory
|
2013-04-21 19:15:54 +00:00
|
|
|
delete buffer;
|
2007-01-13 18:29:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-09 17:45:24 +00:00
|
|
|
/// return true if the whole ParagraphList is deleted
|
|
|
|
static bool isFullyDeleted(ParagraphList const & pars)
|
|
|
|
{
|
|
|
|
pit_type const pars_size = static_cast<pit_type>(pars.size());
|
|
|
|
|
|
|
|
// check all paragraphs
|
|
|
|
for (pit_type pit = 0; pit < pars_size; ++pit) {
|
|
|
|
if (!pars[pit].empty()) // prevent assertion failure
|
|
|
|
if (!pars[pit].isDeleted(0, pars[pit].size()))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-09 16:38:19 +00:00
|
|
|
void copySelectionHelper(Buffer const & buf, Text const & text,
|
2004-11-24 21:53:46 +00:00
|
|
|
pit_type startpit, pit_type endpit,
|
2012-03-13 16:13:31 +00:00
|
|
|
int start, int end, DocumentClassConstPtr dc, CutStack & cutstack)
|
2001-04-17 13:48:09 +00:00
|
|
|
{
|
2009-08-09 16:38:19 +00:00
|
|
|
ParagraphList const & pars = text.paragraphs();
|
|
|
|
|
2013-04-25 21:27:10 +00:00
|
|
|
// In most of these cases, we can try to recover.
|
|
|
|
LASSERT(0 <= start, start = 0);
|
|
|
|
LASSERT(start <= pars[startpit].size(), start = pars[startpit].size());
|
|
|
|
LASSERT(0 <= end, end = 0);
|
|
|
|
LASSERT(end <= pars[endpit].size(), end = pars[endpit].size());
|
|
|
|
LASSERT(startpit != endpit || start <= end, return);
|
2002-03-21 17:27:08 +00:00
|
|
|
|
2003-05-01 12:53:22 +00:00
|
|
|
// Clone the paragraphs within the selection.
|
2020-05-11 10:31:25 +00:00
|
|
|
ParagraphList copy_pars(pars.iterator_at(startpit),
|
|
|
|
pars.iterator_at(endpit + 1));
|
2005-07-19 13:17:01 +00:00
|
|
|
|
2007-04-26 08:30:11 +00:00
|
|
|
// Remove the end of the last paragraph; afterwards, remove the
|
|
|
|
// beginning of the first paragraph. Keep this order - there may only
|
|
|
|
// be one paragraph! Do not track deletions here; this is an internal
|
|
|
|
// action not visible to the user
|
|
|
|
|
2007-02-09 10:27:41 +00:00
|
|
|
Paragraph & back = copy_pars.back();
|
|
|
|
back.eraseChars(end, back.size(), false);
|
|
|
|
Paragraph & front = copy_pars.front();
|
|
|
|
front.eraseChars(0, start, false);
|
|
|
|
|
2020-02-28 06:55:05 +00:00
|
|
|
for (auto & par : copy_pars) {
|
2009-07-16 08:37:32 +00:00
|
|
|
// Since we have a copy of the paragraphs, the insets
|
|
|
|
// do not have a proper buffer reference. It makes
|
|
|
|
// sense to add them temporarily, because the
|
|
|
|
// operations below depend on that (acceptChanges included).
|
2020-02-28 06:55:05 +00:00
|
|
|
par.setInsetBuffers(const_cast<Buffer &>(buf));
|
2009-07-16 08:37:32 +00:00
|
|
|
// PassThru paragraphs have the Language
|
|
|
|
// latex_language. This is invalid for others, so we
|
|
|
|
// need to change it to the buffer language.
|
2020-02-28 06:55:05 +00:00
|
|
|
if (par.isPassThru())
|
|
|
|
par.changeLanguage(buf.params(),
|
2009-07-16 08:37:32 +00:00
|
|
|
latex_language, buf.language());
|
2006-11-07 18:27:03 +00:00
|
|
|
}
|
2002-03-21 17:27:08 +00:00
|
|
|
|
2009-07-16 08:37:32 +00:00
|
|
|
// do not copy text (also nested in insets) which is marked as
|
|
|
|
// deleted, unless the whole selection was deleted
|
2020-01-13 14:20:48 +00:00
|
|
|
if (!lyxrc.ct_markup_copied) {
|
|
|
|
if (!isFullyDeleted(copy_pars))
|
|
|
|
acceptChanges(copy_pars, buf.params());
|
|
|
|
else
|
|
|
|
rejectChanges(copy_pars, buf.params());
|
|
|
|
}
|
2003-05-01 12:53:22 +00:00
|
|
|
|
2009-07-16 08:37:32 +00:00
|
|
|
|
|
|
|
// do some final cleanup now, to make sure that the paragraphs
|
|
|
|
// are not linked to something else.
|
2020-02-28 06:55:05 +00:00
|
|
|
for (auto & par : copy_pars) {
|
|
|
|
par.resetBuffer();
|
|
|
|
par.setInsetOwner(nullptr);
|
2009-07-16 08:37:32 +00:00
|
|
|
}
|
|
|
|
|
2012-03-13 16:13:31 +00:00
|
|
|
cutstack.push(make_pair(copy_pars, dc));
|
2000-04-10 14:29:05 +00:00
|
|
|
}
|
|
|
|
|
2017-07-23 11:11:54 +00:00
|
|
|
} // namespace
|
2005-07-16 15:22:07 +00:00
|
|
|
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
namespace cap {
|
2003-05-01 15:43:58 +00:00
|
|
|
|
2008-03-12 00:19:31 +00:00
|
|
|
void region(CursorSlice const & i1, CursorSlice const & i2,
|
|
|
|
Inset::row_type & r1, Inset::row_type & r2,
|
|
|
|
Inset::col_type & c1, Inset::col_type & c2)
|
|
|
|
{
|
2020-10-01 10:34:15 +00:00
|
|
|
Inset const & p = i1.inset();
|
2008-03-12 00:19:31 +00:00
|
|
|
c1 = p.col(i1.idx());
|
|
|
|
c2 = p.col(i2.idx());
|
|
|
|
if (c1 > c2)
|
|
|
|
swap(c1, c2);
|
|
|
|
r1 = p.row(i1.idx());
|
|
|
|
r2 = p.row(i2.idx());
|
|
|
|
if (r1 > r2)
|
|
|
|
swap(r1, r2);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
docstring grabAndEraseSelection(Cursor & cur)
|
2004-04-18 07:32:34 +00:00
|
|
|
{
|
|
|
|
if (!cur.selection())
|
2006-10-22 10:15:23 +00:00
|
|
|
return docstring();
|
|
|
|
docstring res = grabSelection(cur);
|
2004-04-18 07:32:34 +00:00
|
|
|
eraseSelection(cur);
|
|
|
|
return res;
|
|
|
|
}
|
2003-05-01 13:12:43 +00:00
|
|
|
|
|
|
|
|
Move some Cursor methods to CursorData
Basically, everything that does not depend on a BufferView should move
there. Some methods that do not seem to need a BufferView, like
selHandle or IdxFirst or push actually depend on it and could not be
moved.
This allows to simplify a few uses of recordUndo helpers.
- Move some methods to DocIterator: nextMath, prevMath, getPossibleLabel,
getEncoding;
- Move some methods to CursorData: setCursor, setCursorSelectionTo,
(setCursorTo|normal|reset)Anchor, (set|clear)Selection,
sel(|ection)(Begin|End), selectionAsString, info, currentState,
(mark|clear|check)NewWordPosition, fixIfBroken, sanitize, all undo
related methods, reset, isInside, leaveInset, current mode;
- kill some unused methods: macromode, replaceWord, setScreenPos, touch,
markInsert, markErase;
- Move code around to group things, and add a few comments (a lot remains to be done).
This changes lead to some related changes in other classes: removal,
change of parameter.
No intended change.
2017-07-23 16:13:33 +00:00
|
|
|
bool reduceSelectionToOneCell(CursorData & cur)
|
2008-03-12 00:18:39 +00:00
|
|
|
{
|
|
|
|
if (!cur.selection() || !cur.inMathed())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
CursorSlice i1 = cur.selBegin();
|
|
|
|
CursorSlice i2 = cur.selEnd();
|
|
|
|
if (!i1.inset().asInsetMath())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// the easy case: do nothing if only one cell is selected
|
|
|
|
if (i1.idx() == i2.idx())
|
|
|
|
return true;
|
2017-07-03 17:53:14 +00:00
|
|
|
|
2008-03-12 00:18:39 +00:00
|
|
|
cur.top().pos() = 0;
|
|
|
|
cur.resetAnchor();
|
|
|
|
cur.top().pos() = cur.top().lastpos();
|
2017-07-03 17:53:14 +00:00
|
|
|
|
2008-03-12 00:18:39 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
Move some Cursor methods to CursorData
Basically, everything that does not depend on a BufferView should move
there. Some methods that do not seem to need a BufferView, like
selHandle or IdxFirst or push actually depend on it and could not be
moved.
This allows to simplify a few uses of recordUndo helpers.
- Move some methods to DocIterator: nextMath, prevMath, getPossibleLabel,
getEncoding;
- Move some methods to CursorData: setCursor, setCursorSelectionTo,
(setCursorTo|normal|reset)Anchor, (set|clear)Selection,
sel(|ection)(Begin|End), selectionAsString, info, currentState,
(mark|clear|check)NewWordPosition, fixIfBroken, sanitize, all undo
related methods, reset, isInside, leaveInset, current mode;
- kill some unused methods: macromode, replaceWord, setScreenPos, touch,
markInsert, markErase;
- Move code around to group things, and add a few comments (a lot remains to be done).
This changes lead to some related changes in other classes: removal,
change of parameter.
No intended change.
2017-07-23 16:13:33 +00:00
|
|
|
bool multipleCellsSelected(CursorData const & cur)
|
2008-03-12 00:59:29 +00:00
|
|
|
{
|
|
|
|
if (!cur.selection() || !cur.inMathed())
|
|
|
|
return false;
|
2017-07-03 17:53:14 +00:00
|
|
|
|
2008-03-12 00:59:29 +00:00
|
|
|
CursorSlice i1 = cur.selBegin();
|
|
|
|
CursorSlice i2 = cur.selEnd();
|
|
|
|
if (!i1.inset().asInsetMath())
|
|
|
|
return false;
|
2017-07-03 17:53:14 +00:00
|
|
|
|
2008-03-12 00:59:29 +00:00
|
|
|
if (i1.idx() == i2.idx())
|
|
|
|
return false;
|
2017-07-03 17:53:14 +00:00
|
|
|
|
2008-03-12 00:59:29 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-10-21 18:17:26 +00:00
|
|
|
void switchBetweenClasses(DocumentClassConstPtr oldone,
|
2012-03-13 16:13:31 +00:00
|
|
|
DocumentClassConstPtr newone, InsetText & in, ErrorList & errorlist)
|
2004-04-18 07:32:34 +00:00
|
|
|
{
|
2007-07-09 10:01:57 +00:00
|
|
|
errorlist.clear();
|
|
|
|
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(!in.paragraphs().empty());
|
2008-02-24 14:59:23 +00:00
|
|
|
if (oldone == newone)
|
2005-05-04 11:21:14 +00:00
|
|
|
return;
|
2017-07-03 17:53:14 +00:00
|
|
|
|
2008-02-28 01:42:02 +00:00
|
|
|
DocumentClass const & oldtc = *oldone;
|
|
|
|
DocumentClass const & newtc = *newone;
|
2008-02-24 14:59:23 +00:00
|
|
|
|
2005-05-04 11:21:14 +00:00
|
|
|
// layouts
|
2011-06-08 00:12:57 +00:00
|
|
|
ParIterator it = par_iterator_begin(in);
|
2004-04-18 07:32:34 +00:00
|
|
|
ParIterator end = par_iterator_end(in);
|
2011-06-08 00:12:57 +00:00
|
|
|
// for remembering which layouts we've had to add
|
|
|
|
set<docstring> newlayouts;
|
|
|
|
for (; it != end; ++it) {
|
2008-03-06 21:31:27 +00:00
|
|
|
docstring const name = it->layout().name();
|
2008-07-11 02:43:02 +00:00
|
|
|
|
|
|
|
// the pasted text will keep their own layout name. If this layout does
|
|
|
|
// not exist in the new document, it will behave like a standard layout.
|
2011-06-08 00:12:57 +00:00
|
|
|
bool const added_one = newtc.addLayoutIfNeeded(name);
|
|
|
|
if (added_one)
|
|
|
|
newlayouts.insert(name);
|
|
|
|
|
|
|
|
if (added_one || newlayouts.find(name) != newlayouts.end()) {
|
|
|
|
// Warn the user.
|
|
|
|
docstring const s = bformat(_("Layout `%1$s' was not found."), name);
|
2016-10-09 19:34:12 +00:00
|
|
|
errorlist.push_back(ErrorItem(_("Layout Not Found"), s,
|
|
|
|
{it->id(), 0}, {it->id(), -1}));
|
2011-06-08 00:12:57 +00:00
|
|
|
}
|
2003-06-03 15:10:14 +00:00
|
|
|
|
2008-07-10 17:41:52 +00:00
|
|
|
if (in.usePlainLayout())
|
2008-08-01 20:57:27 +00:00
|
|
|
it->setLayout(newtc.plainLayout());
|
2004-04-18 07:32:34 +00:00
|
|
|
else
|
2008-07-11 02:43:02 +00:00
|
|
|
it->setLayout(newtc[name]);
|
2003-06-03 15:10:14 +00:00
|
|
|
}
|
2005-05-04 11:21:14 +00:00
|
|
|
|
Possible fix for the mystery crash, which is bug #9049.
Investigation of bug #9236 showed that crash to be due to a Paragraph's
holding a dangling pointer to an old and deleted Layout after the
DocumentClass was reset. Since the backtraces look almost identical, it
seems likely that we have the same problem here.
Since this crash seems almost always to involve tables, I looked at the
code in switchBetweenClasses() and found that the Paragraphs that belong
to "hidden" table cells are not seen by the initial recursion using a
ParIterator: It skips right over them. This was confirmed by test code
suggested by Enrico, with results reported in Trac.
The present patch attempts to deal with this problem in the second
recursion, over Insets. When we see an InsetTabular, we call a new
routine that recurses through the cells, looking for hidden ones. If it
finds a hidden one, it then resets the Layout for the cell's Paragraphs
(there should be only one, but we do not make any assumptions) to the
PlainLayout that belongs to the new DocumentClass. This is good enough,
since such cells never have content.
There is extensive discussion of the patch here:
https://www.mail-archive.com/lyx-devel@lists.lyx.org/msg185095.html
Additional testing by Enrico and me confirmed the existence of the
dangling pointer.
2014-08-07 19:00:35 +00:00
|
|
|
// character styles and hidden table cells
|
2005-05-04 11:21:14 +00:00
|
|
|
InsetIterator const i_end = inset_iterator_end(in);
|
2018-02-10 07:21:48 +00:00
|
|
|
for (InsetIterator iit = inset_iterator_begin(in); iit != i_end; ++iit) {
|
|
|
|
InsetCode const code = iit->lyxCode();
|
Possible fix for the mystery crash, which is bug #9049.
Investigation of bug #9236 showed that crash to be due to a Paragraph's
holding a dangling pointer to an old and deleted Layout after the
DocumentClass was reset. Since the backtraces look almost identical, it
seems likely that we have the same problem here.
Since this crash seems almost always to involve tables, I looked at the
code in switchBetweenClasses() and found that the Paragraphs that belong
to "hidden" table cells are not seen by the initial recursion using a
ParIterator: It skips right over them. This was confirmed by test code
suggested by Enrico, with results reported in Trac.
The present patch attempts to deal with this problem in the second
recursion, over Insets. When we see an InsetTabular, we call a new
routine that recurses through the cells, looking for hidden ones. If it
finds a hidden one, it then resets the Layout for the cell's Paragraphs
(there should be only one, but we do not make any assumptions) to the
PlainLayout that belongs to the new DocumentClass. This is good enough,
since such cells never have content.
There is extensive discussion of the patch here:
https://www.mail-archive.com/lyx-devel@lists.lyx.org/msg185095.html
Additional testing by Enrico and me confirmed the existence of the
dangling pointer.
2014-08-07 19:00:35 +00:00
|
|
|
if (code == FLEX_CODE) {
|
2017-10-16 08:12:21 +00:00
|
|
|
// FIXME: Should we verify all InsetCollapsible?
|
2018-02-10 07:21:48 +00:00
|
|
|
docstring const layoutName = iit->layoutName();
|
Possible fix for the mystery crash, which is bug #9049.
Investigation of bug #9236 showed that crash to be due to a Paragraph's
holding a dangling pointer to an old and deleted Layout after the
DocumentClass was reset. Since the backtraces look almost identical, it
seems likely that we have the same problem here.
Since this crash seems almost always to involve tables, I looked at the
code in switchBetweenClasses() and found that the Paragraphs that belong
to "hidden" table cells are not seen by the initial recursion using a
ParIterator: It skips right over them. This was confirmed by test code
suggested by Enrico, with results reported in Trac.
The present patch attempts to deal with this problem in the second
recursion, over Insets. When we see an InsetTabular, we call a new
routine that recurses through the cells, looking for hidden ones. If it
finds a hidden one, it then resets the Layout for the cell's Paragraphs
(there should be only one, but we do not make any assumptions) to the
PlainLayout that belongs to the new DocumentClass. This is good enough,
since such cells never have content.
There is extensive discussion of the patch here:
https://www.mail-archive.com/lyx-devel@lists.lyx.org/msg185095.html
Additional testing by Enrico and me confirmed the existence of the
dangling pointer.
2014-08-07 19:00:35 +00:00
|
|
|
docstring const & n = newone->insetLayout(layoutName).name();
|
|
|
|
bool const is_undefined = n.empty() ||
|
|
|
|
n == DocumentClass::plainInsetLayout().name();
|
|
|
|
if (!is_undefined)
|
|
|
|
continue;
|
2017-07-03 17:53:14 +00:00
|
|
|
|
Possible fix for the mystery crash, which is bug #9049.
Investigation of bug #9236 showed that crash to be due to a Paragraph's
holding a dangling pointer to an old and deleted Layout after the
DocumentClass was reset. Since the backtraces look almost identical, it
seems likely that we have the same problem here.
Since this crash seems almost always to involve tables, I looked at the
code in switchBetweenClasses() and found that the Paragraphs that belong
to "hidden" table cells are not seen by the initial recursion using a
ParIterator: It skips right over them. This was confirmed by test code
suggested by Enrico, with results reported in Trac.
The present patch attempts to deal with this problem in the second
recursion, over Insets. When we see an InsetTabular, we call a new
routine that recurses through the cells, looking for hidden ones. If it
finds a hidden one, it then resets the Layout for the cell's Paragraphs
(there should be only one, but we do not make any assumptions) to the
PlainLayout that belongs to the new DocumentClass. This is good enough,
since such cells never have content.
There is extensive discussion of the patch here:
https://www.mail-archive.com/lyx-devel@lists.lyx.org/msg185095.html
Additional testing by Enrico and me confirmed the existence of the
dangling pointer.
2014-08-07 19:00:35 +00:00
|
|
|
// The flex inset is undefined in newtc
|
|
|
|
docstring const oldname = from_utf8(oldtc.name());
|
|
|
|
docstring const newname = from_utf8(newtc.name());
|
|
|
|
docstring s;
|
|
|
|
if (oldname == newname)
|
|
|
|
s = bformat(_("Flex inset %1$s is undefined after "
|
|
|
|
"reloading `%2$s' layout."), layoutName, oldname);
|
|
|
|
else
|
|
|
|
s = bformat(_("Flex inset %1$s is undefined because of "
|
|
|
|
"conversion from `%2$s' layout to `%3$s'."),
|
|
|
|
layoutName, oldname, newname);
|
|
|
|
// To warn the user that something had to be done.
|
|
|
|
errorlist.push_back(ErrorItem(
|
2016-10-09 19:34:12 +00:00
|
|
|
_("Undefined flex inset"), s,
|
2018-02-10 07:21:48 +00:00
|
|
|
{iit.paragraph().id(), iit.pos()},
|
|
|
|
{iit.paragraph().id(), iit.pos() + 1}));
|
Possible fix for the mystery crash, which is bug #9049.
Investigation of bug #9236 showed that crash to be due to a Paragraph's
holding a dangling pointer to an old and deleted Layout after the
DocumentClass was reset. Since the backtraces look almost identical, it
seems likely that we have the same problem here.
Since this crash seems almost always to involve tables, I looked at the
code in switchBetweenClasses() and found that the Paragraphs that belong
to "hidden" table cells are not seen by the initial recursion using a
ParIterator: It skips right over them. This was confirmed by test code
suggested by Enrico, with results reported in Trac.
The present patch attempts to deal with this problem in the second
recursion, over Insets. When we see an InsetTabular, we call a new
routine that recurses through the cells, looking for hidden ones. If it
finds a hidden one, it then resets the Layout for the cell's Paragraphs
(there should be only one, but we do not make any assumptions) to the
PlainLayout that belongs to the new DocumentClass. This is good enough,
since such cells never have content.
There is extensive discussion of the patch here:
https://www.mail-archive.com/lyx-devel@lists.lyx.org/msg185095.html
Additional testing by Enrico and me confirmed the existence of the
dangling pointer.
2014-08-07 19:00:35 +00:00
|
|
|
} else if (code == TABULAR_CODE) {
|
|
|
|
// The recursion above does not catch paragraphs in "hidden" cells,
|
|
|
|
// i.e., ones that are part of a multirow or multicolum. So we need
|
|
|
|
// to handle those separately.
|
|
|
|
// This is the cause of bug #9049.
|
2018-02-10 07:21:48 +00:00
|
|
|
InsetTabular * table = iit->asInsetTabular();
|
Possible fix for the mystery crash, which is bug #9049.
Investigation of bug #9236 showed that crash to be due to a Paragraph's
holding a dangling pointer to an old and deleted Layout after the
DocumentClass was reset. Since the backtraces look almost identical, it
seems likely that we have the same problem here.
Since this crash seems almost always to involve tables, I looked at the
code in switchBetweenClasses() and found that the Paragraphs that belong
to "hidden" table cells are not seen by the initial recursion using a
ParIterator: It skips right over them. This was confirmed by test code
suggested by Enrico, with results reported in Trac.
The present patch attempts to deal with this problem in the second
recursion, over Insets. When we see an InsetTabular, we call a new
routine that recurses through the cells, looking for hidden ones. If it
finds a hidden one, it then resets the Layout for the cell's Paragraphs
(there should be only one, but we do not make any assumptions) to the
PlainLayout that belongs to the new DocumentClass. This is good enough,
since such cells never have content.
There is extensive discussion of the patch here:
https://www.mail-archive.com/lyx-devel@lists.lyx.org/msg185095.html
Additional testing by Enrico and me confirmed the existence of the
dangling pointer.
2014-08-07 19:00:35 +00:00
|
|
|
table->setLayoutForHiddenCells(newtc);
|
|
|
|
}
|
2005-05-04 11:21:14 +00:00
|
|
|
}
|
2004-04-18 07:32:34 +00:00
|
|
|
}
|
2003-05-01 15:43:58 +00:00
|
|
|
|
2004-04-03 08:37:12 +00:00
|
|
|
|
2008-07-30 09:09:23 +00:00
|
|
|
vector<docstring> availableSelections(Buffer const * buf)
|
2004-04-18 07:32:34 +00:00
|
|
|
{
|
2006-10-11 19:40:50 +00:00
|
|
|
vector<docstring> selList;
|
2008-07-30 09:09:23 +00:00
|
|
|
if (!buf)
|
|
|
|
return selList;
|
2003-05-01 14:45:04 +00:00
|
|
|
|
2020-02-28 06:55:05 +00:00
|
|
|
for (auto const & cut : theCuts) {
|
|
|
|
ParagraphList const & pars = cut.first;
|
2015-10-04 18:38:47 +00:00
|
|
|
docstring textSel;
|
2020-02-28 06:55:05 +00:00
|
|
|
for (auto const & para : pars) {
|
|
|
|
Paragraph par(para, 0, 46);
|
2008-07-30 09:09:23 +00:00
|
|
|
// adapt paragraph to current buffer.
|
2017-10-16 02:14:08 +00:00
|
|
|
par.setInsetBuffers(const_cast<Buffer &>(*buf));
|
2015-10-04 18:38:47 +00:00
|
|
|
textSel += par.asString(AS_STR_INSETS);
|
|
|
|
if (textSel.size() > 45) {
|
|
|
|
support::truncateWithEllipsis(textSel,45);
|
2004-04-18 07:32:34 +00:00
|
|
|
break;
|
|
|
|
}
|
2004-03-28 19:13:11 +00:00
|
|
|
}
|
2015-10-04 18:38:47 +00:00
|
|
|
selList.push_back(textSel);
|
2004-04-18 07:32:34 +00:00
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
return selList;
|
2000-04-10 14:29:05 +00:00
|
|
|
}
|
|
|
|
|
2000-04-11 22:55:29 +00:00
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
size_type numberOfSelections()
|
2005-11-25 09:27:08 +00:00
|
|
|
{
|
|
|
|
return theCuts.size();
|
|
|
|
}
|
|
|
|
|
2016-11-09 14:13:41 +00:00
|
|
|
namespace {
|
2005-11-25 09:27:08 +00:00
|
|
|
|
2018-07-22 20:18:50 +00:00
|
|
|
void cutSelectionHelper(Cursor & cur, CutStack & cuts, bool realcut, bool putclip)
|
2000-04-10 14:29:05 +00:00
|
|
|
{
|
2005-10-12 18:44:53 +00:00
|
|
|
// This doesn't make sense, if there is no selection
|
|
|
|
if (!cur.selection())
|
|
|
|
return;
|
2003-05-06 09:34:56 +00:00
|
|
|
|
2005-10-12 18:44:53 +00:00
|
|
|
// OK, we have a selection. This is always between cur.selBegin()
|
|
|
|
// and cur.selEnd()
|
2001-12-07 18:40:24 +00:00
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
if (cur.inTexted()) {
|
2007-04-29 23:33:02 +00:00
|
|
|
Text * text = cur.text();
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(text);
|
2004-04-18 07:32:34 +00:00
|
|
|
|
2007-07-10 12:44:19 +00:00
|
|
|
saveSelection(cur);
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
// make sure that the depth behind the selection are restored, too
|
2007-10-18 11:51:17 +00:00
|
|
|
cur.recordUndoSelection();
|
2004-11-24 21:53:46 +00:00
|
|
|
pit_type begpit = cur.selBegin().pit();
|
|
|
|
pit_type endpit = cur.selEnd().pit();
|
2004-04-18 07:32:34 +00:00
|
|
|
|
|
|
|
int endpos = cur.selEnd().pos();
|
|
|
|
|
2008-11-17 11:46:07 +00:00
|
|
|
BufferParams const & bp = cur.buffer()->params();
|
2004-04-18 07:32:34 +00:00
|
|
|
if (realcut) {
|
2008-11-17 11:46:07 +00:00
|
|
|
copySelectionHelper(*cur.buffer(),
|
2009-08-09 16:38:19 +00:00
|
|
|
*text,
|
2004-04-18 07:32:34 +00:00
|
|
|
begpit, endpit,
|
|
|
|
cur.selBegin().pos(), endpos,
|
2016-11-09 14:13:41 +00:00
|
|
|
bp.documentClassPtr(), cuts);
|
2007-01-13 18:29:50 +00:00
|
|
|
// Stuff what we got on the clipboard.
|
|
|
|
// Even if there is no selection.
|
2016-11-09 14:13:41 +00:00
|
|
|
if (putclip)
|
|
|
|
putClipboard(cuts[0].first, cuts[0].second,
|
2020-01-13 14:20:48 +00:00
|
|
|
cur.selectionAsString(true, true), bp);
|
2004-04-18 07:32:34 +00:00
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
|
2008-06-20 11:57:43 +00:00
|
|
|
if (begpit != endpit)
|
2010-07-08 20:04:35 +00:00
|
|
|
cur.screenUpdateFlags(Update::Force | Update::FitCursor);
|
2008-06-20 11:57:43 +00:00
|
|
|
|
2016-06-02 19:06:59 +00:00
|
|
|
tie(endpit, endpos) =
|
|
|
|
eraseSelectionHelper(bp, text->paragraphs(), begpit, endpit,
|
|
|
|
cur.selBegin().pos(), endpos);
|
2004-04-18 07:32:34 +00:00
|
|
|
|
|
|
|
// cutSelection can invalidate the cursor so we need to set
|
|
|
|
// it anew. (Lgb)
|
|
|
|
// we prefer the end for when tracking changes
|
|
|
|
cur.pos() = endpos;
|
2004-11-24 21:53:46 +00:00
|
|
|
cur.pit() = endpit;
|
2004-04-18 07:32:34 +00:00
|
|
|
|
|
|
|
// need a valid cursor. (Lgb)
|
|
|
|
cur.clearSelection();
|
2010-12-04 23:29:51 +00:00
|
|
|
|
|
|
|
// After a cut operation, we must make sure that the Buffer is updated
|
|
|
|
// because some further operation might need updated label information for
|
2010-12-04 23:21:47 +00:00
|
|
|
// example. So we cannot just use "cur.forceBufferUpdate()" here.
|
2010-12-04 23:29:51 +00:00
|
|
|
// This fixes #7071.
|
2010-12-04 23:21:47 +00:00
|
|
|
cur.buffer()->updateBuffer();
|
2005-09-06 17:39:39 +00:00
|
|
|
|
|
|
|
// tell tabular that a recent copy happened
|
|
|
|
dirtyTabularStack(false);
|
2004-04-18 07:32:34 +00:00
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
if (cur.inMathed()) {
|
2005-10-12 18:44:53 +00:00
|
|
|
if (cur.selBegin().idx() != cur.selEnd().idx()) {
|
|
|
|
// The current selection spans more than one cell.
|
|
|
|
// Record all cells
|
2007-10-18 11:51:17 +00:00
|
|
|
cur.recordUndoInset();
|
2005-10-12 18:44:53 +00:00
|
|
|
} else {
|
|
|
|
// Record only the current cell to avoid a jumping
|
|
|
|
// cursor after undo
|
2007-10-18 11:51:17 +00:00
|
|
|
cur.recordUndo();
|
2005-10-12 18:44:53 +00:00
|
|
|
}
|
|
|
|
if (realcut)
|
|
|
|
copySelection(cur);
|
|
|
|
eraseSelection(cur);
|
2004-04-18 07:32:34 +00:00
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
}
|
2002-03-21 17:27:08 +00:00
|
|
|
|
2017-07-23 11:11:54 +00:00
|
|
|
} // namespace
|
2016-11-09 14:13:41 +00:00
|
|
|
|
2018-07-22 20:18:50 +00:00
|
|
|
void cutSelection(Cursor & cur, bool realcut)
|
2016-11-09 14:13:41 +00:00
|
|
|
{
|
2018-07-22 20:18:50 +00:00
|
|
|
cutSelectionHelper(cur, theCuts, realcut, true);
|
2016-11-09 14:13:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-22 20:18:50 +00:00
|
|
|
void cutSelectionToTemp(Cursor & cur, bool realcut)
|
2016-11-09 14:13:41 +00:00
|
|
|
{
|
2018-07-22 20:18:50 +00:00
|
|
|
cutSelectionHelper(cur, tempCut, realcut, false);
|
2016-11-09 14:13:41 +00:00
|
|
|
}
|
|
|
|
|
2002-03-21 17:27:08 +00:00
|
|
|
|
2008-08-13 13:46:19 +00:00
|
|
|
void copySelection(Cursor const & cur)
|
2004-03-25 09:16:36 +00:00
|
|
|
{
|
2019-12-31 12:47:06 +00:00
|
|
|
copySelection(cur, cur.selectionAsString(true, true));
|
2007-01-13 18:29:50 +00:00
|
|
|
}
|
|
|
|
|
2004-03-25 09:16:36 +00:00
|
|
|
|
2008-11-16 19:19:07 +00:00
|
|
|
void copyInset(Cursor const & cur, Inset * inset, docstring const & plaintext)
|
|
|
|
{
|
|
|
|
ParagraphList pars;
|
|
|
|
Paragraph par;
|
2008-11-17 11:46:07 +00:00
|
|
|
BufferParams const & bp = cur.buffer()->params();
|
2008-11-16 19:19:07 +00:00
|
|
|
par.setLayout(bp.documentClass().plainLayout());
|
2013-05-19 10:28:11 +00:00
|
|
|
Font font(inherit_font, bp.language);
|
|
|
|
par.insertInset(0, inset, font, Change(Change::UNCHANGED));
|
2008-11-16 19:19:07 +00:00
|
|
|
pars.push_back(par);
|
|
|
|
theCuts.push(make_pair(pars, bp.documentClassPtr()));
|
|
|
|
|
|
|
|
// stuff the selection onto the X clipboard, from an explicit copy request
|
2020-01-13 14:20:48 +00:00
|
|
|
putClipboard(theCuts[0].first, theCuts[0].second, plaintext, bp);
|
2008-11-16 19:19:07 +00:00
|
|
|
}
|
|
|
|
|
2012-10-27 13:45:27 +00:00
|
|
|
|
2007-02-02 03:10:15 +00:00
|
|
|
namespace {
|
2007-01-05 14:40:49 +00:00
|
|
|
|
Move some Cursor methods to CursorData
Basically, everything that does not depend on a BufferView should move
there. Some methods that do not seem to need a BufferView, like
selHandle or IdxFirst or push actually depend on it and could not be
moved.
This allows to simplify a few uses of recordUndo helpers.
- Move some methods to DocIterator: nextMath, prevMath, getPossibleLabel,
getEncoding;
- Move some methods to CursorData: setCursor, setCursorSelectionTo,
(setCursorTo|normal|reset)Anchor, (set|clear)Selection,
sel(|ection)(Begin|End), selectionAsString, info, currentState,
(mark|clear|check)NewWordPosition, fixIfBroken, sanitize, all undo
related methods, reset, isInside, leaveInset, current mode;
- kill some unused methods: macromode, replaceWord, setScreenPos, touch,
markInsert, markErase;
- Move code around to group things, and add a few comments (a lot remains to be done).
This changes lead to some related changes in other classes: removal,
change of parameter.
No intended change.
2017-07-23 16:13:33 +00:00
|
|
|
void copySelectionToStack(CursorData const & cur, CutStack & cutstack)
|
2007-01-05 14:40:49 +00:00
|
|
|
{
|
2004-03-25 09:16:36 +00:00
|
|
|
// this doesn't make sense, if there is no selection
|
|
|
|
if (!cur.selection())
|
|
|
|
return;
|
|
|
|
|
2007-06-07 21:55:55 +00:00
|
|
|
// copySelection can not yet handle the case of cross idx selection
|
|
|
|
if (cur.selBegin().idx() != cur.selEnd().idx())
|
|
|
|
return;
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
if (cur.inTexted()) {
|
2007-04-29 23:33:02 +00:00
|
|
|
Text * text = cur.text();
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(text);
|
2004-04-18 07:32:34 +00:00
|
|
|
// ok we have a selection. This is always between cur.selBegin()
|
|
|
|
// and sel_end cursor
|
2016-03-01 13:39:43 +00:00
|
|
|
copySelectionHelper(*cur.buffer(), *text,
|
|
|
|
cur.selBegin().pit(), cur.selEnd().pit(),
|
2017-07-03 17:53:14 +00:00
|
|
|
cur.selBegin().pos(), cur.selEnd().pos(),
|
2016-03-01 13:39:43 +00:00
|
|
|
cur.buffer()->params().documentClassPtr(),
|
|
|
|
cutstack);
|
2009-05-17 21:06:31 +00:00
|
|
|
// Reset the dirty_tabular_stack_ flag only when something
|
|
|
|
// is copied to the clipboard (not to the selectionBuffer).
|
2009-05-17 21:02:07 +00:00
|
|
|
if (&cutstack == &theCuts)
|
|
|
|
dirtyTabularStack(false);
|
2004-04-18 07:32:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cur.inMathed()) {
|
2006-08-08 21:55:41 +00:00
|
|
|
//lyxerr << "copySelection in mathed" << endl;
|
2004-04-18 07:32:34 +00:00
|
|
|
ParagraphList pars;
|
2006-11-07 18:27:03 +00:00
|
|
|
Paragraph par;
|
2008-11-17 11:46:07 +00:00
|
|
|
BufferParams const & bp = cur.buffer()->params();
|
2008-08-01 21:58:27 +00:00
|
|
|
// FIXME This should be the plain layout...right?
|
2008-08-01 20:57:27 +00:00
|
|
|
par.setLayout(bp.documentClass().plainLayout());
|
2018-02-06 13:10:12 +00:00
|
|
|
// For pasting into text, we set the language to the paragraph language
|
|
|
|
// (rather than the default_language which is always English; see #2596)
|
|
|
|
par.insert(0, grabSelection(cur), Font(sane_font, par.getParLanguage(bp)),
|
|
|
|
Change(Change::UNCHANGED));
|
2006-11-07 18:27:03 +00:00
|
|
|
pars.push_back(par);
|
2008-02-28 01:42:02 +00:00
|
|
|
cutstack.push(make_pair(pars, bp.documentClassPtr()));
|
2004-04-18 07:32:34 +00:00
|
|
|
}
|
2007-02-02 03:10:15 +00:00
|
|
|
}
|
|
|
|
|
2017-07-23 11:11:54 +00:00
|
|
|
} // namespace
|
2007-02-02 03:10:15 +00:00
|
|
|
|
|
|
|
|
|
|
|
void copySelectionToStack()
|
|
|
|
{
|
|
|
|
if (!selectionBuffer.empty())
|
|
|
|
theCuts.push(selectionBuffer[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-10-01 10:34:15 +00:00
|
|
|
void copySelectionToTemp(Cursor const & cur)
|
2018-04-26 01:48:25 +00:00
|
|
|
{
|
|
|
|
copySelectionToStack(cur, tempCut);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-08-13 13:46:19 +00:00
|
|
|
void copySelection(Cursor const & cur, docstring const & plaintext)
|
2007-02-02 03:10:15 +00:00
|
|
|
{
|
2020-06-29 13:13:45 +00:00
|
|
|
// In tablemode, because copy and paste actually use a special table stack,
|
|
|
|
// we need to go through the cells and collect the paragraphs.
|
|
|
|
// In math matrices, we generate a plain text version.
|
2007-02-22 17:55:22 +00:00
|
|
|
if (cur.selBegin().idx() != cur.selEnd().idx()) {
|
|
|
|
ParagraphList pars;
|
2008-11-17 11:46:07 +00:00
|
|
|
BufferParams const & bp = cur.buffer()->params();
|
2020-06-29 13:13:45 +00:00
|
|
|
if (cur.inMathed()) {
|
|
|
|
Paragraph par;
|
|
|
|
par.setLayout(bp.documentClass().plainLayout());
|
|
|
|
// Replace (column-separating) tabs by space (#4449)
|
|
|
|
docstring const clean_text = subst(plaintext, '\t', ' ');
|
|
|
|
// For pasting into text, we set the language to the paragraph language
|
|
|
|
// (rather than the default_language which is always English; see #11898)
|
|
|
|
par.insert(0, clean_text, Font(sane_font, par.getParLanguage(bp)),
|
|
|
|
Change(Change::UNCHANGED));
|
|
|
|
pars.push_back(par);
|
|
|
|
} else {
|
|
|
|
// Get paragraphs from all cells
|
|
|
|
InsetTabular * table = cur.inset().asInsetTabular();
|
|
|
|
LASSERT(table, return);
|
|
|
|
ParagraphList tplist = table->asParList(cur.selBegin().idx(), cur.selEnd().idx());
|
|
|
|
for (auto & cpar : tplist) {
|
|
|
|
cpar.setLayout(bp.documentClass().plainLayout());
|
|
|
|
pars.push_back(cpar);
|
|
|
|
// since the pars are merged later, we separate them by blank
|
|
|
|
Paragraph epar;
|
|
|
|
epar.insert(0, from_ascii(" "), Font(sane_font, epar.getParLanguage(bp)),
|
|
|
|
Change(Change::UNCHANGED));
|
|
|
|
pars.push_back(epar);
|
|
|
|
}
|
|
|
|
// remove last empty par
|
|
|
|
pars.pop_back();
|
|
|
|
// merge all paragraphs to one
|
|
|
|
while (pars.size() > 1)
|
|
|
|
mergeParagraph(bp, pars, 0);
|
|
|
|
}
|
2008-02-28 01:42:02 +00:00
|
|
|
theCuts.push(make_pair(pars, bp.documentClassPtr()));
|
2007-11-29 21:10:35 +00:00
|
|
|
} else {
|
2007-02-22 17:55:22 +00:00
|
|
|
copySelectionToStack(cur, theCuts);
|
2007-11-29 21:10:35 +00:00
|
|
|
}
|
2007-02-02 03:10:15 +00:00
|
|
|
|
|
|
|
// stuff the selection onto the X clipboard, from an explicit copy request
|
2020-01-13 14:20:48 +00:00
|
|
|
putClipboard(theCuts[0].first, theCuts[0].second, plaintext,
|
|
|
|
cur.buffer()->params());
|
2007-02-02 03:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-08-13 13:46:19 +00:00
|
|
|
void saveSelection(Cursor const & cur)
|
2007-02-02 03:10:15 +00:00
|
|
|
{
|
2007-07-06 17:03:45 +00:00
|
|
|
// This function is called, not when a selection is formed, but when
|
|
|
|
// a selection is cleared. Therefore, multiple keyboard selection
|
|
|
|
// will not repeatively trigger this function (bug 3877).
|
2017-07-03 17:53:14 +00:00
|
|
|
if (cur.selection()
|
2007-07-10 12:44:19 +00:00
|
|
|
&& cur.selBegin() == cur.bv().cursor().selBegin()
|
|
|
|
&& cur.selEnd() == cur.bv().cursor().selEnd()) {
|
2019-12-31 12:47:06 +00:00
|
|
|
LYXERR(Debug::SELECTION, "saveSelection: '" << cur.selectionAsString(true, true) << "'");
|
2007-02-02 03:10:15 +00:00
|
|
|
copySelectionToStack(cur, selectionBuffer);
|
2007-07-06 17:03:45 +00:00
|
|
|
}
|
2007-02-02 03:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool selection()
|
|
|
|
{
|
|
|
|
return !selectionBuffer.empty();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void clearSelection()
|
|
|
|
{
|
|
|
|
selectionBuffer.clear();
|
2004-04-18 07:32:34 +00:00
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
|
|
|
2007-09-15 17:24:08 +00:00
|
|
|
void clearCutStack()
|
|
|
|
{
|
|
|
|
theCuts.clear();
|
2016-11-21 15:54:45 +00:00
|
|
|
tempCut.clear();
|
2007-09-15 17:24:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-01-19 20:37:01 +00:00
|
|
|
docstring selection(size_t sel_index, DocumentClassConstPtr docclass)
|
2004-04-18 07:32:34 +00:00
|
|
|
{
|
2015-01-19 20:37:01 +00:00
|
|
|
if (sel_index >= theCuts.size())
|
|
|
|
return docstring();
|
|
|
|
|
2016-06-02 22:49:36 +00:00
|
|
|
unique_ptr<Buffer> buffer(copyToTempBuffer(theCuts[sel_index].first,
|
|
|
|
docclass));
|
2015-01-19 20:37:01 +00:00
|
|
|
if (!buffer)
|
|
|
|
return docstring();
|
|
|
|
|
|
|
|
return buffer->paragraphs().back().asString(AS_STR_INSETS | AS_STR_NEWLINES);
|
2000-04-10 14:29:05 +00:00
|
|
|
}
|
|
|
|
|
2000-04-11 22:55:29 +00:00
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
void pasteParagraphList(Cursor & cur, ParagraphList const & parlist,
|
2020-01-13 14:20:48 +00:00
|
|
|
DocumentClassConstPtr docclass, ErrorList & errorList,
|
|
|
|
cap::BranchAction branchAction)
|
2000-04-10 14:29:05 +00:00
|
|
|
{
|
2005-07-16 15:22:07 +00:00
|
|
|
if (cur.inTexted()) {
|
2007-04-29 23:33:02 +00:00
|
|
|
Text * text = cur.text();
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(text);
|
2005-07-16 15:22:07 +00:00
|
|
|
|
2013-04-21 01:07:07 +00:00
|
|
|
PasteReturnValue prv =
|
2016-11-09 14:13:41 +00:00
|
|
|
pasteSelectionHelper(cur, parlist, docclass, branchAction, errorList);
|
2013-05-03 01:20:43 +00:00
|
|
|
cur.forceBufferUpdate();
|
2005-07-16 15:22:07 +00:00
|
|
|
cur.clearSelection();
|
2016-02-29 12:29:06 +00:00
|
|
|
text->setCursor(cur, prv.pit, prv.pos);
|
2005-07-16 15:22:07 +00:00
|
|
|
}
|
|
|
|
|
2006-09-16 18:11:38 +00:00
|
|
|
// mathed is handled in InsetMathNest/InsetMathGrid
|
2013-04-25 21:27:10 +00:00
|
|
|
LATTEST(!cur.inMathed());
|
2004-03-25 09:16:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-13 08:29:54 +00:00
|
|
|
bool pasteFromStack(Cursor & cur, ErrorList & errorList, size_t sel_index)
|
2007-02-02 03:10:15 +00:00
|
|
|
{
|
|
|
|
// this does not make sense, if there is nothing to paste
|
|
|
|
if (!checkPastePossible(sel_index))
|
2013-07-13 08:29:54 +00:00
|
|
|
return false;
|
2007-02-02 03:10:15 +00:00
|
|
|
|
2007-10-18 11:51:17 +00:00
|
|
|
cur.recordUndo();
|
2007-02-02 03:10:15 +00:00
|
|
|
pasteParagraphList(cur, theCuts[sel_index].first,
|
2016-11-09 14:13:41 +00:00
|
|
|
theCuts[sel_index].second, errorList, BRANCH_ASK);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool pasteFromTemp(Cursor & cur, ErrorList & errorList)
|
|
|
|
{
|
|
|
|
// this does not make sense, if there is nothing to paste
|
|
|
|
if (tempCut.empty() || tempCut[0].first.empty())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
cur.recordUndo();
|
|
|
|
pasteParagraphList(cur, tempCut[0].first,
|
|
|
|
tempCut[0].second, errorList, BRANCH_IGNORE);
|
2013-07-13 08:29:54 +00:00
|
|
|
return true;
|
2007-02-02 03:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-13 08:29:54 +00:00
|
|
|
bool pasteClipboardText(Cursor & cur, ErrorList & errorList, bool asParagraphs,
|
2013-04-14 17:45:36 +00:00
|
|
|
Clipboard::TextType type)
|
2007-01-13 18:29:50 +00:00
|
|
|
{
|
|
|
|
// Use internal clipboard if it is the most recent one
|
2013-04-14 17:45:36 +00:00
|
|
|
// This overrides asParagraphs and type on purpose!
|
2013-07-13 08:29:54 +00:00
|
|
|
if (theClipboard().isInternal())
|
|
|
|
return pasteFromStack(cur, errorList, 0);
|
2007-01-13 18:29:50 +00:00
|
|
|
|
|
|
|
// First try LyX format
|
2013-04-14 17:45:36 +00:00
|
|
|
if ((type == Clipboard::LyXTextType ||
|
|
|
|
type == Clipboard::LyXOrPlainTextType ||
|
|
|
|
type == Clipboard::AnyTextType) &&
|
|
|
|
theClipboard().hasTextContents(Clipboard::LyXTextType)) {
|
2007-01-13 18:29:50 +00:00
|
|
|
string lyx = theClipboard().getAsLyX();
|
|
|
|
if (!lyx.empty()) {
|
2020-03-13 15:15:31 +00:00
|
|
|
Buffer buffer(string(), false);
|
2007-01-13 18:29:50 +00:00
|
|
|
buffer.setUnnamed(true);
|
|
|
|
if (buffer.readString(lyx)) {
|
2007-10-18 11:51:17 +00:00
|
|
|
cur.recordUndo();
|
2007-01-13 18:29:50 +00:00
|
|
|
pasteParagraphList(cur, buffer.paragraphs(),
|
2008-02-28 01:42:02 +00:00
|
|
|
buffer.params().documentClassPtr(), errorList);
|
2013-07-13 08:29:54 +00:00
|
|
|
return true;
|
2007-01-13 18:29:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-14 17:45:36 +00:00
|
|
|
// Then try TeX and HTML
|
|
|
|
Clipboard::TextType types[2] = {Clipboard::HtmlTextType, Clipboard::LaTeXTextType};
|
2013-11-11 20:52:14 +00:00
|
|
|
string names[2] = {"html", "latexclipboard"};
|
2013-04-14 17:45:36 +00:00
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
|
|
if (type != types[i] && type != Clipboard::AnyTextType)
|
|
|
|
continue;
|
|
|
|
bool available = theClipboard().hasTextContents(types[i]);
|
|
|
|
|
|
|
|
// If a specific type was explicitly requested, try to
|
|
|
|
// interpret plain text: The user told us that the clipboard
|
|
|
|
// contents is in the desired format
|
|
|
|
if (!available && type == types[i]) {
|
|
|
|
types[i] = Clipboard::PlainTextType;
|
|
|
|
available = theClipboard().hasTextContents(types[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (available) {
|
|
|
|
docstring text = theClipboard().getAsText(types[i]);
|
|
|
|
available = !text.empty();
|
|
|
|
if (available) {
|
2020-03-13 15:15:31 +00:00
|
|
|
Buffer buffer(string(), false);
|
2013-04-14 17:45:36 +00:00
|
|
|
buffer.setUnnamed(true);
|
2013-07-13 08:29:54 +00:00
|
|
|
available = buffer.importString(names[i], text, errorList);
|
|
|
|
if (available)
|
|
|
|
available = !buffer.paragraphs().empty();
|
|
|
|
if (available && !buffer.paragraphs()[0].empty()) {
|
2018-03-09 11:15:19 +00:00
|
|
|
// TeX2lyx (also used in the HTML chain) assumes English as document language
|
|
|
|
// if no language is explicitly set (as is the case here).
|
|
|
|
// We thus reset the temp buffer's language to the context language
|
|
|
|
buffer.changeLanguage(buffer.language(), cur.getFont().language());
|
2013-04-14 17:45:36 +00:00
|
|
|
cur.recordUndo();
|
|
|
|
pasteParagraphList(cur, buffer.paragraphs(),
|
|
|
|
buffer.params().documentClassPtr(), errorList);
|
2013-07-13 08:29:54 +00:00
|
|
|
return true;
|
2013-04-14 17:45:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-01-13 18:29:50 +00:00
|
|
|
// Then try plain text
|
2013-04-14 17:45:36 +00:00
|
|
|
docstring const text = theClipboard().getAsText(Clipboard::PlainTextType);
|
2007-01-13 18:29:50 +00:00
|
|
|
if (text.empty())
|
2013-07-13 08:29:54 +00:00
|
|
|
return false;
|
2007-10-18 11:51:17 +00:00
|
|
|
cur.recordUndo();
|
2007-01-13 18:29:50 +00:00
|
|
|
if (asParagraphs)
|
2009-08-09 16:49:41 +00:00
|
|
|
cur.text()->insertStringAsParagraphs(cur, text, cur.current_font);
|
2007-01-13 18:29:50 +00:00
|
|
|
else
|
2009-08-09 16:49:41 +00:00
|
|
|
cur.text()->insertStringAsLines(cur, text, cur.current_font);
|
2016-06-29 17:32:30 +00:00
|
|
|
cur.forceBufferUpdate();
|
2013-07-13 08:29:54 +00:00
|
|
|
return true;
|
2007-01-13 18:29:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-10-29 15:42:01 +00:00
|
|
|
void pasteSimpleText(Cursor & cur, bool asParagraphs)
|
|
|
|
{
|
|
|
|
docstring text;
|
|
|
|
// Use internal clipboard if it is the most recent one
|
|
|
|
if (theClipboard().isInternal()) {
|
|
|
|
if (!checkPastePossible(0))
|
|
|
|
return;
|
|
|
|
|
|
|
|
ParagraphList const & pars = theCuts[0].first;
|
|
|
|
ParagraphList::const_iterator it = pars.begin();
|
|
|
|
for (; it != pars.end(); ++it) {
|
|
|
|
if (it != pars.begin())
|
|
|
|
text += "\n";
|
|
|
|
text += (*it).asString();
|
|
|
|
}
|
|
|
|
asParagraphs = false;
|
|
|
|
} else {
|
|
|
|
// Then try plain text
|
2013-04-14 17:45:36 +00:00
|
|
|
text = theClipboard().getAsText(Clipboard::PlainTextType);
|
2011-10-29 15:42:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (text.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
cur.recordUndo();
|
2018-07-22 20:18:50 +00:00
|
|
|
cutSelection(cur, false);
|
2011-10-29 15:42:01 +00:00
|
|
|
if (asParagraphs)
|
|
|
|
cur.text()->insertStringAsParagraphs(cur, text, cur.current_font);
|
|
|
|
else
|
|
|
|
cur.text()->insertStringAsLines(cur, text, cur.current_font);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-24 15:44:11 +00:00
|
|
|
void pasteClipboardGraphics(Cursor & cur, ErrorList & /* errorList */,
|
2008-02-03 10:43:03 +00:00
|
|
|
Clipboard::GraphicsType preferedType)
|
|
|
|
{
|
2013-04-25 21:27:10 +00:00
|
|
|
LASSERT(theClipboard().hasGraphicsContents(preferedType), return);
|
2008-02-03 10:43:03 +00:00
|
|
|
|
|
|
|
// get picture from clipboard
|
|
|
|
FileName filename = theClipboard().getAsGraphics(cur, preferedType);
|
|
|
|
if (filename.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// create inset for graphic
|
2009-11-08 15:53:21 +00:00
|
|
|
InsetGraphics * inset = new InsetGraphics(cur.buffer());
|
2008-02-03 10:43:03 +00:00
|
|
|
InsetGraphicsParams params;
|
2010-11-04 14:28:33 +00:00
|
|
|
params.filename = support::DocFileName(filename.absFileName(), false);
|
2008-02-03 10:43:03 +00:00
|
|
|
inset->setParams(params);
|
|
|
|
cur.recordUndo();
|
|
|
|
cur.insert(inset);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
void pasteSelection(Cursor & cur, ErrorList & errorList)
|
2005-11-29 15:08:35 +00:00
|
|
|
{
|
2007-02-02 03:10:15 +00:00
|
|
|
if (selectionBuffer.empty())
|
2005-11-29 15:08:35 +00:00
|
|
|
return;
|
2007-10-18 11:51:17 +00:00
|
|
|
cur.recordUndo();
|
2007-02-02 03:10:15 +00:00
|
|
|
pasteParagraphList(cur, selectionBuffer[0].first,
|
2007-05-28 22:27:45 +00:00
|
|
|
selectionBuffer[0].second, errorList);
|
2005-11-29 15:08:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-04-18 10:44:31 +00:00
|
|
|
void replaceSelectionWithString(Cursor & cur, docstring const & str)
|
2004-03-25 09:16:36 +00:00
|
|
|
{
|
2007-10-18 11:51:17 +00:00
|
|
|
cur.recordUndo();
|
2006-08-16 21:12:20 +00:00
|
|
|
DocIterator selbeg = cur.selectionBegin();
|
2004-03-25 09:16:36 +00:00
|
|
|
|
2008-10-28 17:51:08 +00:00
|
|
|
// Get font setting before we cut, we need a copy here, not a bare reference.
|
|
|
|
Font const font =
|
2008-11-17 11:46:07 +00:00
|
|
|
selbeg.paragraph().getFontSettings(cur.buffer()->params(), selbeg.pos());
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
|
|
// Insert the new string
|
2006-08-16 21:12:20 +00:00
|
|
|
pos_type pos = cur.selEnd().pos();
|
|
|
|
Paragraph & par = cur.selEnd().paragraph();
|
2020-03-13 01:50:54 +00:00
|
|
|
for (auto const & c : str) {
|
2020-02-28 06:55:05 +00:00
|
|
|
par.insertChar(pos, c, font, cur.buffer()->params().track_changes);
|
2020-03-13 01:50:54 +00:00
|
|
|
++pos;
|
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
|
|
// Cut the selection
|
2018-07-22 20:18:50 +00:00
|
|
|
cutSelection(cur, false);
|
2004-03-25 09:16:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
void replaceSelection(Cursor & cur)
|
2004-03-25 09:16:36 +00:00
|
|
|
{
|
|
|
|
if (cur.selection())
|
2018-07-22 20:18:50 +00:00
|
|
|
cutSelection(cur, false);
|
2004-03-25 09:16:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
void eraseSelection(Cursor & cur)
|
2004-04-18 07:32:34 +00:00
|
|
|
{
|
2007-01-04 16:50:03 +00:00
|
|
|
//lyxerr << "cap::eraseSelection begin: " << cur << endl;
|
2004-04-18 07:32:34 +00:00
|
|
|
CursorSlice const & i1 = cur.selBegin();
|
|
|
|
CursorSlice const & i2 = cur.selEnd();
|
2014-10-16 14:16:15 +00:00
|
|
|
if (!i1.asInsetMath()) {
|
|
|
|
LYXERR0("Can't erase this selection");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
saveSelection(cur);
|
|
|
|
cur.top() = i1;
|
|
|
|
InsetMath * p = i1.asInsetMath();
|
|
|
|
if (i1.idx() == i2.idx()) {
|
|
|
|
i1.cell().erase(i1.pos(), i2.pos());
|
|
|
|
// We may have deleted i1.cell(cur.pos()).
|
|
|
|
// Make sure that pos is valid.
|
|
|
|
if (cur.pos() > cur.lastpos())
|
|
|
|
cur.pos() = cur.lastpos();
|
|
|
|
} else if (p->nrows() > 0 && p->ncols() > 0) {
|
|
|
|
// This is a grid, delete a nice square region
|
|
|
|
Inset::row_type r1, r2;
|
|
|
|
Inset::col_type c1, c2;
|
|
|
|
region(i1, i2, r1, r2, c1, c2);
|
|
|
|
for (Inset::row_type row = r1; row <= r2; ++row)
|
|
|
|
for (Inset::col_type col = c1; col <= c2; ++col)
|
|
|
|
p->cell(p->index(row, col)).clear();
|
|
|
|
// We've deleted the whole cell. Only pos 0 is valid.
|
|
|
|
cur.pos() = 0;
|
2004-04-18 07:32:34 +00:00
|
|
|
} else {
|
2014-10-16 14:16:15 +00:00
|
|
|
Inset::idx_type idx1 = i1.idx();
|
|
|
|
Inset::idx_type idx2 = i2.idx();
|
|
|
|
if (idx1 > idx2)
|
|
|
|
swap(idx1, idx2);
|
|
|
|
for (Inset::idx_type idx = idx1 ; idx <= idx2; ++idx)
|
|
|
|
p->cell(idx).clear();
|
|
|
|
// We've deleted the whole cell. Only pos 0 is valid.
|
|
|
|
cur.pos() = 0;
|
2004-04-18 07:32:34 +00:00
|
|
|
}
|
2014-10-16 14:16:15 +00:00
|
|
|
|
|
|
|
// need a valid cursor. (Lgb)
|
|
|
|
cur.clearSelection();
|
2007-01-04 16:50:03 +00:00
|
|
|
//lyxerr << "cap::eraseSelection end: " << cur << endl;
|
2004-04-18 07:32:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
void selDel(Cursor & cur)
|
2004-04-18 07:32:34 +00:00
|
|
|
{
|
2007-01-04 16:50:03 +00:00
|
|
|
//lyxerr << "cap::selDel" << endl;
|
2005-10-12 18:44:53 +00:00
|
|
|
if (cur.selection())
|
2004-04-18 07:32:34 +00:00
|
|
|
eraseSelection(cur);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
void selClearOrDel(Cursor & cur)
|
2004-04-18 07:32:34 +00:00
|
|
|
{
|
2007-01-04 16:50:03 +00:00
|
|
|
//lyxerr << "cap::selClearOrDel" << endl;
|
2004-04-18 07:32:34 +00:00
|
|
|
if (lyxrc.auto_region_delete)
|
|
|
|
selDel(cur);
|
|
|
|
else
|
2016-02-28 16:36:29 +00:00
|
|
|
cur.selection(false);
|
2004-04-18 07:32:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
Move some Cursor methods to CursorData
Basically, everything that does not depend on a BufferView should move
there. Some methods that do not seem to need a BufferView, like
selHandle or IdxFirst or push actually depend on it and could not be
moved.
This allows to simplify a few uses of recordUndo helpers.
- Move some methods to DocIterator: nextMath, prevMath, getPossibleLabel,
getEncoding;
- Move some methods to CursorData: setCursor, setCursorSelectionTo,
(setCursorTo|normal|reset)Anchor, (set|clear)Selection,
sel(|ection)(Begin|End), selectionAsString, info, currentState,
(mark|clear|check)NewWordPosition, fixIfBroken, sanitize, all undo
related methods, reset, isInside, leaveInset, current mode;
- kill some unused methods: macromode, replaceWord, setScreenPos, touch,
markInsert, markErase;
- Move code around to group things, and add a few comments (a lot remains to be done).
This changes lead to some related changes in other classes: removal,
change of parameter.
No intended change.
2017-07-23 16:13:33 +00:00
|
|
|
docstring grabSelection(CursorData const & cur)
|
2004-04-18 07:32:34 +00:00
|
|
|
{
|
|
|
|
if (!cur.selection())
|
2006-10-22 10:15:23 +00:00
|
|
|
return docstring();
|
2004-04-18 07:32:34 +00:00
|
|
|
|
2005-10-12 18:44:53 +00:00
|
|
|
#if 0
|
2008-03-12 00:18:39 +00:00
|
|
|
// grab selection by glueing multiple cells together. This is not what
|
|
|
|
// we want because selections spanning multiple cells will get "&" and "\\"
|
|
|
|
// seperators.
|
2007-12-12 19:28:07 +00:00
|
|
|
ostringstream os;
|
2005-10-12 18:44:53 +00:00
|
|
|
for (DocIterator dit = cur.selectionBegin();
|
|
|
|
dit != cur.selectionEnd(); dit.forwardPos())
|
|
|
|
os << asString(dit.cell());
|
|
|
|
return os.str();
|
|
|
|
#endif
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
CursorSlice i1 = cur.selBegin();
|
|
|
|
CursorSlice i2 = cur.selEnd();
|
|
|
|
|
|
|
|
if (i1.idx() == i2.idx()) {
|
2006-09-16 18:11:38 +00:00
|
|
|
if (i1.inset().asInsetMath()) {
|
2007-04-26 16:06:39 +00:00
|
|
|
MathData::const_iterator it = i1.cell().begin();
|
2009-11-08 11:45:46 +00:00
|
|
|
Buffer * buf = cur.buffer();
|
|
|
|
return asString(MathData(buf, it + i1.pos(), it + i2.pos()));
|
2004-04-18 07:32:34 +00:00
|
|
|
} else {
|
2006-10-22 10:15:23 +00:00
|
|
|
return from_ascii("unknown selection 1");
|
2004-04-18 07:32:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-04-29 13:39:47 +00:00
|
|
|
Inset::row_type r1, r2;
|
|
|
|
Inset::col_type c1, c2;
|
2004-04-18 07:32:34 +00:00
|
|
|
region(i1, i2, r1, r2, c1, c2);
|
|
|
|
|
2006-10-22 10:15:23 +00:00
|
|
|
docstring data;
|
2006-09-16 18:11:38 +00:00
|
|
|
if (i1.inset().asInsetMath()) {
|
2007-04-29 13:39:47 +00:00
|
|
|
for (Inset::row_type row = r1; row <= r2; ++row) {
|
2004-04-18 07:32:34 +00:00
|
|
|
if (row > r1)
|
|
|
|
data += "\\\\";
|
2007-04-29 13:39:47 +00:00
|
|
|
for (Inset::col_type col = c1; col <= c2; ++col) {
|
2004-04-18 07:32:34 +00:00
|
|
|
if (col > c1)
|
|
|
|
data += '&';
|
2006-09-16 18:11:38 +00:00
|
|
|
data += asString(i1.asInsetMath()->
|
|
|
|
cell(i1.asInsetMath()->index(row, col)));
|
2004-04-18 07:32:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2006-10-22 10:15:23 +00:00
|
|
|
data = from_ascii("unknown selection 2");
|
2004-04-18 07:32:34 +00:00
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-09-06 17:39:39 +00:00
|
|
|
void dirtyTabularStack(bool b)
|
|
|
|
{
|
|
|
|
dirty_tabular_stack_ = b;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool tabularStackDirty()
|
|
|
|
{
|
|
|
|
return dirty_tabular_stack_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-25 09:16:36 +00:00
|
|
|
} // namespace cap
|
|
|
|
} // namespace lyx
|