* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
- * \author Jürgen Vigna
- * \author Lars Gullik Bjønnes
+ * \author Jürgen Vigna
+ * \author Lars Gullik Bjønnes
* \author Alfredo Braunstein
* \author Michael Gerz
*
#include "Buffer.h"
#include "buffer_funcs.h"
+#include "BufferList.h"
#include "BufferParams.h"
#include "BufferView.h"
#include "Changes.h"
#include "insets/InsetCommand.h"
#include "insets/InsetGraphics.h"
#include "insets/InsetGraphicsParams.h"
+#include "insets/InsetInclude.h"
#include "insets/InsetTabular.h"
#include "mathed/MathData.h"
pasteSelectionHelper(Cursor & cur, ParagraphList const & parlist,
DocumentClass const * const oldDocClass, ErrorList & errorlist)
{
- Buffer const & buffer = cur.buffer();
+ Buffer const & buffer = *cur.buffer();
pit_type pit = cur.pit();
pos_type pos = cur.pos();
- ParagraphList & pars = cur.text()->paragraphs();
+ InsetText * target_inset = cur.inset().asInsetText();
+ if (!target_inset) {
+ InsetTabular * it = cur.inset().asInsetTabular();
+ target_inset = it? it->cell(cur.idx())->asInsetText() : 0;
+ }
+ LASSERT(target_inset, return make_pair(PitPosPair(pit, pos), pit));
+ ParagraphList & pars = target_inset->paragraphs();
if (parlist.empty())
return make_pair(PitPosPair(pit, pos), pit);
// Convert newline to paragraph break in ERT inset.
// This should not be here!
- Inset * inset = pars[pit].inInset();
- if (inset && (inset->lyxCode() == ERT_CODE ||
- inset->lyxCode() == LISTINGS_CODE)) {
+ InsetCode const code = target_inset->lyxCode();
+ if (code == ERT_CODE || code == LISTINGS_CODE) {
for (size_t i = 0; i != insertion.size(); ++i) {
for (pos_type j = 0; j != insertion[i].size(); ++j) {
if (insertion[i].isNewline(j)) {
// do not track deletion of newline
insertion[i].eraseChar(j, false);
+ insertion[i].setInsetOwner(target_inset);
breakParagraphConservative(
buffer.params(),
insertion, i, j);
+ break;
}
}
}
}
- // set the paragraphs to empty layout if necessary
- if (cur.inset().useEmptyLayout()) {
- bool forceEmptyLayout = cur.inset().forceEmptyLayout();
- Layout const & emptyLayout = newDocClass->emptyLayout();
+ // set the paragraphs to plain layout if necessary
+ if (cur.inset().usePlainLayout()) {
+ bool forcePlainLayout = cur.inset().forcePlainLayout();
+ Layout const & plainLayout = newDocClass->plainLayout();
Layout const & defaultLayout = newDocClass->defaultLayout();
ParagraphList::iterator const end = insertion.end();
ParagraphList::iterator par = insertion.begin();
for (; par != end; ++par) {
Layout const & parLayout = par->layout();
- if (forceEmptyLayout || parLayout == defaultLayout)
- par->setLayout(emptyLayout);
+ if (forcePlainLayout || parLayout == defaultLayout)
+ par->setLayout(plainLayout);
}
- } else { // check if we need to reset from empty layout
+ } else { // check if we need to reset from plain layout
Layout const & defaultLayout = newDocClass->defaultLayout();
- Layout const & emptyLayout = newDocClass->emptyLayout();
+ Layout const & plainLayout = newDocClass->plainLayout();
ParagraphList::iterator const end = insertion.end();
ParagraphList::iterator par = insertion.begin();
for (; par != end; ++par) {
Layout const & parLayout = par->layout();
- if (parLayout == emptyLayout)
+ if (parLayout == plainLayout)
par->setLayout(defaultLayout);
}
}
+ InsetText in(buffer);
// Make sure there is no class difference.
- InsetText in;
+ in.paragraphs().clear();
// 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
max_depth = tmpbuf->getMaxDepthAfter();
// Set the inset owner of this paragraph.
- tmpbuf->setInsetOwner(pars[pit].inInset());
+ tmpbuf->setInsetOwner(target_inset);
for (pos_type i = 0; i < tmpbuf->size(); ++i) {
// do not track deletion of invalid insets
if (Inset * inset = tmpbuf->getInset(i))
- if (!pars[pit].insetAllowed(inset->lyxCode()))
+ if (!target_inset->insetAllowed(inset->lyxCode()))
tmpbuf->eraseChar(i--, false);
}
}
// Prepare the paragraphs and insets for insertion.
- // A couple of insets store buffer references so need updating.
insertion.swap(in.paragraphs());
InsetIterator const i_end = inset_iterator_end(in);
-
for (InsetIterator it = inset_iterator_begin(in); it != i_end; ++it) {
-
- it->setBuffer(const_cast<Buffer &>(buffer));
-
switch (it->lyxCode()) {
case LABEL_CODE: {
break;
}
+ case INCLUDE_CODE: {
+ InsetInclude & inc = static_cast<InsetInclude &>(*it);
+ inc.updateCommand();
+ break;
+ }
+
case BIBITEM_CODE: {
// check for duplicates
InsetCommand & bib = static_cast<InsetCommand &>(*it);
mergeParagraph(buffer.params(), pars, pit);
}
- pit_type last_paste = pit + insertion.size() - 1;
-
// Store the new cursor position.
+ pit_type last_paste = pit + insertion.size() - 1;
+ pit_type startpit = pit;
pit = last_paste;
pos = pars[last_paste].size();
+ // FIXME Should we do it here, or should we let updateLabels() do it?
+ // 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));
+
// Join (conditionally) last pasted paragraph with next one, i.e.,
// the tail of the spliced document paragraph
if (!empty && last_paste + 1 != pit_type(pars.size())) {
{
// For some strange reason gcc 3.2 and 3.3 do not accept
// Buffer buffer(string(), false);
- Buffer buffer("", false);
- buffer.setUnnamed(true);
- buffer.paragraphs() = paragraphs;
- buffer.params().setDocumentClass(docclass);
+ // 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.
+ static Buffer * buffer = theBufferList().newBuffer(
+ FileName::tempName().absFilename() + "_clipboard.internal");
+ buffer->setUnnamed(true);
+ buffer->paragraphs() = paragraphs;
+ buffer->inset().setBuffer(*buffer);
+ buffer->params().setDocumentClass(docclass);
ostringstream lyx;
- if (buffer.write(lyx))
+ if (buffer->write(lyx))
theClipboard().put(lyx.str(), plaintext);
else
theClipboard().put(string(), plaintext);
+ // Save that memory
+ buffer->paragraphs().clear();
}
-void copySelectionHelper(Buffer const & buf, ParagraphList & pars,
+void copySelectionHelper(Buffer const & buf, ParagraphList const & pars,
pit_type startpit, pit_type endpit,
int start, int end, DocumentClass const * const dc, CutStack & cutstack)
{
ParIterator end = par_iterator_end(in);
for (ParIterator it = par_iterator_begin(in); it != end; ++it) {
docstring const name = it->layout().name();
- bool hasLayout = newtc.hasLayout(name);
- if (in.useEmptyLayout())
- it->setLayout(newtc.emptyLayout());
- else if (hasLayout)
- it->setLayout(newtc[name]);
+ // 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);
+
+ if (in.usePlainLayout())
+ it->setLayout(newtc.plainLayout());
else
- it->setLayout(newtc.defaultLayout());
-
- if (!hasLayout && name != oldtc.defaultLayoutName()) {
- docstring const s = bformat(
- _("Layout had to be changed from\n%1$s to %2$s\n"
- "because of class conversion from\n%3$s to %4$s"),
- name, it->layout().name(),
- from_utf8(oldtc.name()), from_utf8(newtc.name()));
- // To warn the user that something had to be done.
- errorlist.push_back(ErrorItem(_("Changed Layout"), s,
- it->id(), 0,
- it->size()));
- }
+ it->setLayout(newtc[name]);
}
// character styles
}
-vector<docstring> availableSelections()
+vector<docstring> availableSelections(Buffer const * buf)
{
vector<docstring> selList;
+ if (!buf)
+ return selList;
CutStack::const_iterator cit = theCuts.begin();
CutStack::const_iterator end = theCuts.end();
ParagraphList::const_iterator pit = pars.begin();
ParagraphList::const_iterator pend = pars.end();
for (; pit != pend; ++pit) {
- asciiSel += pit->asString(AS_STR_INSETS);
+ Paragraph par(*pit, 0, 26);
+ // adapt paragraph to current buffer.
+ par.setBuffer(const_cast<Buffer &>(*buf));
+ asciiSel += par.asString(AS_STR_INSETS);
if (asciiSel.size() > 25) {
asciiSel.replace(22, docstring::npos,
from_ascii("..."));
int endpos = cur.selEnd().pos();
- BufferParams const & bp = cur.buffer().params();
+ BufferParams const & bp = cur.buffer()->params();
if (realcut) {
- copySelectionHelper(cur.buffer(),
+ copySelectionHelper(*cur.buffer(),
text->paragraphs(),
begpit, endpit,
cur.selBegin().pos(), endpos,
// need a valid cursor. (Lgb)
cur.clearSelection();
- updateLabels(cur.buffer());
+ cur.buffer()->updateLabels();
// tell tabular that a recent copy happened
dirtyTabularStack(false);
}
-void copySelection(Cursor & cur)
+void copySelection(Cursor const & cur)
{
copySelection(cur, cur.selectionAsString(true));
}
+void copyInset(Cursor const & cur, Inset * inset, docstring const & plaintext)
+{
+ ParagraphList pars;
+ Paragraph par;
+ BufferParams const & bp = cur.buffer()->params();
+ 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);
+}
+
namespace {
-void copySelectionToStack(Cursor & cur, CutStack & cutstack)
+void copySelectionToStack(Cursor const & cur, CutStack & cutstack)
{
// this doesn't make sense, if there is no selection
if (!cur.selection())
(par != cur.selEnd().pit() || pos < cur.selEnd().pos()))
++pos;
- copySelectionHelper(cur.buffer(), pars, par, cur.selEnd().pit(),
+ copySelectionHelper(*cur.buffer(), pars, par, cur.selEnd().pit(),
pos, cur.selEnd().pos(),
- cur.buffer().params().documentClassPtr(), cutstack);
+ cur.buffer()->params().documentClassPtr(), cutstack);
dirtyTabularStack(false);
}
//lyxerr << "copySelection in mathed" << endl;
ParagraphList pars;
Paragraph par;
- BufferParams const & bp = cur.buffer().params();
- // FIXME This should be the empty layout...right?
- par.setLayout(bp.documentClass().emptyLayout());
+ BufferParams const & bp = cur.buffer()->params();
+ // FIXME This should be the plain layout...right?
+ par.setLayout(bp.documentClass().plainLayout());
par.insert(0, grabSelection(cur), Font(), Change(Change::UNCHANGED));
pars.push_back(par);
cutstack.push(make_pair(pars, bp.documentClassPtr()));
}
-void copySelection(Cursor & cur, docstring const & plaintext)
+void copySelection(Cursor const & cur, docstring const & plaintext)
{
// In tablemode, because copy and paste actually use special table stack
// we do not attempt to get selected paragraphs under cursor. Instead, a
if (cur.selBegin().idx() != cur.selEnd().idx()) {
ParagraphList pars;
Paragraph par;
- BufferParams const & bp = cur.buffer().params();
- par.setLayout(bp.documentClass().emptyLayout());
+ BufferParams const & bp = cur.buffer()->params();
+ par.setLayout(bp.documentClass().plainLayout());
par.insert(0, plaintext, Font(), Change(Change::UNCHANGED));
pars.push_back(par);
theCuts.push(make_pair(pars, bp.documentClassPtr()));
}
-void saveSelection(Cursor & cur)
+void saveSelection(Cursor const & cur)
{
// This function is called, not when a selection is formed, but when
// a selection is cleared. Therefore, multiple keyboard selection
if (cur.selection()
&& cur.selBegin() == cur.bv().cursor().selBegin()
&& cur.selEnd() == cur.bv().cursor().selEnd()) {
- LYXERR(Debug::ACTION, "'" << cur.selectionAsString(true) << "'");
+ LYXERR(Debug::SELECTION, "saveSelection: '" << cur.selectionAsString(true) << "'");
copySelectionToStack(cur, selectionBuffer);
}
}
docstring selection(size_t sel_index)
{
return sel_index < theCuts.size()
- ? theCuts[sel_index].first.back().asString(AS_STR_INSETS)
+ ? theCuts[sel_index].first.back().asString(AS_STR_INSETS | AS_STR_NEWLINES)
: docstring();
}
boost::tie(ppp, endpit) =
pasteSelectionHelper(cur, parlist, docclass, errorList);
- updateLabels(cur.buffer());
+ cur.buffer()->updateLabels();
cur.clearSelection();
text->setCursor(cur, ppp.first, ppp.second);
}
return;
// create inset for graphic
- InsetGraphics * inset = new InsetGraphics(cur.buffer());
+ InsetGraphics * inset = new InsetGraphics(*cur.buffer());
InsetGraphicsParams params;
params.filename = support::DocFileName(filename.absFilename());
inset->setParams(params);
cur.recordUndo();
DocIterator selbeg = cur.selectionBegin();
- // Get font setting before we cut
+ // Get font setting before we cut, we need a copy here, not a bare reference.
Font const font =
- selbeg.paragraph().getFontSettings(cur.buffer().params(), selbeg.pos());
+ selbeg.paragraph().getFontSettings(cur.buffer()->params(), selbeg.pos());
// Insert the new string
pos_type pos = cur.selEnd().pos();
docstring::const_iterator cit = str.begin();
docstring::const_iterator end = str.end();
for (; cit != end; ++cit, ++pos)
- par.insertChar(pos, *cit, font, cur.buffer().params().trackChanges);
+ par.insertChar(pos, *cit, font, cur.buffer()->params().trackChanges);
// Cut the selection
cutSelection(cur, true, false);
if (lyxrc.auto_region_delete)
selDel(cur);
else
- cur.selection() = false;
+ cur.setSelection(false);
}
} // namespace cap
-
} // namespace lyx