* \author Jürgen Vigna
* \author Lars Gullik Bjønnes
* \author Alfredo Braunstein
+ * \author Michael Gerz
*
* Full author contact details are available in file CREDITS.
*/
#include "buffer.h"
#include "buffer_funcs.h"
#include "bufferparams.h"
-#include "BufferView.h"
#include "cursor.h"
#include "debug.h"
#include "errorlist.h"
#include "insets/insetcharstyle.h"
#include "insets/insettabular.h"
-#include "mathed/math_data.h"
-#include "mathed/math_inset.h"
-#include "mathed/math_support.h"
+#include "mathed/MathData.h"
+#include "mathed/InsetMath.h"
+#include "mathed/MathSupport.h"
#include "support/lstrings.h"
-#include "frontends/Gui.h"
-#include "frontends/LyXView.h"
#include "frontends/Clipboard.h"
+#include "frontends/Selection.h"
#include <boost/tuple/tuple.hpp>
-using lyx::pos_type;
-using lyx::pit_type;
-using lyx::textclass_type;
-using lyx::support::bformat;
+namespace lyx {
-using lyx::frontend::Gui;
-using lyx::frontend::Clipboard;
+using support::bformat;
+using frontend::Clipboard;
using std::endl;
using std::for_each;
namespace {
-typedef std::pair<lyx::pit_type, int> PitPosPair;
+typedef std::pair<pit_type, int> PitPosPair;
typedef limited_stack<pair<ParagraphList, textclass_type> > CutStack;
// store whether the tabular stack is newer than the normal copy stack
// FIXME: this is a workaround for bug 1919. Should be removed for 1.5,
// when we (hopefully) have a one-for-all paste mechanism.
-bool dirty_tabular_stack_;
-
-class resetParagraph : public std::unary_function<Paragraph, Buffer const &> {
-public:
- resetParagraph(Buffer const & b) : buffer_(b) {}
- void operator()(Paragraph & p) const {
- p.cleanChanges();
- // ERT paragraphs have the Language latex_language.
- // This is invalid outside of ERT, so we need to change it
- // to the buffer language.
- if (p.ownerCode() == InsetBase::ERT_CODE) {
- p.changeLanguage(buffer_.params(), latex_language,
- buffer_.getLanguage());
- }
- p.setInsetOwner(0);
- }
-private:
- Buffer const & buffer_;
-};
+bool dirty_tabular_stack_ = false;
void region(CursorSlice const & i1, CursorSlice const & i2,
pair<PitPosPair, pit_type>
-pasteSelectionHelper(Buffer const & buffer,
- ParagraphList & pars, pit_type pit, int pos,
- ParagraphList const & parlist, textclass_type textclass,
- ErrorList & errorlist)
+pasteSelectionHelper(LCursor & cur, ParagraphList const & parlist,
+ textclass_type textclass, ErrorList & errorlist)
{
+ Buffer const & buffer = cur.buffer();
+ pit_type pit = cur.pit();
+ pos_type pos = cur.pos();
+ ParagraphList & pars = cur.text()->paragraphs();
+
if (parlist.empty())
return make_pair(PitPosPair(pit, pos), pit);
for (ParagraphList::size_type i = 0; i < insertion.size(); ++i) {
for (pos_type j = 0; j < insertion[i].size(); ++j) {
if (insertion[i].isNewline(j)) {
- insertion[i].erase(j);
+ // do not track deletion of newline
+ insertion[i].eraseChar(j, false);
breakParagraphConservative(
buffer.params(),
insertion, i, j);
}
}
+ // If we are in an inset which returns forceDefaultParagraphs,
+ // set the paragraphs to default
+ if (cur.inset().forceDefaultParagraphs(cur.idx())) {
+ LyXLayout_ptr const layout =
+ buffer.params().getLyXTextClass().defaultLayout();
+ ParagraphList::iterator const end = insertion.end();
+ for (ParagraphList::iterator par = insertion.begin();
+ par != end; ++par)
+ par->layout(layout);
+ }
+
// Make sure there is no class difference.
InsetText in;
// This works without copying any paragraph data because we have
// since we store pointers to insets at some places and we don't
// want to invalidate them.
insertion.swap(in.paragraphs());
- lyx::cap::switchBetweenClasses(textclass, tc, in, errorlist);
+ cap::switchBetweenClasses(textclass, tc, in, errorlist);
insertion.swap(in.paragraphs());
ParagraphList::iterator tmpbuf = insertion.begin();
int depth_delta = pars[pit].params().depth() - tmpbuf->params().depth();
- Paragraph::depth_type max_depth = pars[pit].getMaxDepthAfter();
+ depth_type max_depth = pars[pit].getMaxDepthAfter();
for (; tmpbuf != insertion.end(); ++tmpbuf) {
// If we have a negative jump so that the depth would
for (pos_type i = 0; i < tmpbuf->size(); ++i) {
if (tmpbuf->getChar(i) == Paragraph::META_INSET &&
!pars[pit].insetAllowed(tmpbuf->getInset(i)->lyxCode()))
- tmpbuf->erase(i--);
+ // do not track deletion of invalid insets
+ tmpbuf->eraseChar(i--, false);
}
- // reset change tracking status
- if (buffer.params().tracking_changes)
- tmpbuf->cleanChanges(Paragraph::trackingOn);
- else
- tmpbuf->cleanChanges(Paragraph::trackingOff);
+ tmpbuf->setChange(Change(buffer.params().trackChanges ?
+ Change::INSERTED : Change::UNCHANGED));
}
bool const empty = pars[pit].empty();
ParIterator fend = par_iterator_end(in);
for (; fpit != fend; ++fpit) {
- InsetList::iterator lit = fpit->insetlist.begin();
- InsetList::iterator eit = fpit->insetlist.end();
+ InsetList::const_iterator lit = fpit->insetlist.begin();
+ InsetList::const_iterator eit = fpit->insetlist.end();
for (; lit != eit; ++lit) {
switch (lit->inset->lyxCode()) {
return PitPosPair(endpit, endpos);
// Start and end is inside same paragraph
- if (endpit == pit_type(pars.size()) ||
- startpit == endpit) {
- endpos -= pars[startpit].erase(startpos, endpos);
+ if (endpit == pit_type(pars.size()) || startpit == endpit) {
+ endpos -= pars[startpit].eraseChars(startpos, endpos, params.trackChanges);
return PitPosPair(endpit, endpos);
}
- // A paragraph break has to be physically removed by merging, but
- // only if either (1) change tracking is off, or (2) the para break
- // is "blue"
for (pit_type pit = startpit; pit != endpit + 1;) {
- bool const merge = !params.tracking_changes ||
- pars[pit].lookupChange(pars[pit].size()) ==
- Change::INSERTED;
- pos_type const left = ( pit == startpit ? startpos : 0 );
- pos_type const right = ( pit == endpit ? endpos :
- pars[pit].size() + 1 );
- // Logical erase only:
- pars[pit].erase(left, right);
- // Separate handling of para break:
+ pos_type const left = (pit == startpit ? startpos : 0);
+ pos_type const right = (pit == endpit ? endpos : pars[pit].size() + 1);
+
+ bool const merge = pars[pit].isMergedOnEndOfParDeletion(params.trackChanges);
+
+ // Logically erase only, including the end-of-paragraph character
+ pars[pit].eraseChars(left, right, params.trackChanges);
+
+ // Separate handling of paragraph break:
if (merge && pit != endpit &&
- (pit + 1 != endpit || pars[pit].hasSameLayout(pars[pit + 1]))) {
+ (pit + 1 != endpit || pars[pit].hasSameLayout(pars[pit + 1]))) {
pos_type const thissize = pars[pit].size();
if (doclear)
pars[pit + 1].stripLeadingSpaces();
ParagraphList paragraphs(boost::next(pars.begin(), startpit),
boost::next(pars.begin(), endpit + 1));
- for_each(paragraphs.begin(), paragraphs.end(), resetParagraph(buf));
+ ParagraphList::iterator it = paragraphs.begin();
+ ParagraphList::iterator it_end = paragraphs.end();
+
+ for (; it != it_end; it++) {
+ // ERT paragraphs have the Language latex_language.
+ // This is invalid outside of ERT, so we need to change it
+ // to the buffer language.
+ if (it->ownerCode() == InsetBase::ERT_CODE) {
+ it->changeLanguage(buf.params(), latex_language,
+ buf.getLanguage());
+ }
+ it->setInsetOwner(0);
+ }
// Cut out the end of the last paragraph.
Paragraph & back = paragraphs.back();
- back.erase(end, back.size());
+ // do not track deletion here; it is an internal action not visible to the user
+ back.eraseChars(end, back.size(), false);
// Cut out the begin of the first paragraph
Paragraph & front = paragraphs.front();
- front.erase(0, start);
+ // again, do not track deletion
+ front.eraseChars(0, start, false);
theCuts.push(make_pair(paragraphs, tc));
}
-namespace lyx {
namespace cap {
-string grabAndEraseSelection(LCursor & cur)
+docstring grabAndEraseSelection(LCursor & cur)
{
if (!cur.selection())
- return string();
- string res = grabSelection(cur);
+ return docstring();
+ docstring res = grabSelection(cur);
eraseSelection(cur);
return res;
}
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"),
- lyx::from_utf8(name), lyx::from_utf8(it->layout()->name()),
- lyx::from_utf8(tclass1.name()), lyx::from_utf8(tclass2.name()));
+ from_utf8(name), from_utf8(it->layout()->name()),
+ from_utf8(tclass1.name()), from_utf8(tclass2.name()));
// To warn the user that something had to be done.
errorlist.push_back(ErrorItem(_("Changed Layout"), s,
it->id(), 0,
"Character style %1$s is "
"undefined because of class "
"conversion from\n%2$s to %3$s"),
- lyx::from_utf8(name), lyx::from_utf8(tclass1.name()),
- lyx::from_utf8(tclass2.name()));
+ from_utf8(name), from_utf8(tclass1.name()),
+ from_utf8(tclass2.name()));
// To warn the user that something had to be done.
errorlist.push_back(ErrorItem(
_("Undefined character style"),
}
-std::vector<string> const availableSelections(Buffer const & buffer)
+std::vector<docstring> const availableSelections(Buffer const & buffer)
{
- vector<string> selList;
+ vector<docstring> selList;
CutStack::const_iterator cit = theCuts.begin();
CutStack::const_iterator end = theCuts.end();
// we do not use cit-> here because gcc 2.9x does not
// like it (JMarc)
ParagraphList const & pars = (*cit).first;
- string asciiSel;
+ docstring asciiSel;
ParagraphList::const_iterator pit = pars.begin();
ParagraphList::const_iterator pend = pars.end();
for (; pit != pend; ++pit) {
asciiSel += pit->asString(buffer, false);
if (asciiSel.size() > 25) {
- asciiSel.replace(22, string::npos, "...");
+ asciiSel.replace(22, docstring::npos,
+ from_ascii("..."));
break;
}
}
}
-lyx::size_type numberOfSelections()
+size_type numberOfSelections()
{
return theCuts.size();
}
LyXText * text = cur.text();
BOOST_ASSERT(text);
// Stuff what we got on the clipboard. Even if there is no selection.
-
- // There is a problem with having the stuffing here in that the
- // larger the selection the slower LyX will get. This can be
- // solved by running the line below only when the selection has
- // finished. The solution used currently just works, to make it
- // faster we need to be more clever and probably also have more
- // calls to cur.bv().owner()->gui().selection().put. (Lgb)
-// cur.bv().owner()->gui().selection().put(cur.selectionAsString(true));
-
+ if (realcut)
+ theClipboard().put(cur.selectionAsString(true));
// make sure that the depth behind the selection are restored, too
recordUndoSelection(cur);
// need a valid cursor. (Lgb)
cur.clearSelection();
updateLabels(cur.buffer());
+ theSelection().haveSelection(false);
// tell tabular that a recent copy happened
dirtyTabularStack(false);
void copySelection(LCursor & cur)
{
// stuff the selection onto the X clipboard, from an explicit copy request
- cur.bv().owner()->gui().clipboard().put(cur.selectionAsString(true));
+ theClipboard().put(cur.selectionAsString(true));
+ copySelectionToStack(cur);
+}
+
+
+void copySelectionToStack(LCursor & cur)
+{
// this doesn't make sense, if there is no selection
if (!cur.selection())
return;
if (cur.inMathed()) {
//lyxerr << "copySelection in mathed" << endl;
ParagraphList pars;
- pars.push_back(Paragraph());
+ Paragraph par;
BufferParams const & bp = cur.buffer().params();
- pars.back().layout(bp.getLyXTextClass().defaultLayout());
- for_each(pars.begin(), pars.end(), resetParagraph(cur.buffer()));
- pars.back().insert(0, grabSelection(cur), LyXFont());
+ par.layout(bp.getLyXTextClass().defaultLayout());
+ par.insert(0, grabSelection(cur), LyXFont(), Change(Change::UNCHANGED));
+ pars.push_back(par);
theCuts.push(make_pair(pars, bp.textclass));
}
// tell tabular that a recent copy happened
}
-std::string getSelection(Buffer const & buf, size_t sel_index)
+docstring getSelection(Buffer const & buf, size_t sel_index)
{
return sel_index < theCuts.size()
? theCuts[sel_index].first.back().asString(buf, false)
- : string();
+ : docstring();
}
LyXText * text = cur.text();
BOOST_ASSERT(text);
- recordUndo(cur);
-
pit_type endpit;
PitPosPair ppp;
boost::tie(ppp, endpit) =
- pasteSelectionHelper(cur.buffer(),
- text->paragraphs(),
- cur.pit(), cur.pos(),
- parlist, textclass,
- errorList);
+ pasteSelectionHelper(cur, parlist,
+ textclass, errorList);
updateLabels(cur.buffer());
cur.clearSelection();
- text->setCursor(cur, ppp.first, ppp.second);
+ text->setCursor(cur.top(), ppp.first, ppp.second);
}
- // mathed is handled in MathNestInset/MathGridInset
+ // mathed is handled in InsetMathNest/InsetMathGrid
BOOST_ASSERT(!cur.inMathed());
}
if (!checkPastePossible(sel_index))
return;
+ recordUndo(cur);
pasteParagraphList(cur, theCuts[sel_index].first,
theCuts[sel_index].second, errorList);
cur.setSelection();
}
-// simple replacing. The font of the first selected character is used
-void replaceSelectionWithString(LCursor & cur, string const & str, bool backwards)
+void replaceSelectionWithString(LCursor & cur, docstring const & str, bool backwards)
{
recordUndo(cur);
DocIterator selbeg = cur.selectionBegin();
// Insert the new string
pos_type pos = cur.selEnd().pos();
Paragraph & par = cur.selEnd().paragraph();
- string::const_iterator cit = str.begin();
- string::const_iterator end = str.end();
+ docstring::const_iterator cit = str.begin();
+ docstring::const_iterator end = str.end();
for (; cit != end; ++cit, ++pos)
- par.insertChar(pos, (*cit), font);
+ par.insertChar(pos, *cit, font, cur.buffer().params().trackChanges);
// Cut the selection
cutSelection(cur, true, false);
// select the replacement
if (backwards) {
selbeg.pos() += str.length();
- cur.setSelection(selbeg, -str.length());
+ cur.setSelection(selbeg, -int(str.length()));
} else
cur.setSelection(selbeg, str.length());
}
void eraseSelection(LCursor & cur)
{
- //lyxerr << "LCursor::eraseSelection begin: " << cur << endl;
+ //lyxerr << "cap::eraseSelection begin: " << cur << endl;
CursorSlice const & i1 = cur.selBegin();
CursorSlice const & i2 = cur.selEnd();
- if (i1.inset().asMathInset()) {
+ if (i1.inset().asInsetMath()) {
cur.top() = i1;
if (i1.idx() == i2.idx()) {
i1.cell().erase(i1.pos(), i2.pos());
if (cur.pos() > cur.lastpos())
cur.pos() = cur.lastpos();
} else {
- MathInset * p = i1.asMathInset();
+ InsetMath * p = i1.asInsetMath();
InsetBase::row_type r1, r2;
InsetBase::col_type c1, c2;
region(i1, i2, r1, r2, c1, c2);
}
// need a valid cursor. (Lgb)
cur.clearSelection();
+ theSelection().haveSelection(false);
} else {
lyxerr << "can't erase this selection 1" << endl;
}
- //lyxerr << "LCursor::eraseSelection end: " << cur << endl;
+ //lyxerr << "cap::eraseSelection end: " << cur << endl;
}
void selDel(LCursor & cur)
{
- //lyxerr << "LCursor::selDel" << endl;
+ //lyxerr << "cap::selDel" << endl;
if (cur.selection())
eraseSelection(cur);
}
void selClearOrDel(LCursor & cur)
{
- //lyxerr << "LCursor::selClearOrDel" << endl;
+ //lyxerr << "cap::selClearOrDel" << endl;
if (lyxrc.auto_region_delete)
selDel(cur);
else
}
-string grabSelection(LCursor const & cur)
+docstring grabSelection(LCursor const & cur)
{
if (!cur.selection())
- return string();
+ return docstring();
// FIXME: What is wrong with the following?
#if 0
CursorSlice i2 = cur.selEnd();
if (i1.idx() == i2.idx()) {
- if (i1.inset().asMathInset()) {
+ if (i1.inset().asInsetMath()) {
MathArray::const_iterator it = i1.cell().begin();
return asString(MathArray(it + i1.pos(), it + i2.pos()));
} else {
- return "unknown selection 1";
+ return from_ascii("unknown selection 1");
}
}
InsetBase::col_type c1, c2;
region(i1, i2, r1, r2, c1, c2);
- string data;
- if (i1.inset().asMathInset()) {
+ docstring data;
+ if (i1.inset().asInsetMath()) {
for (InsetBase::row_type row = r1; row <= r2; ++row) {
if (row > r1)
data += "\\\\";
for (InsetBase::col_type col = c1; col <= c2; ++col) {
if (col > c1)
data += '&';
- data += asString(i1.asMathInset()->
- cell(i1.asMathInset()->index(row, col)));
+ data += asString(i1.asInsetMath()->
+ cell(i1.asInsetMath()->index(row, col)));
}
}
} else {
- data = "unknown selection 2";
+ data = from_ascii("unknown selection 2");
}
return data;
}
} // namespace cap
+
} // namespace lyx