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"
|
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"
|
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"
|
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"
|
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
|
|
|
|
2004-03-25 09:16:36 +00:00
|
|
|
#include <boost/tuple/tuple.hpp>
|
2007-10-02 18:27:20 +00:00
|
|
|
#include <boost/next_prior.hpp>
|
2004-03-25 09:16:36 +00:00
|
|
|
|
2007-01-16 14:31:07 +00:00
|
|
|
#include <string>
|
|
|
|
|
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
|
|
|
|
2008-02-28 01:42:02 +00:00
|
|
|
typedef limited_stack<pair<ParagraphList, DocumentClass const *> > 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);
|
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
|
|
|
|
2005-07-16 15:22:07 +00:00
|
|
|
pair<PitPosPair, pit_type>
|
2007-04-26 14:56:30 +00:00
|
|
|
pasteSelectionHelper(Cursor & cur, ParagraphList const & parlist,
|
2008-05-22 14:06:23 +00:00
|
|
|
DocumentClass const * const oldDocClass, 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();
|
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();
|
|
|
|
target_inset = it? it->cell(cur.idx())->asInsetText() : 0;
|
|
|
|
}
|
2008-09-12 13:36:40 +00:00
|
|
|
LASSERT(target_inset, return make_pair(PitPosPair(pit, pos), pit));
|
|
|
|
ParagraphList & pars = target_inset->paragraphs();
|
2006-09-19 09:39:17 +00:00
|
|
|
|
2005-11-29 15:08:35 +00:00
|
|
|
if (parlist.empty())
|
2005-07-16 15:22:07 +00:00
|
|
|
return make_pair(PitPosPair(pit, pos), pit);
|
|
|
|
|
|
|
|
BOOST_ASSERT (pos <= pars[pit].size());
|
|
|
|
|
|
|
|
// Make a copy of the CaP paragraphs.
|
2005-11-29 15:08:35 +00:00
|
|
|
ParagraphList insertion = parlist;
|
2008-05-22 14:06:23 +00:00
|
|
|
DocumentClass const * const newDocClass =
|
|
|
|
buffer.params().documentClassPtr();
|
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.
|
|
|
|
|
|
|
|
// Convert newline to paragraph break in ERT inset.
|
|
|
|
// This should not be here!
|
2008-09-12 13:43:48 +00:00
|
|
|
InsetCode const code = target_inset->lyxCode();
|
|
|
|
if (code == ERT_CODE || code == LISTINGS_CODE) {
|
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
|
|
|
|
2008-08-01 21:58:27 +00:00
|
|
|
// set the paragraphs to plain layout if necessary
|
2008-07-10 17:41:52 +00:00
|
|
|
if (cur.inset().usePlainLayout()) {
|
|
|
|
bool forcePlainLayout = cur.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();
|
2006-09-19 09:39:17 +00:00
|
|
|
ParagraphList::iterator const end = insertion.end();
|
2008-05-22 14:06:23 +00:00
|
|
|
ParagraphList::iterator par = insertion.begin();
|
|
|
|
for (; par != end; ++par) {
|
|
|
|
Layout const & parLayout = par->layout();
|
2008-07-10 17:41:52 +00:00
|
|
|
if (forcePlainLayout || parLayout == defaultLayout)
|
2008-08-01 21:58:27 +00:00
|
|
|
par->setLayout(plainLayout);
|
2008-05-22 14:06:23 +00:00
|
|
|
}
|
2008-08-01 21:58:27 +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();
|
2008-05-22 14:06:23 +00:00
|
|
|
ParagraphList::iterator const end = insertion.end();
|
|
|
|
ParagraphList::iterator par = insertion.begin();
|
|
|
|
for (; par != end; ++par) {
|
|
|
|
Layout const & parLayout = par->layout();
|
2008-08-01 21:58:27 +00:00
|
|
|
if (parLayout == plainLayout)
|
2008-05-22 14:06:23 +00:00
|
|
|
par->setLayout(defaultLayout);
|
|
|
|
}
|
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);
|
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);
|
|
|
|
|
|
|
|
// Only set this from the 2nd on as the 2nd depends
|
|
|
|
// for maxDepth still on pit.
|
|
|
|
if (tmpbuf != insertion.begin())
|
|
|
|
max_depth = tmpbuf->getMaxDepthAfter();
|
|
|
|
|
|
|
|
// 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
|
|
|
|
2006-11-07 18:27:03 +00:00
|
|
|
tmpbuf->setChange(Change(buffer.params().trackChanges ?
|
2007-05-28 22:27:45 +00:00
|
|
|
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) {
|
2009-04-04 13:48:30 +00:00
|
|
|
// Even though this will also be done later, it has to be done here
|
|
|
|
// since, e.g., InsetLabel::updateCommand() is going to try to access
|
|
|
|
// the buffer() member.
|
|
|
|
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");
|
|
|
|
lab->updateCommand(oldname, false);
|
2010-07-09 14:37:00 +00:00
|
|
|
// We need to update the buffer reference cache.
|
|
|
|
cur.forceBufferUpdate();
|
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) {
|
2010-10-26 16:45:21 +00:00
|
|
|
InsetMathHull * mi = itt->asInsetMath()->asHullInset();
|
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));
|
|
|
|
if (mi->asRefInset()->getTarget() == oldname)
|
|
|
|
mi->asRefInset()->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");
|
2008-03-04 06:41:47 +00:00
|
|
|
lab.updateCommand(oldname, false);
|
2010-07-09 14:37:00 +00:00
|
|
|
// We need to update the buffer reference cache.
|
|
|
|
cur.forceBufferUpdate();
|
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) {
|
|
|
|
InsetMathHull & mi =
|
|
|
|
static_cast<InsetMathHull &>(*itt);
|
|
|
|
// 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-01-28 17:37:22 +00:00
|
|
|
mi.setBuffer(const_cast<Buffer &>(buffer));
|
|
|
|
if (mi.asRefInset()->getTarget() == oldname)
|
|
|
|
mi.asRefInset()->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.
|
|
|
|
cur.forceBufferUpdate();
|
2009-02-19 04:50:25 +00:00
|
|
|
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.
|
|
|
|
cur.forceBufferUpdate();
|
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;
|
|
|
|
// FIXME: add an option to add the branch to the master's BranchList.
|
|
|
|
docstring text = bformat(
|
|
|
|
_("The pasted branch \"%1$s\" is undefined.\n"
|
|
|
|
"Do you want to add it to the document's branch list?"),
|
|
|
|
name);
|
|
|
|
if (frontend::Alert::prompt(_("Unknown branch"),
|
|
|
|
text, 0, 1, _("&Add"), _("&Don't Add")) != 0)
|
|
|
|
break;
|
|
|
|
lyx::dispatch(FuncRequest(LFUN_BRANCH_ADD, name));
|
2010-07-09 14:37:00 +00:00
|
|
|
// We need to update the list of branches.
|
|
|
|
cur.forceBufferUpdate();
|
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) {
|
2006-03-23 20:11:06 +00:00
|
|
|
pars.insert(boost::next(pars.begin(), pit),
|
|
|
|
insertion.begin(),
|
2006-04-05 23:56:29 +00:00
|
|
|
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 {
|
2006-03-23 20:11:06 +00:00
|
|
|
pars.insert(boost::next(pars.begin(), pit + 1),
|
|
|
|
insertion.begin(),
|
2006-04-05 23:56:29 +00:00
|
|
|
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)
|
|
|
|
pars[p].setBuffer(const_cast<Buffer &>(buffer));
|
|
|
|
|
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 {
|
2007-01-14 18:27:27 +00:00
|
|
|
pars[last_paste + 1].stripLeadingSpaces(buffer.params().trackChanges);
|
2005-07-16 15:22:07 +00:00
|
|
|
++last_paste;
|
2004-04-18 07:32:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-07-16 15:22:07 +00:00
|
|
|
return make_pair(PitPosPair(pit, pos), last_paste + 1);
|
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) {
|
|
|
|
endpos -= pars[startpit].eraseChars(startpos, endpos, params.trackChanges);
|
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);
|
2006-11-24 21:22:57 +00:00
|
|
|
bool const merge = pars[pit].isMergedOnEndOfParDeletion(params.trackChanges);
|
|
|
|
|
2006-11-23 22:20:38 +00:00
|
|
|
// Logically erase only, including the end-of-paragraph character
|
|
|
|
pars[pit].eraseChars(left, right, params.trackChanges);
|
|
|
|
|
|
|
|
// Separate handling of paragraph break:
|
2006-03-11 13:31:41 +00:00
|
|
|
if (merge && pit != endpit &&
|
2008-02-18 10:16:43 +00:00
|
|
|
(pit + 1 != endpit
|
|
|
|
|| 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
|
|
|
|
2008-02-28 01:42:02 +00:00
|
|
|
void putClipboard(ParagraphList const & paragraphs,
|
|
|
|
DocumentClass const * const docclass, docstring const & plaintext)
|
2007-01-13 18:29:50 +00:00
|
|
|
{
|
2007-02-19 20:08:11 +00:00
|
|
|
// For some strange reason gcc 3.2 and 3.3 do not accept
|
|
|
|
// Buffer buffer(string(), false);
|
2008-07-09 21:27:15 +00:00
|
|
|
// This needs to be static to avoid a memory leak. When a Buffer is
|
|
|
|
// constructed, it constructs a BufferParams, which in turn constructs
|
|
|
|
// a DocumentClass, via new, that is never deleted. If we were to go to
|
|
|
|
// some kind of garbage collection there, or a shared_ptr, then this
|
|
|
|
// would not be needed.
|
2008-07-14 09:00:11 +00:00
|
|
|
static Buffer * buffer = theBufferList().newBuffer(
|
2010-04-21 01:19:09 +00:00
|
|
|
FileName::tempName().absFileName() + "_clipboard.internal");
|
2008-07-14 08:48:25 +00:00
|
|
|
buffer->setUnnamed(true);
|
|
|
|
buffer->paragraphs() = paragraphs;
|
2008-07-23 12:49:22 +00:00
|
|
|
buffer->inset().setBuffer(*buffer);
|
2008-07-14 08:48:25 +00:00
|
|
|
buffer->params().setDocumentClass(docclass);
|
2007-12-12 19:28:07 +00:00
|
|
|
ostringstream lyx;
|
2008-07-14 08:48:25 +00:00
|
|
|
if (buffer->write(lyx))
|
2007-01-13 18:29:50 +00:00
|
|
|
theClipboard().put(lyx.str(), plaintext);
|
|
|
|
else
|
|
|
|
theClipboard().put(string(), plaintext);
|
2008-07-09 21:27:15 +00:00
|
|
|
// Save that memory
|
2008-07-14 08:48:25 +00:00
|
|
|
buffer->paragraphs().clear();
|
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,
|
2008-02-28 01:42:02 +00:00
|
|
|
int start, int end, DocumentClass const * const dc, CutStack & cutstack)
|
2001-04-17 13:48:09 +00:00
|
|
|
{
|
2009-08-09 16:38:19 +00:00
|
|
|
ParagraphList const & pars = text.paragraphs();
|
|
|
|
|
2008-04-10 21:49:34 +00:00
|
|
|
LASSERT(0 <= start && start <= pars[startpit].size(), /**/);
|
|
|
|
LASSERT(0 <= end && end <= pars[endpit].size(), /**/);
|
|
|
|
LASSERT(startpit != endpit || start <= end, /**/);
|
2002-03-21 17:27:08 +00:00
|
|
|
|
2003-05-01 12:53:22 +00:00
|
|
|
// Clone the paragraphs within the selection.
|
2007-02-09 10:27:41 +00:00
|
|
|
ParagraphList copy_pars(boost::next(pars.begin(), startpit),
|
|
|
|
boost::next(pars.begin(), 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);
|
|
|
|
|
|
|
|
ParagraphList::iterator it = copy_pars.begin();
|
|
|
|
ParagraphList::iterator it_end = copy_pars.end();
|
2006-11-07 18:27:03 +00:00
|
|
|
|
|
|
|
for (; it != it_end; it++) {
|
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).
|
|
|
|
it->setBuffer(const_cast<Buffer &>(buf));
|
|
|
|
// PassThru paragraphs have the Language
|
|
|
|
// latex_language. This is invalid for others, so we
|
|
|
|
// need to change it to the buffer language.
|
2009-08-09 16:38:19 +00:00
|
|
|
if (text.inset().getLayout().isPassThru())
|
2009-07-16 08:37:32 +00:00
|
|
|
it->changeLanguage(buf.params(),
|
|
|
|
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
|
2009-05-14 22:21:05 +00:00
|
|
|
if (!isFullyDeleted(copy_pars))
|
|
|
|
acceptChanges(copy_pars, buf.params());
|
2010-06-01 16:13:54 +00:00
|
|
|
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.
|
|
|
|
it = copy_pars.begin();
|
|
|
|
for (; it != it_end; it++) {
|
|
|
|
it->setBuffer(*static_cast<Buffer *>(0));
|
|
|
|
it->setInsetOwner(0);
|
|
|
|
}
|
|
|
|
|
2008-02-28 01:42:02 +00:00
|
|
|
DocumentClass * d = const_cast<DocumentClass *>(dc);
|
|
|
|
cutstack.push(make_pair(copy_pars, d));
|
2000-04-10 14:29:05 +00:00
|
|
|
}
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
} // namespace anon
|
2003-05-01 15:43:58 +00:00
|
|
|
|
2003-05-06 09:34:56 +00:00
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
Inset & p = i1.inset();
|
|
|
|
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
|
|
|
|
|
|
|
|
2008-03-12 00:18:39 +00:00
|
|
|
bool reduceSelectionToOneCell(Cursor & cur)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
|
|
|
cur.top().pos() = 0;
|
|
|
|
cur.resetAnchor();
|
|
|
|
cur.top().pos() = cur.top().lastpos();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-03-12 00:59:29 +00:00
|
|
|
bool multipleCellsSelected(Cursor const & cur)
|
|
|
|
{
|
|
|
|
if (!cur.selection() || !cur.inMathed())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
CursorSlice i1 = cur.selBegin();
|
|
|
|
CursorSlice i2 = cur.selEnd();
|
|
|
|
if (!i1.inset().asInsetMath())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (i1.idx() == i2.idx())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-28 01:42:02 +00:00
|
|
|
void switchBetweenClasses(DocumentClass const * const oldone,
|
|
|
|
DocumentClass const * const newone, InsetText & in, ErrorList & errorlist)
|
2004-04-18 07:32:34 +00:00
|
|
|
{
|
2007-07-09 10:01:57 +00:00
|
|
|
errorlist.clear();
|
|
|
|
|
2008-04-10 21:49:34 +00:00
|
|
|
LASSERT(!in.paragraphs().empty(), /**/);
|
2008-02-24 14:59:23 +00:00
|
|
|
if (oldone == newone)
|
2005-05-04 11:21:14 +00:00
|
|
|
return;
|
This is one of a series of patches that will merge the layout modules development in personal/branches/rgheck back into the tree.
Design goal: Allow the use of layout "modules", which are to LaTeX packages as layout files are to LaTeX document classes. Thus, one could have a module that defined certain character styles, environments, commands, or what have you, and include it in various documents, each of which uses a different document class, without having to modify the layout files themselves. For example, a theorems.module could be used with article.layout to provide support for theorem-type environments, without having to modify article.layout itself, and the same module could be used with book.layout, etc.
This first patch does some reworking of the infrastructrue. We need to distinguish between the TextClass that a particular document is using and the layout of that document, since modules, in particular, can modify the layout. The solution adopted here is to add a TextClass pointer to BufferParams, which will hold the layout. The layout itself is then constructed from the TextClass the document is using. At present, this is completely trivial, but that will change when modules are added.
The pointer in question is a boost::shared_ptr. This is needed because CutAndPaste saves a copy of the layout with each cut or copied selection. We cannot assume the selection vanishes when the document is closed, so there are two options: (i) keep a list of all the layouts that have ever been used by any document; (ii) used some kind of smart pointer. The latter seems preferable, as the former would waste memory. More importantly, the use of a smart pointer allows modules to be modified on disk and then reloaded while LyX is running, and it will eventually allow the same for layout files.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@19756 a592a061-630c-0410-9148-cb99ea01b6c8
2007-08-23 16:41:13 +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
|
2004-04-18 07:32:34 +00:00
|
|
|
ParIterator end = par_iterator_end(in);
|
|
|
|
for (ParIterator it = par_iterator_begin(in); 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.
|
|
|
|
newtc.addLayoutIfNeeded(name);
|
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
|
|
|
|
|
|
|
// character styles
|
|
|
|
InsetIterator const i_end = inset_iterator_end(in);
|
|
|
|
for (InsetIterator it = inset_iterator_begin(in); it != i_end; ++it) {
|
2009-07-14 20:25:25 +00:00
|
|
|
if (it->lyxCode() != FLEX_CODE)
|
2007-11-03 09:03:08 +00:00
|
|
|
// FIXME: Should we verify all InsetCollapsable?
|
|
|
|
continue;
|
2009-08-24 23:02:50 +00:00
|
|
|
docstring const & n = newone->insetLayout(it->name()).name();
|
|
|
|
bool const is_undefined = n.empty() ||
|
|
|
|
n == DocumentClass::plainInsetLayout().name();
|
|
|
|
if (!is_undefined)
|
2007-11-03 09:03:08 +00:00
|
|
|
continue;
|
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
|
|
|
// The flex inset is undefined in newtc
|
2007-11-03 09:03:08 +00:00
|
|
|
docstring const s = bformat(_(
|
|
|
|
"Flex inset %1$s is "
|
|
|
|
"undefined because of class "
|
|
|
|
"conversion from\n%2$s to %3$s"),
|
2009-07-14 20:25:25 +00:00
|
|
|
it->name(), from_utf8(oldtc.name()),
|
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
|
|
|
from_utf8(newtc.name()));
|
2007-11-03 09:03:08 +00:00
|
|
|
// To warn the user that something had to be done.
|
|
|
|
errorlist.push_back(ErrorItem(
|
|
|
|
_("Undefined flex inset"),
|
|
|
|
s, it.paragraph().id(), it.pos(), it.pos() + 1));
|
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
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
CutStack::const_iterator cit = theCuts.begin();
|
|
|
|
CutStack::const_iterator end = theCuts.end();
|
|
|
|
for (; cit != end; ++cit) {
|
|
|
|
// we do not use cit-> here because gcc 2.9x does not
|
|
|
|
// like it (JMarc)
|
|
|
|
ParagraphList const & pars = (*cit).first;
|
2006-10-11 19:40:50 +00:00
|
|
|
docstring asciiSel;
|
2004-04-18 07:32:34 +00:00
|
|
|
ParagraphList::const_iterator pit = pars.begin();
|
|
|
|
ParagraphList::const_iterator pend = pars.end();
|
|
|
|
for (; pit != pend; ++pit) {
|
2008-07-30 09:09:23 +00:00
|
|
|
Paragraph par(*pit, 0, 26);
|
|
|
|
// adapt paragraph to current buffer.
|
|
|
|
par.setBuffer(const_cast<Buffer &>(*buf));
|
|
|
|
asciiSel += par.asString(AS_STR_INSETS);
|
2004-04-18 07:32:34 +00:00
|
|
|
if (asciiSel.size() > 25) {
|
2006-10-11 19:40:50 +00:00
|
|
|
asciiSel.replace(22, docstring::npos,
|
2007-05-28 22:27:45 +00:00
|
|
|
from_ascii("..."));
|
2004-04-18 07:32:34 +00:00
|
|
|
break;
|
|
|
|
}
|
2004-03-28 19:13:11 +00:00
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
selList.push_back(asciiSel);
|
|
|
|
}
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
void cutSelection(Cursor & cur, bool doclear, bool realcut)
|
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();
|
2008-04-10 21:49:34 +00:00
|
|
|
LASSERT(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,
|
2008-02-28 01:42:02 +00:00
|
|
|
bp.documentClassPtr(), theCuts);
|
2007-01-13 18:29:50 +00:00
|
|
|
// Stuff what we got on the clipboard.
|
|
|
|
// Even if there is no selection.
|
|
|
|
putClipboard(theCuts[0].first, theCuts[0].second,
|
|
|
|
cur.selectionAsString(true));
|
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
|
|
|
|
2004-04-20 08:51:15 +00:00
|
|
|
boost::tie(endpit, endpos) =
|
2004-04-18 07:32:34 +00:00
|
|
|
eraseSelectionHelper(bp,
|
|
|
|
text->paragraphs(),
|
|
|
|
begpit, endpit,
|
2007-08-30 12:28:22 +00:00
|
|
|
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
|
|
|
|
2007-02-14 11:21:39 +00:00
|
|
|
// sometimes necessary
|
2007-05-28 22:27:45 +00:00
|
|
|
if (doclear
|
2007-02-14 11:21:39 +00:00
|
|
|
&& text->paragraphs()[begpit].stripLeadingSpaces(bp.trackChanges))
|
|
|
|
cur.fixIfBroken();
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
// need a valid cursor. (Lgb)
|
|
|
|
cur.clearSelection();
|
2010-07-09 14:37:00 +00:00
|
|
|
cur.forceBufferUpdate();
|
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
|
|
|
|
|
|
|
|
2008-08-13 13:46:19 +00:00
|
|
|
void copySelection(Cursor const & cur)
|
2004-03-25 09:16:36 +00:00
|
|
|
{
|
2007-01-13 18:29:50 +00:00
|
|
|
copySelection(cur, cur.selectionAsString(true));
|
|
|
|
}
|
|
|
|
|
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());
|
|
|
|
par.insertInset(0, inset, Change(Change::UNCHANGED));
|
|
|
|
pars.push_back(par);
|
|
|
|
theCuts.push(make_pair(pars, bp.documentClassPtr()));
|
|
|
|
|
|
|
|
// stuff the selection onto the X clipboard, from an explicit copy request
|
|
|
|
putClipboard(theCuts[0].first, theCuts[0].second, plaintext);
|
|
|
|
}
|
|
|
|
|
2007-02-02 03:10:15 +00:00
|
|
|
namespace {
|
2007-01-05 14:40:49 +00:00
|
|
|
|
2008-08-13 13:46:19 +00:00
|
|
|
void copySelectionToStack(Cursor 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();
|
2008-04-10 21:49:34 +00:00
|
|
|
LASSERT(text, /**/);
|
2004-04-18 07:32:34 +00:00
|
|
|
// ok we have a selection. This is always between cur.selBegin()
|
|
|
|
// and sel_end cursor
|
|
|
|
|
|
|
|
// copy behind a space if there is one
|
|
|
|
ParagraphList & pars = text->paragraphs();
|
|
|
|
pos_type pos = cur.selBegin().pos();
|
2004-11-24 21:53:46 +00:00
|
|
|
pit_type par = cur.selBegin().pit();
|
2007-02-21 21:17:39 +00:00
|
|
|
while (pos < pars[par].size() &&
|
|
|
|
pars[par].isLineSeparator(pos) &&
|
|
|
|
(par != cur.selEnd().pit() || pos < cur.selEnd().pos()))
|
2004-04-18 07:32:34 +00:00
|
|
|
++pos;
|
|
|
|
|
2009-08-09 16:38:19 +00:00
|
|
|
copySelectionHelper(*cur.buffer(), *text, par, cur.selEnd().pit(),
|
This is one of a series of patches that will merge the layout modules development in personal/branches/rgheck back into the tree.
Design goal: Allow the use of layout "modules", which are to LaTeX packages as layout files are to LaTeX document classes. Thus, one could have a module that defined certain character styles, environments, commands, or what have you, and include it in various documents, each of which uses a different document class, without having to modify the layout files themselves. For example, a theorems.module could be used with article.layout to provide support for theorem-type environments, without having to modify article.layout itself, and the same module could be used with book.layout, etc.
This first patch does some reworking of the infrastructrue. We need to distinguish between the TextClass that a particular document is using and the layout of that document, since modules, in particular, can modify the layout. The solution adopted here is to add a TextClass pointer to BufferParams, which will hold the layout. The layout itself is then constructed from the TextClass the document is using. At present, this is completely trivial, but that will change when modules are added.
The pointer in question is a boost::shared_ptr. This is needed because CutAndPaste saves a copy of the layout with each cut or copied selection. We cannot assume the selection vanishes when the document is closed, so there are two options: (i) keep a list of all the layouts that have ever been used by any document; (ii) used some kind of smart pointer. The latter seems preferable, as the former would waste memory. More importantly, the use of a smart pointer allows modules to be modified on disk and then reloaded while LyX is running, and it will eventually allow the same for layout files.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@19756 a592a061-630c-0410-9148-cb99ea01b6c8
2007-08-23 16:41:13 +00:00
|
|
|
pos, cur.selEnd().pos(),
|
2008-11-17 11:46:07 +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());
|
2007-04-29 18:17:15 +00:00
|
|
|
par.insert(0, grabSelection(cur), Font(), 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
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void copySelectionToStack()
|
|
|
|
{
|
|
|
|
if (!selectionBuffer.empty())
|
|
|
|
theCuts.push(selectionBuffer[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-08-13 13:46:19 +00:00
|
|
|
void copySelection(Cursor const & cur, docstring const & plaintext)
|
2007-02-02 03:10:15 +00:00
|
|
|
{
|
2007-02-22 17:55:22 +00:00
|
|
|
// In tablemode, because copy and paste actually use special table stack
|
2007-12-02 23:55:38 +00:00
|
|
|
// we do not attempt to get selected paragraphs under cursor. Instead, a
|
2007-02-22 17:55:22 +00:00
|
|
|
// paragraph with the plain text version is generated so that table cells
|
|
|
|
// can be pasted as pure text somewhere else.
|
|
|
|
if (cur.selBegin().idx() != cur.selEnd().idx()) {
|
|
|
|
ParagraphList pars;
|
|
|
|
Paragraph par;
|
2008-11-17 11:46:07 +00:00
|
|
|
BufferParams const & bp = cur.buffer()->params();
|
2008-08-01 20:57:27 +00:00
|
|
|
par.setLayout(bp.documentClass().plainLayout());
|
2007-04-29 18:17:15 +00:00
|
|
|
par.insert(0, plaintext, Font(), Change(Change::UNCHANGED));
|
2007-02-22 17:55:22 +00:00
|
|
|
pars.push_back(par);
|
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
|
|
|
|
putClipboard(theCuts[0].first, theCuts[0].second, plaintext);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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).
|
2007-07-10 12:44:19 +00:00
|
|
|
if (cur.selection()
|
|
|
|
&& cur.selBegin() == cur.bv().cursor().selBegin()
|
|
|
|
&& cur.selEnd() == cur.bv().cursor().selEnd()) {
|
2008-07-14 23:47:54 +00:00
|
|
|
LYXERR(Debug::SELECTION, "saveSelection: '" << cur.selectionAsString(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();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-27 20:43:16 +00:00
|
|
|
docstring selection(size_t sel_index)
|
2004-04-18 07:32:34 +00:00
|
|
|
{
|
|
|
|
return sel_index < theCuts.size()
|
2008-10-19 20:55:54 +00:00
|
|
|
? theCuts[sel_index].first.back().asString(AS_STR_INSETS | AS_STR_NEWLINES)
|
2006-10-11 19:40:50 +00:00
|
|
|
: docstring();
|
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,
|
2008-02-28 01:42:02 +00:00
|
|
|
DocumentClass const * const docclass, ErrorList & errorList)
|
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();
|
2008-04-10 21:49:34 +00:00
|
|
|
LASSERT(text, /**/);
|
2005-07-16 15:22:07 +00:00
|
|
|
|
|
|
|
pit_type endpit;
|
|
|
|
PitPosPair ppp;
|
|
|
|
|
|
|
|
boost::tie(ppp, endpit) =
|
2008-02-28 01:42:02 +00:00
|
|
|
pasteSelectionHelper(cur, parlist, docclass, errorList);
|
2010-07-09 14:37:00 +00:00
|
|
|
cur.forceBufferUpdate();
|
2005-07-16 15:22:07 +00:00
|
|
|
cur.clearSelection();
|
2007-06-26 14:11:42 +00:00
|
|
|
text->setCursor(cur, ppp.first, ppp.second);
|
2005-07-16 15:22:07 +00:00
|
|
|
}
|
|
|
|
|
2006-09-16 18:11:38 +00:00
|
|
|
// mathed is handled in InsetMathNest/InsetMathGrid
|
2008-04-10 21:49:34 +00:00
|
|
|
LASSERT(!cur.inMathed(), /**/);
|
2004-03-25 09:16:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
void 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))
|
|
|
|
return;
|
|
|
|
|
2007-10-18 11:51:17 +00:00
|
|
|
cur.recordUndo();
|
2007-02-02 03:10:15 +00:00
|
|
|
pasteParagraphList(cur, theCuts[sel_index].first,
|
|
|
|
theCuts[sel_index].second, errorList);
|
|
|
|
cur.setSelection();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-03 10:43:03 +00:00
|
|
|
void pasteClipboardText(Cursor & cur, ErrorList & errorList, bool asParagraphs)
|
2007-01-13 18:29:50 +00:00
|
|
|
{
|
|
|
|
// Use internal clipboard if it is the most recent one
|
|
|
|
if (theClipboard().isInternal()) {
|
2007-02-21 21:40:14 +00:00
|
|
|
pasteFromStack(cur, errorList, 0);
|
2007-01-13 18:29:50 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// First try LyX format
|
|
|
|
if (theClipboard().hasLyXContents()) {
|
|
|
|
string lyx = theClipboard().getAsLyX();
|
|
|
|
if (!lyx.empty()) {
|
2007-02-19 20:08:11 +00:00
|
|
|
// For some strange reason gcc 3.2 and 3.3 do not accept
|
|
|
|
// Buffer buffer(string(), false);
|
|
|
|
Buffer buffer("", 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);
|
2007-01-13 18:29:50 +00:00
|
|
|
cur.setSelection();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Then try plain text
|
|
|
|
docstring const text = theClipboard().getAsText();
|
|
|
|
if (text.empty())
|
|
|
|
return;
|
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);
|
2007-01-13 18:29:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2008-04-10 21:49:34 +00:00
|
|
|
LASSERT(theClipboard().hasGraphicsContents(preferedType), /**/);
|
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-04-21 01:19:09 +00:00
|
|
|
params.filename = support::DocFileName(filename.absFileName());
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
void replaceSelectionWithString(Cursor & cur, docstring const & str, bool backwards)
|
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();
|
2006-12-08 19:46:16 +00:00
|
|
|
docstring::const_iterator cit = str.begin();
|
|
|
|
docstring::const_iterator end = str.end();
|
2004-03-25 09:16:36 +00:00
|
|
|
for (; cit != end; ++cit, ++pos)
|
2008-11-17 11:46:07 +00:00
|
|
|
par.insertChar(pos, *cit, font, cur.buffer()->params().trackChanges);
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
|
|
// Cut the selection
|
|
|
|
cutSelection(cur, true, false);
|
2006-08-16 21:12:20 +00:00
|
|
|
|
|
|
|
// select the replacement
|
|
|
|
if (backwards) {
|
|
|
|
selbeg.pos() += str.length();
|
2006-10-21 11:29:34 +00:00
|
|
|
cur.setSelection(selbeg, -int(str.length()));
|
2006-08-16 21:12:20 +00:00
|
|
|
} else
|
|
|
|
cur.setSelection(selbeg, str.length());
|
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())
|
|
|
|
cutSelection(cur, true, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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();
|
2006-09-16 18:11:38 +00:00
|
|
|
if (i1.inset().asInsetMath()) {
|
2007-07-10 12:44:19 +00:00
|
|
|
saveSelection(cur);
|
2005-04-06 07:38:24 +00:00
|
|
|
cur.top() = i1;
|
2004-04-18 07:32:34 +00:00
|
|
|
if (i1.idx() == i2.idx()) {
|
|
|
|
i1.cell().erase(i1.pos(), i2.pos());
|
2005-10-12 18:44:53 +00:00
|
|
|
// We may have deleted i1.cell(cur.pos()).
|
|
|
|
// Make sure that pos is valid.
|
|
|
|
if (cur.pos() > cur.lastpos())
|
|
|
|
cur.pos() = cur.lastpos();
|
2004-04-18 07:32:34 +00:00
|
|
|
} else {
|
2006-09-16 18:11:38 +00:00
|
|
|
InsetMath * p = i1.asInsetMath();
|
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);
|
2007-04-29 13:39:47 +00:00
|
|
|
for (Inset::row_type row = r1; row <= r2; ++row)
|
|
|
|
for (Inset::col_type col = c1; col <= c2; ++col)
|
2004-04-18 07:32:34 +00:00
|
|
|
p->cell(p->index(row, col)).clear();
|
2005-04-06 07:38:24 +00:00
|
|
|
// We've deleted the whole cell. Only pos 0 is valid.
|
2005-04-26 11:12:20 +00:00
|
|
|
cur.pos() = 0;
|
2004-04-18 07:32:34 +00:00
|
|
|
}
|
2005-10-12 18:44:53 +00:00
|
|
|
// need a valid cursor. (Lgb)
|
|
|
|
cur.clearSelection();
|
2004-04-18 07:32:34 +00:00
|
|
|
} else {
|
|
|
|
lyxerr << "can't erase this selection 1" << endl;
|
|
|
|
}
|
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
|
2008-09-24 21:27:41 +00:00
|
|
|
cur.setSelection(false);
|
2004-04-18 07:32:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
docstring grabSelection(Cursor 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
|