]> git.lyx.org Git - lyx.git/blobdiff - src/CutAndPaste.cpp
Include the recently added test files in package
[lyx.git] / src / CutAndPaste.cpp
index 07274a39a040158e90637523b7266c351d130a55..4b95b9be506f1d28f9a0dfcd2b1f7fb74d62dee0 100644 (file)
@@ -35,8 +35,9 @@
 #include "Paragraph.h"
 #include "ParagraphParameters.h"
 #include "ParIterator.h"
-#include "Undo.h"
+#include "TextClass.h"
 
+#include "insets/InsetBibitem.h"
 #include "insets/InsetBranch.h"
 #include "insets/InsetCommand.h"
 #include "insets/InsetFlex.h"
@@ -55,6 +56,7 @@
 #include "support/debug.h"
 #include "support/docstream.h"
 #include "support/gettext.h"
+#include "support/lassert.h"
 #include "support/limited_stack.h"
 #include "support/lstrings.h"
 
@@ -77,7 +79,7 @@ namespace {
 
 typedef pair<pit_type, int> PitPosPair;
 
-typedef limited_stack<pair<ParagraphList, DocumentClass const *> > CutStack;
+typedef limited_stack<pair<ParagraphList, DocumentClassConstPtr> > CutStack;
 
 CutStack theCuts(10);
 // persistent selection, cleared until the next selection
@@ -96,8 +98,8 @@ bool checkPastePossible(int index)
 
 
 pair<PitPosPair, pit_type>
-pasteSelectionHelper(Cursor & cur, ParagraphList const & parlist,
-                    DocumentClass const * const oldDocClass, ErrorList & errorlist)
+pasteSelectionHelper(Cursor const & cur, ParagraphList const & parlist,
+                    DocumentClassConstPtr oldDocClass, ErrorList & errorlist)
 {
        Buffer const & buffer = *cur.buffer();
        pit_type pit = cur.pit();
@@ -117,16 +119,14 @@ pasteSelectionHelper(Cursor & cur, ParagraphList const & parlist,
 
        // Make a copy of the CaP paragraphs.
        ParagraphList insertion = parlist;
-       DocumentClass const * const newDocClass = 
-               buffer.params().documentClassPtr();
+       DocumentClassConstPtr newDocClass = buffer.params().documentClassPtr();
 
        // 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!
-       InsetCode const code = target_inset->lyxCode();
-       if (code == ERT_CODE || code == LISTINGS_CODE) {
+       // Convert newline to paragraph break in ParbreakIsNewline
+       if (target_inset->getLayout().parbreakIsNewline()
+           || pars[pit].layout().parbreak_is_newline) {
                for (size_t i = 0; i != insertion.size(); ++i) {
                        for (pos_type j = 0; j != insertion[i].size(); ++j) {
                                if (insertion[i].isNewline(j)) {
@@ -227,21 +227,23 @@ pasteSelectionHelper(Cursor & cur, ParagraphList const & parlist,
        InsetIterator const i_end = inset_iterator_end(in);
        for (InsetIterator it = inset_iterator_begin(in); it != i_end; ++it) {
                // 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
+               // since some inset might going to try to access
                // the buffer() member.
                it->setBuffer(const_cast<Buffer &>(buffer));
                switch (it->lyxCode()) {
 
                case MATH_HULL_CODE: {
                        // check for equation labels and resolve duplicates
-                       InsetMathHull & ins = static_cast<InsetMathHull &>(*it);
-                       std::vector<InsetLabel *> labels = ins.getLabels();
+                       InsetMathHull * ins = it->asInsetMath()->asHullInset();
+                       std::vector<InsetLabel *> labels = ins->getLabels();
                        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);
+                               lab->updateLabel(oldname);
+                               // We need to update the buffer reference cache.
+                               cur.forceBufferUpdate();
                                docstring const newname = lab->getParam("name");
                                if (oldname == newname)
                                        continue;
@@ -249,19 +251,17 @@ pasteSelectionHelper(Cursor & cur, ParagraphList const & parlist,
                                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);
+                                               InsetCommand * ref = itt->asInsetCommand();
+                                               if (ref->getParam("reference") == oldname)
+                                                       ref->setParam("reference", newname);
                                        } else if (itt->lyxCode() == MATH_REF_CODE) {
-                                               InsetMathHull & mi =
-                                                       static_cast<InsetMathHull &>(*itt);
+                                               InsetMathRef * mi = itt->asInsetMath()->asRefInset();
                                                // this is necessary to prevent an uninitialized
                                                // buffer when the RefInset is in a MathBox.
-                                               // FIXME audit setBuffer/updateBuffer calls
-                                               mi.setBuffer(const_cast<Buffer &>(buffer));
-                                               if (mi.asRefInset()->getTarget() == oldname)
-                                                       mi.asRefInset()->changeTarget(newname);
+                                               // FIXME audit setBuffer calls
+                                               mi->setBuffer(const_cast<Buffer &>(buffer));
+                                               if (mi->getTarget() == oldname)
+                                                       mi->changeTarget(newname);
                                        }
                                }
                        }
@@ -270,9 +270,11 @@ pasteSelectionHelper(Cursor & cur, ParagraphList const & parlist,
 
                case LABEL_CODE: {
                        // check for duplicates
-                       InsetCommand & lab = static_cast<InsetCommand &>(*it);
+                       InsetLabel & lab = static_cast<InsetLabel &>(*it);
                        docstring const oldname = lab.getParam("name");
-                       lab.updateCommand(oldname, false);
+                       lab.updateLabel(oldname);
+                       // We need to update the buffer reference cache.
+                       cur.forceBufferUpdate();
                        docstring const newname = lab.getParam("name");
                        if (oldname == newname)
                                break;
@@ -283,14 +285,13 @@ pasteSelectionHelper(Cursor & cur, ParagraphList const & parlist,
                                        if (ref.getParam("reference") == oldname)
                                                ref.setParam("reference", newname);
                                } else if (itt->lyxCode() == MATH_REF_CODE) {
-                                       InsetMathHull & mi =
-                                               static_cast<InsetMathHull &>(*itt);
+                                       InsetMathRef * mi = itt->asInsetMath()->asRefInset();
                                        // this is necessary to prevent an uninitialized
                                        // buffer when the RefInset is in a MathBox.
-                                       // FIXME audit setBuffer/updateBuffer calls
-                                       mi.setBuffer(const_cast<Buffer &>(buffer));
-                                       if (mi.asRefInset()->getTarget() == oldname)
-                                               mi.asRefInset()->changeTarget(newname);
+                                       // FIXME audit setBuffer calls
+                                       mi->setBuffer(const_cast<Buffer &>(buffer));
+                                       if (mi->getTarget() == oldname)
+                                               mi->changeTarget(newname);
                                }
                        }
                        break;
@@ -299,14 +300,18 @@ pasteSelectionHelper(Cursor & cur, ParagraphList const & parlist,
                case INCLUDE_CODE: {
                        InsetInclude & inc = static_cast<InsetInclude &>(*it);
                        inc.updateCommand();
+                       // We need to update the list of included files.
+                       cur.forceBufferUpdate();
                        break;
                }
 
                case BIBITEM_CODE: {
                        // check for duplicates
-                       InsetCommand & bib = static_cast<InsetCommand &>(*it);
+                       InsetBibitem & bib = static_cast<InsetBibitem &>(*it);
                        docstring const oldkey = bib.getParam("key");
                        bib.updateCommand(oldkey, false);
+                       // We need to update the buffer reference cache.
+                       cur.forceBufferUpdate();
                        docstring const newkey = bib.getParam("key");
                        if (oldkey == newkey)
                                break;
@@ -314,10 +319,9 @@ pasteSelectionHelper(Cursor & cur, ParagraphList const & parlist,
                        for (InsetIterator itt = inset_iterator_begin(in);
                             itt != i_end; ++itt) {
                                if (itt->lyxCode() == CITE_CODE) {
-                                       InsetCommand & ref =
-                                               static_cast<InsetCommand &>(*itt);
-                                       if (ref.getParam("key") == oldkey)
-                                               ref.setParam("key", newkey);
+                                       InsetCommand * ref = itt->asInsetCommand();
+                                       if (ref->getParam("key") == oldkey)
+                                               ref->setParam("key", newkey);
                                }
                        }
                        break;
@@ -345,6 +349,8 @@ pasteSelectionHelper(Cursor & cur, ParagraphList const & parlist,
                                          text, 0, 1, _("&Add"), _("&Don't Add")) != 0)
                                break;
                        lyx::dispatch(FuncRequest(LFUN_BRANCH_ADD, name));
+                       // We need to update the list of branches.
+                       cur.forceBufferUpdate();
                        break;
                }
 
@@ -454,17 +460,16 @@ PitPosPair eraseSelectionHelper(BufferParams const & params,
 
 
 void putClipboard(ParagraphList const & paragraphs, 
-       DocumentClass const * const docclass, docstring const & plaintext)
+       DocumentClassConstPtr docclass, docstring const & plaintext)
 {
        // For some strange reason gcc 3.2 and 3.3 do not accept
        // Buffer buffer(string(), false);
-       // 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");
+       // This used to need to be static to avoid a memory leak. It no longer needs
+       // to be so, but the alternative is to construct a new one of these (with a
+       // new temporary directory, etc) every time, and then to destroy it. So maybe
+       // it's worth just keeping this one around.
+       static Buffer * buffer = theBufferList().newInternalBuffer(
+               FileName::tempName("clipboard.internal").absFileName());
        buffer->setUnnamed(true);
        buffer->paragraphs() = paragraphs;
        buffer->inset().setBuffer(*buffer);
@@ -496,7 +501,7 @@ static bool isFullyDeleted(ParagraphList const & pars)
 
 void copySelectionHelper(Buffer const & buf, Text const & text,
        pit_type startpit, pit_type endpit,
-       int start, int end, DocumentClass const * const dc, CutStack & cutstack)
+       int start, int end, DocumentClassConstPtr dc, CutStack & cutstack)
 {
        ParagraphList const & pars = text.paragraphs();
 
@@ -521,7 +526,7 @@ void copySelectionHelper(Buffer const & buf, Text const & text,
        ParagraphList::iterator it = copy_pars.begin();
        ParagraphList::iterator it_end = copy_pars.end();
 
-       for (; it != it_end; it++) {
+       for (; it != it_end; ++it) {
                // 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
@@ -530,7 +535,7 @@ void copySelectionHelper(Buffer const & buf, Text const & text,
                // PassThru paragraphs have the Language
                // latex_language. This is invalid for others, so we
                // need to change it to the buffer language.
-               if (text.inset().getLayout().isPassThru())
+               if (it->isPassThru())
                        it->changeLanguage(buf.params(), 
                                           latex_language, buf.language());
        }
@@ -546,13 +551,12 @@ void copySelectionHelper(Buffer const & buf, Text const & text,
        // 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++) {
+       for (; it != it_end; ++it) {
                it->setBuffer(*static_cast<Buffer *>(0));
                it->setInsetOwner(0);
        }
 
-       DocumentClass * d = const_cast<DocumentClass *>(dc);
-       cutstack.push(make_pair(copy_pars, d));
+       cutstack.push(make_pair(copy_pars, dc));
 }
 
 } // namespace anon
@@ -627,8 +631,8 @@ bool multipleCellsSelected(Cursor const & cur)
 }
 
 
-void switchBetweenClasses(DocumentClass const * const oldone, 
-               DocumentClass const * const newone, InsetText & in, ErrorList & errorlist)
+void switchBetweenClasses(DocumentClassConstPtr  oldone,
+               DocumentClassConstPtr newone, InsetText & in, ErrorList & errorlist)
 {
        errorlist.clear();
 
@@ -640,13 +644,25 @@ void switchBetweenClasses(DocumentClass const * const oldone,
        DocumentClass const & newtc = *newone;
 
        // layouts
+       ParIterator it = par_iterator_begin(in);
        ParIterator end = par_iterator_end(in);
-       for (ParIterator it = par_iterator_begin(in); it != end; ++it) {
+       // for remembering which layouts we've had to add
+       set<docstring> newlayouts;
+       for (; it != end; ++it) {
                docstring const name = it->layout().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);
+               bool const added_one = newtc.addLayoutIfNeeded(name);
+               if (added_one)
+                       newlayouts.insert(name);
+
+               if (added_one || newlayouts.find(name) != newlayouts.end()) {
+                       // Warn the user.
+                       docstring const s = bformat(_("Layout `%1$s' was not found."), name);
+                       errorlist.push_back(
+                               ErrorItem(_("Layout Not Found"), s, it->id(), 0, it->size()));
+               }
 
                if (in.usePlainLayout())
                        it->setLayout(newtc.plainLayout());
@@ -660,18 +676,25 @@ void switchBetweenClasses(DocumentClass const * const oldone,
                if (it->lyxCode() != FLEX_CODE)
                        // FIXME: Should we verify all InsetCollapsable?
                        continue;
-               docstring const & n = newone->insetLayout(it->name()).name();
+
+               docstring const layoutName = it->layoutName();
+               docstring const & n = newone->insetLayout(layoutName).name();
                bool const is_undefined = n.empty() ||
                        n == DocumentClass::plainInsetLayout().name();
                if (!is_undefined)
                        continue;
+
                // The flex inset is undefined in newtc
-               docstring const s = bformat(_(
-                       "Flex inset %1$s is "
-                       "undefined because of class "
-                       "conversion from\n%2$s to %3$s"),
-                       it->name(), from_utf8(oldtc.name()),
-                       from_utf8(newtc.name()));
+               docstring const oldname = from_utf8(oldtc.name());
+               docstring const newname = from_utf8(newtc.name());
+               docstring s;
+               if (oldname == newname)
+                       s = bformat(_("Flex inset %1$s is undefined after "
+                               "reloading `%2$s' layout."), layoutName, oldname);
+               else
+                       s = bformat(_("Flex inset %1$s is undefined because of "
+                               "conversion from `%2$s' layout to `%3$s'."),
+                               layoutName, oldname, newname);
                // To warn the user that something had to be done.
                errorlist.push_back(ErrorItem(
                                _("Undefined flex inset"),
@@ -756,7 +779,7 @@ void cutSelection(Cursor & cur, bool doclear, bool realcut)
                }
 
                if (begpit != endpit)
-                       cur.updateFlags(Update::Force | Update::FitCursor);
+                       cur.screenUpdateFlags(Update::Force | Update::FitCursor);
 
                boost::tie(endpit, endpos) =
                        eraseSelectionHelper(bp,
@@ -777,6 +800,11 @@ void cutSelection(Cursor & cur, bool doclear, bool realcut)
 
                // need a valid cursor. (Lgb)
                cur.clearSelection();
+
+               // After a cut operation, we must make sure that the Buffer is updated
+               // because some further operation might need updated label information for
+               // example. So we cannot just use "cur.forceBufferUpdate()" here.
+               // This fixes #7071.
                cur.buffer()->updateBuffer();
 
                // tell tabular that a recent copy happened
@@ -944,7 +972,7 @@ docstring selection(size_t sel_index)
 
 
 void pasteParagraphList(Cursor & cur, ParagraphList const & parlist,
-                       DocumentClass const * const docclass, ErrorList & errorList)
+                       DocumentClassConstPtr docclass, ErrorList & errorList)
 {
        if (cur.inTexted()) {
                Text * text = cur.text();
@@ -955,7 +983,7 @@ void pasteParagraphList(Cursor & cur, ParagraphList const & parlist,
 
                boost::tie(ppp, endpit) =
                        pasteSelectionHelper(cur, parlist, docclass, errorList);
-               cur.buffer()->updateBuffer();
+               cur.forceBufferUpdate();
                cur.clearSelection();
                text->setCursor(cur, ppp.first, ppp.second);
        }
@@ -974,7 +1002,6 @@ void pasteFromStack(Cursor & cur, ErrorList & errorList, size_t sel_index)
        cur.recordUndo();
        pasteParagraphList(cur, theCuts[sel_index].first,
                           theCuts[sel_index].second, errorList);
-       cur.setSelection();
 }
 
 
@@ -998,7 +1025,6 @@ void pasteClipboardText(Cursor & cur, ErrorList & errorList, bool asParagraphs)
                                cur.recordUndo();
                                pasteParagraphList(cur, buffer.paragraphs(),
                                        buffer.params().documentClassPtr(), errorList);
-                               cur.setSelection();
                                return;
                        }
                }
@@ -1016,6 +1042,39 @@ void pasteClipboardText(Cursor & cur, ErrorList & errorList, bool asParagraphs)
 }
 
 
+void pasteSimpleText(Cursor & cur, bool asParagraphs)
+{
+       docstring text;
+       // Use internal clipboard if it is the most recent one
+       if (theClipboard().isInternal()) {
+               if (!checkPastePossible(0))
+                       return;
+
+               ParagraphList const & pars = theCuts[0].first;
+               ParagraphList::const_iterator it = pars.begin();
+               for (; it != pars.end(); ++it) {
+                       if (it != pars.begin())
+                               text += "\n";
+                       text += (*it).asString();
+               }
+               asParagraphs = false;
+       } else {
+               // Then try plain text
+               text = theClipboard().getAsText();
+       }
+
+       if (text.empty())
+               return;
+
+       cur.recordUndo();
+       cutSelection(cur, true, false);
+       if (asParagraphs)
+               cur.text()->insertStringAsParagraphs(cur, text, cur.current_font);
+       else
+               cur.text()->insertStringAsLines(cur, text, cur.current_font);
+}
+
+
 void pasteClipboardGraphics(Cursor & cur, ErrorList & /* errorList */,
                            Clipboard::GraphicsType preferedType)
 {
@@ -1029,7 +1088,7 @@ void pasteClipboardGraphics(Cursor & cur, ErrorList & /* errorList */,
        // create inset for graphic
        InsetGraphics * inset = new InsetGraphics(cur.buffer());
        InsetGraphicsParams params;
-       params.filename = support::DocFileName(filename.absFileName());
+       params.filename = support::DocFileName(filename.absFileName(), false);
        inset->setParams(params);
        cur.recordUndo();
        cur.insert(inset);
@@ -1046,7 +1105,7 @@ void pasteSelection(Cursor & cur, ErrorList & errorList)
 }
 
 
-void replaceSelectionWithString(Cursor & cur, docstring const & str, bool backwards)
+void replaceSelectionWithString(Cursor & cur, docstring const & str)
 {
        cur.recordUndo();
        DocIterator selbeg = cur.selectionBegin();
@@ -1065,13 +1124,6 @@ void replaceSelectionWithString(Cursor & cur, docstring const & str, bool backwa
 
        // Cut the selection
        cutSelection(cur, true, false);
-
-       // select the replacement
-       if (backwards) {
-               selbeg.pos() += str.length();
-               cur.setSelection(selbeg, -int(str.length()));
-       } else
-               cur.setSelection(selbeg, str.length());
 }