2 * \file CutAndPaste.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
7 * \author Lars Gullik Bjønnes
8 * \author Alfredo Braunstein
11 * Full author contact details are available in file CREDITS.
16 #include "CutAndPaste.h"
19 #include "buffer_funcs.h"
20 #include "BufferParams.h"
21 #include "BufferView.h"
24 #include "ErrorList.h"
26 #include "FuncRequest.h"
27 #include "InsetIterator.h"
28 #include "InsetList.h"
33 #include "Paragraph.h"
34 #include "paragraph_funcs.h"
35 #include "ParagraphParameters.h"
36 #include "ParIterator.h"
39 #include "insets/InsetFlex.h"
40 #include "insets/InsetCommand.h"
41 #include "insets/InsetGraphics.h"
42 #include "insets/InsetGraphicsParams.h"
43 #include "insets/InsetTabular.h"
45 #include "mathed/MathData.h"
46 #include "mathed/InsetMath.h"
47 #include "mathed/MathSupport.h"
49 #include "support/debug.h"
50 #include "support/docstream.h"
51 #include "support/gettext.h"
52 #include "support/limited_stack.h"
53 #include "support/lstrings.h"
55 #include "frontends/Clipboard.h"
56 #include "frontends/Selection.h"
58 #include <boost/tuple/tuple.hpp>
59 #include <boost/next_prior.hpp>
64 using namespace lyx::support
;
65 using lyx::frontend::Clipboard
;
71 typedef pair
<pit_type
, int> PitPosPair
;
73 typedef limited_stack
<pair
<ParagraphList
, DocumentClass
const *> > CutStack
;
76 // persistent selection, cleared until the next selection
77 CutStack
selectionBuffer(1);
79 // store whether the tabular stack is newer than the normal copy stack
80 // FIXME: this is a workaround for bug 1919. Should be removed for 1.5,
81 // when we (hopefully) have a one-for-all paste mechanism.
82 bool dirty_tabular_stack_
= false;
85 bool checkPastePossible(int index
)
87 return size_t(index
) < theCuts
.size() && !theCuts
[index
].first
.empty();
91 pair
<PitPosPair
, pit_type
>
92 pasteSelectionHelper(Cursor
& cur
, ParagraphList
const & parlist
,
93 DocumentClass
const * const docclass
, ErrorList
& errorlist
)
95 Buffer
const & buffer
= cur
.buffer();
96 pit_type pit
= cur
.pit();
97 pos_type pos
= cur
.pos();
98 ParagraphList
& pars
= cur
.text()->paragraphs();
101 return make_pair(PitPosPair(pit
, pos
), pit
);
103 BOOST_ASSERT (pos
<= pars
[pit
].size());
105 // Make a copy of the CaP paragraphs.
106 ParagraphList insertion
= parlist
;
107 DocumentClass
const * const tc
= buffer
.params().documentClassPtr();
109 // Now remove all out of the pars which is NOT allowed in the
110 // new environment and set also another font if that is required.
112 // Convert newline to paragraph break in ERT inset.
113 // This should not be here!
114 Inset
* inset
= pars
[pit
].inInset();
115 if (inset
&& (inset
->lyxCode() == ERT_CODE
||
116 inset
->lyxCode() == LISTINGS_CODE
)) {
117 for (size_t i
= 0; i
!= insertion
.size(); ++i
) {
118 for (pos_type j
= 0; j
!= insertion
[i
].size(); ++j
) {
119 if (insertion
[i
].isNewline(j
)) {
120 // do not track deletion of newline
121 insertion
[i
].eraseChar(j
, false);
122 breakParagraphConservative(
130 // set the paragraphs to empty layout if necessary
131 // note that we are doing this if the empty layout is
132 // supposed to be the default, not just if it is forced
133 if (cur
.inset().useEmptyLayout()) {
134 Layout
const & layout
=
135 buffer
.params().documentClass().emptyLayout();
136 ParagraphList::iterator
const end
= insertion
.end();
137 for (ParagraphList::iterator par
= insertion
.begin();
139 par
->setLayout(layout
);
142 // Make sure there is no class difference.
144 // This works without copying any paragraph data because we have
145 // a specialized swap method for ParagraphList. This is important
146 // since we store pointers to insets at some places and we don't
147 // want to invalidate them.
148 insertion
.swap(in
.paragraphs());
149 cap::switchBetweenClasses(docclass
, tc
, in
, errorlist
);
150 insertion
.swap(in
.paragraphs());
152 ParagraphList::iterator tmpbuf
= insertion
.begin();
153 int depth_delta
= pars
[pit
].params().depth() - tmpbuf
->params().depth();
155 depth_type max_depth
= pars
[pit
].getMaxDepthAfter();
157 for (; tmpbuf
!= insertion
.end(); ++tmpbuf
) {
158 // If we have a negative jump so that the depth would
159 // go below 0 depth then we have to redo the delta to
160 // this new max depth level so that subsequent
161 // paragraphs are aligned correctly to this paragraph
163 if (int(tmpbuf
->params().depth()) + depth_delta
< 0)
166 // Set the right depth so that we are not too deep or shallow.
167 tmpbuf
->params().depth(tmpbuf
->params().depth() + depth_delta
);
168 if (tmpbuf
->params().depth() > max_depth
)
169 tmpbuf
->params().depth(max_depth
);
171 // Only set this from the 2nd on as the 2nd depends
172 // for maxDepth still on pit.
173 if (tmpbuf
!= insertion
.begin())
174 max_depth
= tmpbuf
->getMaxDepthAfter();
176 // Set the inset owner of this paragraph.
177 tmpbuf
->setInsetOwner(pars
[pit
].inInset());
178 for (pos_type i
= 0; i
< tmpbuf
->size(); ++i
) {
179 // do not track deletion of invalid insets
180 if (Inset
* inset
= tmpbuf
->getInset(i
))
181 if (!pars
[pit
].insetAllowed(inset
->lyxCode()))
182 tmpbuf
->eraseChar(i
--, false);
185 tmpbuf
->setChange(Change(buffer
.params().trackChanges
?
186 Change::INSERTED
: Change::UNCHANGED
));
189 bool const empty
= pars
[pit
].empty();
191 // Make the buf exactly the same layout as the cursor
193 insertion
.begin()->makeSameLayout(pars
[pit
]);
196 // Prepare the paragraphs and insets for insertion.
197 // A couple of insets store buffer references so need updating.
198 insertion
.swap(in
.paragraphs());
200 InsetIterator
const i_end
= inset_iterator_end(in
);
202 for (InsetIterator it
= inset_iterator_begin(in
); it
!= i_end
; ++it
) {
204 it
->setBuffer(const_cast<Buffer
&>(buffer
));
206 switch (it
->lyxCode()) {
209 // check for duplicates
210 InsetCommand
& lab
= static_cast<InsetCommand
&>(*it
);
211 docstring
const oldname
= lab
.getParam("name");
212 lab
.updateCommand(oldname
, false);
213 docstring
const newname
= lab
.getParam("name");
214 if (oldname
!= newname
) {
215 // adapt the references
216 for (InsetIterator itt
= inset_iterator_begin(in
); itt
!= i_end
; ++itt
) {
217 if (itt
->lyxCode() == REF_CODE
) {
218 InsetCommand
& ref
= dynamic_cast<InsetCommand
&>(*itt
);
219 if (ref
.getParam("reference") == oldname
)
220 ref
.setParam("reference", newname
);
228 // check for duplicates
229 InsetCommand
& bib
= static_cast<InsetCommand
&>(*it
);
230 docstring
const oldkey
= bib
.getParam("key");
231 bib
.updateCommand(oldkey
, false);
232 docstring
const newkey
= bib
.getParam("key");
233 if (oldkey
!= newkey
) {
234 // adapt the references
235 for (InsetIterator itt
= inset_iterator_begin(in
); itt
!= i_end
; ++itt
) {
236 if (itt
->lyxCode() == CITE_CODE
) {
237 InsetCommand
& ref
= dynamic_cast<InsetCommand
&>(*itt
);
238 if (ref
.getParam("key") == oldkey
)
239 ref
.setParam("key", newkey
);
250 insertion
.swap(in
.paragraphs());
252 // Split the paragraph for inserting the buf if necessary.
254 breakParagraphConservative(buffer
.params(), pars
, pit
, pos
);
258 pars
.insert(boost::next(pars
.begin(), pit
),
262 // merge the empty par with the last par of the insertion
263 mergeParagraph(buffer
.params(), pars
,
264 pit
+ insertion
.size() - 1);
266 pars
.insert(boost::next(pars
.begin(), pit
+ 1),
270 // merge the first par of the insertion with the current par
271 mergeParagraph(buffer
.params(), pars
, pit
);
274 pit_type last_paste
= pit
+ insertion
.size() - 1;
276 // Store the new cursor position.
278 pos
= pars
[last_paste
].size();
280 // Join (conditionally) last pasted paragraph with next one, i.e.,
281 // the tail of the spliced document paragraph
282 if (!empty
&& last_paste
+ 1 != pit_type(pars
.size())) {
283 if (pars
[last_paste
+ 1].hasSameLayout(pars
[last_paste
])) {
284 mergeParagraph(buffer
.params(), pars
, last_paste
);
285 } else if (pars
[last_paste
+ 1].empty()) {
286 pars
[last_paste
+ 1].makeSameLayout(pars
[last_paste
]);
287 mergeParagraph(buffer
.params(), pars
, last_paste
);
288 } else if (pars
[last_paste
].empty()) {
289 pars
[last_paste
].makeSameLayout(pars
[last_paste
+ 1]);
290 mergeParagraph(buffer
.params(), pars
, last_paste
);
292 pars
[last_paste
+ 1].stripLeadingSpaces(buffer
.params().trackChanges
);
297 return make_pair(PitPosPair(pit
, pos
), last_paste
+ 1);
301 PitPosPair
eraseSelectionHelper(BufferParams
const & params
,
302 ParagraphList
& pars
,
303 pit_type startpit
, pit_type endpit
,
304 int startpos
, int endpos
)
306 // Start of selection is really invalid.
307 if (startpit
== pit_type(pars
.size()) ||
308 (startpos
> pars
[startpit
].size()))
309 return PitPosPair(endpit
, endpos
);
311 // Start and end is inside same paragraph
312 if (endpit
== pit_type(pars
.size()) || startpit
== endpit
) {
313 endpos
-= pars
[startpit
].eraseChars(startpos
, endpos
, params
.trackChanges
);
314 return PitPosPair(endpit
, endpos
);
317 for (pit_type pit
= startpit
; pit
!= endpit
+ 1;) {
318 pos_type
const left
= (pit
== startpit
? startpos
: 0);
319 pos_type right
= (pit
== endpit
? endpos
: pars
[pit
].size() + 1);
320 bool const merge
= pars
[pit
].isMergedOnEndOfParDeletion(params
.trackChanges
);
322 // Logically erase only, including the end-of-paragraph character
323 pars
[pit
].eraseChars(left
, right
, params
.trackChanges
);
325 // Separate handling of paragraph break:
326 if (merge
&& pit
!= endpit
&&
328 || pars
[pit
].hasSameLayout(pars
[endpit
])
329 || pars
[endpit
].size() == endpos
)) {
330 if (pit
+ 1 == endpit
)
331 endpos
+= pars
[pit
].size();
332 mergeParagraph(params
, pars
, pit
);
338 // Ensure legal cursor pos:
341 return PitPosPair(endpit
, endpos
);
345 void putClipboard(ParagraphList
const & paragraphs
,
346 DocumentClass
const * const docclass
, docstring
const & plaintext
)
348 // For some strange reason gcc 3.2 and 3.3 do not accept
349 // Buffer buffer(string(), false);
350 Buffer
buffer("", false);
351 buffer
.setUnnamed(true);
352 buffer
.paragraphs() = paragraphs
;
353 buffer
.params().setDocumentClass(docclass
);
355 if (buffer
.write(lyx
))
356 theClipboard().put(lyx
.str(), plaintext
);
358 theClipboard().put(string(), plaintext
);
362 void copySelectionHelper(Buffer
const & buf
, ParagraphList
& pars
,
363 pit_type startpit
, pit_type endpit
,
364 int start
, int end
, DocumentClass
const * const dc
, CutStack
& cutstack
)
366 BOOST_ASSERT(0 <= start
&& start
<= pars
[startpit
].size());
367 BOOST_ASSERT(0 <= end
&& end
<= pars
[endpit
].size());
368 BOOST_ASSERT(startpit
!= endpit
|| start
<= end
);
370 // Clone the paragraphs within the selection.
371 ParagraphList
copy_pars(boost::next(pars
.begin(), startpit
),
372 boost::next(pars
.begin(), endpit
+ 1));
374 // Remove the end of the last paragraph; afterwards, remove the
375 // beginning of the first paragraph. Keep this order - there may only
376 // be one paragraph! Do not track deletions here; this is an internal
377 // action not visible to the user
379 Paragraph
& back
= copy_pars
.back();
380 back
.eraseChars(end
, back
.size(), false);
381 Paragraph
& front
= copy_pars
.front();
382 front
.eraseChars(0, start
, false);
384 ParagraphList::iterator it
= copy_pars
.begin();
385 ParagraphList::iterator it_end
= copy_pars
.end();
387 for (; it
!= it_end
; it
++) {
388 // ERT paragraphs have the Language latex_language.
389 // This is invalid outside of ERT, so we need to change it
390 // to the buffer language.
391 if (it
->ownerCode() == ERT_CODE
|| it
->ownerCode() == LISTINGS_CODE
) {
392 it
->changeLanguage(buf
.params(), latex_language
, buf
.language());
394 it
->setInsetOwner(0);
397 // do not copy text (also nested in insets) which is marked as deleted
398 acceptChanges(copy_pars
, buf
.params());
400 DocumentClass
* d
= const_cast<DocumentClass
*>(dc
);
401 cutstack
.push(make_pair(copy_pars
, d
));
411 void region(CursorSlice
const & i1
, CursorSlice
const & i2
,
412 Inset::row_type
& r1
, Inset::row_type
& r2
,
413 Inset::col_type
& c1
, Inset::col_type
& c2
)
415 Inset
& p
= i1
.inset();
416 c1
= p
.col(i1
.idx());
417 c2
= p
.col(i2
.idx());
420 r1
= p
.row(i1
.idx());
421 r2
= p
.row(i2
.idx());
427 docstring
grabAndEraseSelection(Cursor
& cur
)
429 if (!cur
.selection())
431 docstring res
= grabSelection(cur
);
437 bool reduceSelectionToOneCell(Cursor
& cur
)
439 if (!cur
.selection() || !cur
.inMathed())
442 CursorSlice i1
= cur
.selBegin();
443 CursorSlice i2
= cur
.selEnd();
444 if (!i1
.inset().asInsetMath())
447 // the easy case: do nothing if only one cell is selected
448 if (i1
.idx() == i2
.idx())
453 cur
.top().pos() = cur
.top().lastpos();
459 bool multipleCellsSelected(Cursor
const & cur
)
461 if (!cur
.selection() || !cur
.inMathed())
464 CursorSlice i1
= cur
.selBegin();
465 CursorSlice i2
= cur
.selEnd();
466 if (!i1
.inset().asInsetMath())
469 if (i1
.idx() == i2
.idx())
476 void switchBetweenClasses(DocumentClass
const * const oldone
,
477 DocumentClass
const * const newone
, InsetText
& in
, ErrorList
& errorlist
)
481 BOOST_ASSERT(!in
.paragraphs().empty());
482 if (oldone
== newone
)
485 DocumentClass
const & oldtc
= *oldone
;
486 DocumentClass
const & newtc
= *newone
;
489 ParIterator end
= par_iterator_end(in
);
490 for (ParIterator it
= par_iterator_begin(in
); it
!= end
; ++it
) {
491 docstring
const name
= it
->layout().name();
492 bool hasLayout
= newtc
.hasLayout(name
);
494 if (in
.useEmptyLayout())
495 it
->setLayout(newtc
.emptyLayout());
497 it
->setLayout(newtc
[name
]);
499 it
->setLayout(newtc
.defaultLayout());
501 if (!hasLayout
&& name
!= oldtc
.defaultLayoutName()) {
502 docstring
const s
= bformat(
503 _("Layout had to be changed from\n%1$s to %2$s\n"
504 "because of class conversion from\n%3$s to %4$s"),
505 name
, it
->layout().name(),
506 from_utf8(oldtc
.name()), from_utf8(newtc
.name()));
507 // To warn the user that something had to be done.
508 errorlist
.push_back(ErrorItem(_("Changed Layout"), s
,
515 InsetIterator
const i_end
= inset_iterator_end(in
);
516 for (InsetIterator it
= inset_iterator_begin(in
); it
!= i_end
; ++it
) {
517 InsetCollapsable
* inset
= it
->asInsetCollapsable();
520 if (inset
->lyxCode() != FLEX_CODE
)
521 // FIXME: Should we verify all InsetCollapsable?
523 inset
->setLayout(newone
);
524 if (!inset
->undefined())
526 // The flex inset is undefined in newtc
527 docstring
const s
= bformat(_(
528 "Flex inset %1$s is "
529 "undefined because of class "
530 "conversion from\n%2$s to %3$s"),
531 inset
->name(), from_utf8(oldtc
.name()),
532 from_utf8(newtc
.name()));
533 // To warn the user that something had to be done.
534 errorlist
.push_back(ErrorItem(
535 _("Undefined flex inset"),
536 s
, it
.paragraph().id(), it
.pos(), it
.pos() + 1));
541 vector
<docstring
> availableSelections()
543 vector
<docstring
> selList
;
545 CutStack::const_iterator cit
= theCuts
.begin();
546 CutStack::const_iterator end
= theCuts
.end();
547 for (; cit
!= end
; ++cit
) {
548 // we do not use cit-> here because gcc 2.9x does not
550 ParagraphList
const & pars
= (*cit
).first
;
552 ParagraphList::const_iterator pit
= pars
.begin();
553 ParagraphList::const_iterator pend
= pars
.end();
554 for (; pit
!= pend
; ++pit
) {
555 asciiSel
+= pit
->asString(false);
556 if (asciiSel
.size() > 25) {
557 asciiSel
.replace(22, docstring::npos
,
563 selList
.push_back(asciiSel
);
570 size_type
numberOfSelections()
572 return theCuts
.size();
576 void cutSelection(Cursor
& cur
, bool doclear
, bool realcut
)
578 // This doesn't make sense, if there is no selection
579 if (!cur
.selection())
582 // OK, we have a selection. This is always between cur.selBegin()
585 if (cur
.inTexted()) {
586 Text
* text
= cur
.text();
591 // make sure that the depth behind the selection are restored, too
592 cur
.recordUndoSelection();
593 pit_type begpit
= cur
.selBegin().pit();
594 pit_type endpit
= cur
.selEnd().pit();
596 int endpos
= cur
.selEnd().pos();
598 BufferParams
const & bp
= cur
.buffer().params();
600 copySelectionHelper(cur
.buffer(),
603 cur
.selBegin().pos(), endpos
,
604 bp
.documentClassPtr(), theCuts
);
605 // Stuff what we got on the clipboard.
606 // Even if there is no selection.
607 putClipboard(theCuts
[0].first
, theCuts
[0].second
,
608 cur
.selectionAsString(true));
611 boost::tie(endpit
, endpos
) =
612 eraseSelectionHelper(bp
,
615 cur
.selBegin().pos(), endpos
);
617 // cutSelection can invalidate the cursor so we need to set
619 // we prefer the end for when tracking changes
623 // sometimes necessary
625 && text
->paragraphs()[begpit
].stripLeadingSpaces(bp
.trackChanges
))
628 // need a valid cursor. (Lgb)
629 cur
.clearSelection();
630 updateLabels(cur
.buffer());
632 // tell tabular that a recent copy happened
633 dirtyTabularStack(false);
636 if (cur
.inMathed()) {
637 if (cur
.selBegin().idx() != cur
.selEnd().idx()) {
638 // The current selection spans more than one cell.
640 cur
.recordUndoInset();
642 // Record only the current cell to avoid a jumping
653 void copySelection(Cursor
& cur
)
655 copySelection(cur
, cur
.selectionAsString(true));
661 void copySelectionToStack(Cursor
& cur
, CutStack
& cutstack
)
663 // this doesn't make sense, if there is no selection
664 if (!cur
.selection())
667 // copySelection can not yet handle the case of cross idx selection
668 if (cur
.selBegin().idx() != cur
.selEnd().idx())
671 if (cur
.inTexted()) {
672 Text
* text
= cur
.text();
674 // ok we have a selection. This is always between cur.selBegin()
675 // and sel_end cursor
677 // copy behind a space if there is one
678 ParagraphList
& pars
= text
->paragraphs();
679 pos_type pos
= cur
.selBegin().pos();
680 pit_type par
= cur
.selBegin().pit();
681 while (pos
< pars
[par
].size() &&
682 pars
[par
].isLineSeparator(pos
) &&
683 (par
!= cur
.selEnd().pit() || pos
< cur
.selEnd().pos()))
686 copySelectionHelper(cur
.buffer(), pars
, par
, cur
.selEnd().pit(),
687 pos
, cur
.selEnd().pos(),
688 cur
.buffer().params().documentClassPtr(), cutstack
);
689 dirtyTabularStack(false);
692 if (cur
.inMathed()) {
693 //lyxerr << "copySelection in mathed" << endl;
696 BufferParams
const & bp
= cur
.buffer().params();
697 // FIXME This should be the empty layout...right?
698 par
.setLayout(bp
.documentClass().emptyLayout());
699 par
.insert(0, grabSelection(cur
), Font(), Change(Change::UNCHANGED
));
701 cutstack
.push(make_pair(pars
, bp
.documentClassPtr()));
708 void copySelectionToStack()
710 if (!selectionBuffer
.empty())
711 theCuts
.push(selectionBuffer
[0]);
715 void copySelection(Cursor
& cur
, docstring
const & plaintext
)
717 // In tablemode, because copy and paste actually use special table stack
718 // we do not attempt to get selected paragraphs under cursor. Instead, a
719 // paragraph with the plain text version is generated so that table cells
720 // can be pasted as pure text somewhere else.
721 if (cur
.selBegin().idx() != cur
.selEnd().idx()) {
724 BufferParams
const & bp
= cur
.buffer().params();
725 par
.setLayout(bp
.documentClass().emptyLayout());
726 par
.insert(0, plaintext
, Font(), Change(Change::UNCHANGED
));
728 theCuts
.push(make_pair(pars
, bp
.documentClassPtr()));
730 copySelectionToStack(cur
, theCuts
);
733 // stuff the selection onto the X clipboard, from an explicit copy request
734 putClipboard(theCuts
[0].first
, theCuts
[0].second
, plaintext
);
738 void saveSelection(Cursor
& cur
)
740 // This function is called, not when a selection is formed, but when
741 // a selection is cleared. Therefore, multiple keyboard selection
742 // will not repeatively trigger this function (bug 3877).
744 && cur
.selBegin() == cur
.bv().cursor().selBegin()
745 && cur
.selEnd() == cur
.bv().cursor().selEnd()) {
746 LYXERR(Debug::ACTION
, "'" << cur
.selectionAsString(true) << "'");
747 copySelectionToStack(cur
, selectionBuffer
);
754 return !selectionBuffer
.empty();
758 void clearSelection()
760 selectionBuffer
.clear();
770 docstring
selection(size_t sel_index
)
772 return sel_index
< theCuts
.size()
773 ? theCuts
[sel_index
].first
.back().asString(false)
778 void pasteParagraphList(Cursor
& cur
, ParagraphList
const & parlist
,
779 DocumentClass
const * const docclass
, ErrorList
& errorList
)
781 if (cur
.inTexted()) {
782 Text
* text
= cur
.text();
788 boost::tie(ppp
, endpit
) =
789 pasteSelectionHelper(cur
, parlist
, docclass
, errorList
);
790 updateLabels(cur
.buffer());
791 cur
.clearSelection();
792 text
->setCursor(cur
, ppp
.first
, ppp
.second
);
795 // mathed is handled in InsetMathNest/InsetMathGrid
796 BOOST_ASSERT(!cur
.inMathed());
800 void pasteFromStack(Cursor
& cur
, ErrorList
& errorList
, size_t sel_index
)
802 // this does not make sense, if there is nothing to paste
803 if (!checkPastePossible(sel_index
))
807 pasteParagraphList(cur
, theCuts
[sel_index
].first
,
808 theCuts
[sel_index
].second
, errorList
);
813 void pasteClipboardText(Cursor
& cur
, ErrorList
& errorList
, bool asParagraphs
)
815 // Use internal clipboard if it is the most recent one
816 if (theClipboard().isInternal()) {
817 pasteFromStack(cur
, errorList
, 0);
821 // First try LyX format
822 if (theClipboard().hasLyXContents()) {
823 string lyx
= theClipboard().getAsLyX();
825 // For some strange reason gcc 3.2 and 3.3 do not accept
826 // Buffer buffer(string(), false);
827 Buffer
buffer("", false);
828 buffer
.setUnnamed(true);
829 if (buffer
.readString(lyx
)) {
831 pasteParagraphList(cur
, buffer
.paragraphs(),
832 buffer
.params().documentClassPtr(), errorList
);
839 // Then try plain text
840 docstring
const text
= theClipboard().getAsText();
845 cur
.text()->insertStringAsParagraphs(cur
, text
);
847 cur
.text()->insertStringAsLines(cur
, text
);
851 void pasteClipboardGraphics(Cursor
& cur
, ErrorList
& /* errorList */,
852 Clipboard::GraphicsType preferedType
)
854 BOOST_ASSERT(theClipboard().hasGraphicsContents(preferedType
));
856 // get picture from clipboard
857 FileName filename
= theClipboard().getAsGraphics(cur
, preferedType
);
858 if (filename
.empty())
861 // create inset for graphic
862 InsetGraphics
* inset
= new InsetGraphics
;
863 InsetGraphicsParams params
;
864 params
.filename
= EmbeddedFile(filename
.absFilename(), cur
.buffer().filePath());
865 inset
->setParams(params
);
871 void pasteSelection(Cursor
& cur
, ErrorList
& errorList
)
873 if (selectionBuffer
.empty())
876 pasteParagraphList(cur
, selectionBuffer
[0].first
,
877 selectionBuffer
[0].second
, errorList
);
881 void replaceSelectionWithString(Cursor
& cur
, docstring
const & str
, bool backwards
)
884 DocIterator selbeg
= cur
.selectionBegin();
886 // Get font setting before we cut
888 selbeg
.paragraph().getFontSettings(cur
.buffer().params(), selbeg
.pos());
890 // Insert the new string
891 pos_type pos
= cur
.selEnd().pos();
892 Paragraph
& par
= cur
.selEnd().paragraph();
893 docstring::const_iterator cit
= str
.begin();
894 docstring::const_iterator end
= str
.end();
895 for (; cit
!= end
; ++cit
, ++pos
)
896 par
.insertChar(pos
, *cit
, font
, cur
.buffer().params().trackChanges
);
899 cutSelection(cur
, true, false);
901 // select the replacement
903 selbeg
.pos() += str
.length();
904 cur
.setSelection(selbeg
, -int(str
.length()));
906 cur
.setSelection(selbeg
, str
.length());
910 void replaceSelection(Cursor
& cur
)
913 cutSelection(cur
, true, false);
917 void eraseSelection(Cursor
& cur
)
919 //lyxerr << "cap::eraseSelection begin: " << cur << endl;
920 CursorSlice
const & i1
= cur
.selBegin();
921 CursorSlice
const & i2
= cur
.selEnd();
922 if (i1
.inset().asInsetMath()) {
925 if (i1
.idx() == i2
.idx()) {
926 i1
.cell().erase(i1
.pos(), i2
.pos());
927 // We may have deleted i1.cell(cur.pos()).
928 // Make sure that pos is valid.
929 if (cur
.pos() > cur
.lastpos())
930 cur
.pos() = cur
.lastpos();
932 InsetMath
* p
= i1
.asInsetMath();
933 Inset::row_type r1
, r2
;
934 Inset::col_type c1
, c2
;
935 region(i1
, i2
, r1
, r2
, c1
, c2
);
936 for (Inset::row_type row
= r1
; row
<= r2
; ++row
)
937 for (Inset::col_type col
= c1
; col
<= c2
; ++col
)
938 p
->cell(p
->index(row
, col
)).clear();
939 // We've deleted the whole cell. Only pos 0 is valid.
942 // need a valid cursor. (Lgb)
943 cur
.clearSelection();
945 lyxerr
<< "can't erase this selection 1" << endl
;
947 //lyxerr << "cap::eraseSelection end: " << cur << endl;
951 void selDel(Cursor
& cur
)
953 //lyxerr << "cap::selDel" << endl;
959 void selClearOrDel(Cursor
& cur
)
961 //lyxerr << "cap::selClearOrDel" << endl;
962 if (lyxrc
.auto_region_delete
)
965 cur
.selection() = false;
969 docstring
grabSelection(Cursor
const & cur
)
971 if (!cur
.selection())
975 // grab selection by glueing multiple cells together. This is not what
976 // we want because selections spanning multiple cells will get "&" and "\\"
979 for (DocIterator dit
= cur
.selectionBegin();
980 dit
!= cur
.selectionEnd(); dit
.forwardPos())
981 os
<< asString(dit
.cell());
985 CursorSlice i1
= cur
.selBegin();
986 CursorSlice i2
= cur
.selEnd();
988 if (i1
.idx() == i2
.idx()) {
989 if (i1
.inset().asInsetMath()) {
990 MathData::const_iterator it
= i1
.cell().begin();
991 return asString(MathData(it
+ i1
.pos(), it
+ i2
.pos()));
993 return from_ascii("unknown selection 1");
997 Inset::row_type r1
, r2
;
998 Inset::col_type c1
, c2
;
999 region(i1
, i2
, r1
, r2
, c1
, c2
);
1002 if (i1
.inset().asInsetMath()) {
1003 for (Inset::row_type row
= r1
; row
<= r2
; ++row
) {
1006 for (Inset::col_type col
= c1
; col
<= c2
; ++col
) {
1009 data
+= asString(i1
.asInsetMath()->
1010 cell(i1
.asInsetMath()->index(row
, col
)));
1014 data
= from_ascii("unknown selection 2");
1020 void dirtyTabularStack(bool b
)
1022 dirty_tabular_stack_
= b
;
1026 bool tabularStackDirty()
1028 return dirty_tabular_stack_
;