1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995-2000 The LyX Team.
8 * ====================================================== */
12 #include "CutAndPaste.h"
13 #include "BufferView.h"
15 #include "lyxparagraph.h"
16 #include "insets/inseterror.h"
17 #include "lyx_gui_misc.h"
18 #include "lyxcursor.h"
22 #pragma implementation
27 extern BufferView * current_view;
29 // Jürgen, note that this means that you cannot currently have a list
30 // of selections cut/copied. So IMHO later we should have a
31 // list/vector/deque that we could store
32 // struct selection_item {
33 // LyXParagraph * buf;
34 // LyXTextClassList::size_type textclass;
36 // in and some method of choosing beween them (based on the first few chars
37 // in the selection probably.) This would be a nice feature and quite
38 // easy to implement. (Lgb)
40 // Sure but I just cleaned up this code for now with the same functionality
41 // as before. I also want to add a XClipboard function so that we can copy
42 // text from LyX to some other X-application in the form of ASCII or in the
43 // form of LaTeX (or Docbook depending on the document-class!). Think how nice
44 // it could be to select a math-inset do a "Copy to X-Clipboard as LaTeX" and
45 // then do a middle mouse button click in the application you want and have
46 // the whole formula there in LaTeX-Code. (Jug)
50 LyXParagraph * buf = 0;
51 LyXTextClassList::size_type textclass = 0;
53 // for now here this should be in another Cut&Paste Class!
54 // Jürgen, I moved this out of CutAndPaste since it does not operate on any
55 // member of the CutAndPaste class and in addition it was private.
56 // Perhaps it even should take a parameter? (Lgb)
62 LyXParagraph * tmppar;
80 bool CutAndPaste::cutSelection(LyXParagraph * startpar, LyXParagraph ** endpar,
81 int start, int & end, char tc, bool doclear)
83 if (!startpar || (start > startpar->Last()))
91 (startpar->ParFromPos(start) ==
92 (*endpar)->ParFromPos(end))) {
93 // only within one paragraph
94 buf = new LyXParagraph;
95 LyXParagraph::size_type i = start;
96 if (end > startpar->Last())
97 end = startpar->Last();
98 for (; i < end; ++i) {
99 startpar->CopyIntoMinibuffer(*current_view->buffer(), start);
100 startpar->Erase(start);
102 buf->InsertFromMinibuffer(buf->Last());
106 // more than one paragraph
107 (*endpar)->BreakParagraphConservative(current_view->buffer()->params,
109 *endpar = (*endpar)->next();
112 startpar->BreakParagraphConservative(current_view->buffer()->params,
115 // store the selection
116 buf = startpar->ParFromPos(start)->next_;
118 (*endpar)->previous()->next(0);
121 startpar->ParFromPos(start)->next(*endpar);
123 (*endpar)->previous(startpar->ParFromPos(start));
125 // care about footnotes
126 if (buf->footnoteflag) {
127 LyXParagraph * tmppar = buf;
129 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
130 tmppar = tmppar->next_;
134 // the cut selection should begin with standard layout
137 // paste the paragraphs again, if possible
139 startpar->next()->StripLeadingSpaces(textclass);
140 if (startpar->FirstPhysicalPar()->HasSameLayout(startpar->next()) ||
141 !startpar->next()->Last()) {
142 startpar->ParFromPos(start)->PasteParagraph(current_view->buffer()->params);
143 (*endpar) = startpar; // this because endpar gets deleted here!
149 bool CutAndPaste::cutSelection(LyXParagraph * startpar, LyXParagraph ** endpar,
150 int start, int & end, char tc, bool doclear)
152 if (!startpar || (start > startpar->size()))
160 startpar == (*endpar)) {
161 // only within one paragraph
162 buf = new LyXParagraph;
163 LyXParagraph::size_type i = start;
164 if (end > startpar->size())
165 end = startpar->size();
166 for (; i < end; ++i) {
167 startpar->CopyIntoMinibuffer(*current_view->buffer(),
169 startpar->Erase(start);
171 buf->InsertFromMinibuffer(buf->size());
175 // more than one paragraph
176 (*endpar)->BreakParagraphConservative(current_view->buffer()->params,
178 *endpar = (*endpar)->next();
181 startpar->BreakParagraphConservative(current_view->buffer()->params,
184 // store the selection
185 buf = startpar->next();
188 (*endpar)->previous()->next(0);
191 startpar->next(*endpar);
193 (*endpar)->previous(startpar);
195 // the cut selection should begin with standard layout
198 // paste the paragraphs again, if possible
200 startpar->next()->StripLeadingSpaces(textclass);
201 if (startpar->HasSameLayout(startpar->next()) ||
202 !startpar->next()->size()) {
203 startpar->PasteParagraph(current_view->buffer()->params);
204 (*endpar) = startpar; // this because endpar gets deleted here!
213 bool CutAndPaste::copySelection(LyXParagraph * startpar, LyXParagraph * endpar,
214 int start, int end, char tc)
216 if (!startpar || (start > startpar->Last()))
224 (startpar->ParFromPos(start) ==
225 (endpar)->ParFromPos(end))) {
226 // only within one paragraph
227 buf = new LyXParagraph;
228 LyXParagraph::size_type i = start;
229 if (end > startpar->Last())
230 end = startpar->Last();
231 for (; i < end; ++i) {
232 startpar->CopyIntoMinibuffer(*current_view->buffer(),
234 buf->InsertFromMinibuffer(buf->Last());
237 // copy more than one paragraph
238 // clone the paragraphs within the selection
239 LyXParagraph * tmppar = startpar->ParFromPos(start);
240 buf = tmppar->Clone();
241 LyXParagraph * tmppar2 = buf;
243 while (tmppar != endpar->ParFromPos(end)
245 tmppar = tmppar->next_;
246 tmppar2->next(tmppar->Clone());
247 tmppar2->next_->previous(tmppar2);
248 tmppar2 = tmppar2->next_;
252 // care about footnotes
253 if (buf->footnoteflag) {
256 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
257 tmppar = tmppar->next_;
261 // the buf paragraph is too big
262 LyXParagraph::size_type tmpi2 = startpar->PositionInParFromPos(start);
263 for (; tmpi2; --tmpi2)
266 // now tmppar 2 is too big, delete all after end
267 tmpi2 = endpar->PositionInParFromPos(end);
268 while (tmppar2->size() > tmpi2) {
269 tmppar2->Erase(tmppar2->size() - 1);
275 bool CutAndPaste::copySelection(LyXParagraph * startpar, LyXParagraph * endpar,
276 int start, int end, char tc)
278 if (!startpar || (start > startpar->size()))
286 startpar == endpar) {
287 // only within one paragraph
288 buf = new LyXParagraph;
289 LyXParagraph::size_type i = start;
290 if (end > startpar->size())
291 end = startpar->size();
292 for (; i < end; ++i) {
293 startpar->CopyIntoMinibuffer(*current_view->buffer(), i);
294 buf->InsertFromMinibuffer(buf->size());
297 // copy more than one paragraph
298 // clone the paragraphs within the selection
299 LyXParagraph * tmppar = startpar;
300 buf = tmppar->Clone();
301 LyXParagraph * tmppar2 = buf;
303 while (tmppar != endpar
305 tmppar = tmppar->next();
306 tmppar2->next(tmppar->Clone());
307 tmppar2->next()->previous(tmppar2);
308 tmppar2 = tmppar2->next();
312 // the buf paragraph is too big
313 LyXParagraph::size_type tmpi2 = start;
314 for (; tmpi2; --tmpi2)
317 // now tmppar 2 is too big, delete all after end
319 while (tmppar2->size() > tmpi2) {
320 tmppar2->Erase(tmppar2->size() - 1);
329 bool CutAndPaste::pasteSelection(LyXParagraph ** par, LyXParagraph ** endpar,
332 if (!checkPastePossible(*par))
335 if (pos > (*par)->Last())
336 pos = (*par)->Last();
338 LyXParagraph * tmpbuf;
339 LyXParagraph * tmppar = *par;
342 // There are two cases: cutbuffer only one paragraph or many
344 // only within a paragraph
345 tmpbuf = buf->Clone();
346 // Some provisions should be done here for checking
347 // if we are inserting at the beginning of a
348 // paragraph. If there are a space at the beginning
349 // of the text to insert and we are inserting at
350 // the beginning of the paragraph the space should
352 while (buf->size()) {
353 // This is an attempt to fix the
354 // "never insert a space at the
355 // beginning of a paragraph" problem.
356 if (!tmppos && buf->IsLineSeparator(0)) {
359 buf->CutIntoMinibuffer(current_view->buffer()->params, 0);
361 if (tmppar->InsertFromMinibuffer(tmppos))
367 *endpar = tmppar->next();
372 // make a copy of the simple cut_buffer
374 LyXParagraph * simple_cut_clone = tmpbuf->Clone();
375 LyXParagraph * tmpbuf2 = simple_cut_clone;
376 if ((*par)->footnoteflag) {
377 tmpbuf->footnoteflag = (*par)->footnoteflag;
378 tmpbuf->footnotekind = (*par)->footnotekind;
380 while (tmpbuf->next_) {
381 tmpbuf = tmpbuf->next_;
382 tmpbuf2->next(tmpbuf->Clone());
383 tmpbuf2->next_->previous(tmpbuf2);
384 tmpbuf2 = tmpbuf2->next_;
385 if ((*par)->footnoteflag){
386 tmpbuf->footnoteflag = (*par)->footnoteflag;
387 tmpbuf->footnotekind = (*par)->footnotekind;
391 // make sure there is no class difference
392 SwitchLayoutsBetweenClasses(textclass, tc, buf);
394 // make the buf exactly the same layout than
395 // the cursor paragraph
396 buf->MakeSameLayout(*par);
398 // find the end of the buffer
399 LyXParagraph * lastbuffer = buf;
400 while (lastbuffer->next())
401 lastbuffer = lastbuffer->next();
403 bool paste_the_end = false;
405 // open the paragraph for inserting the buf
407 if (((*par)->Last() > pos) || !(*par)->next()) {
408 (*par)->BreakParagraphConservative(current_view->buffer()->params,
410 paste_the_end = true;
412 // set the end for redoing later
413 *endpar = (*par)->ParFromPos(pos)->next_->next();
416 lastbuffer->ParFromPos(lastbuffer->Last())->next(
417 (*par)->ParFromPos(pos)->next_);
418 (*par)->ParFromPos(pos)->next()->previous(
419 lastbuffer->ParFromPos(lastbuffer->Last()));
421 (*par)->ParFromPos(pos)->next(buf);
422 buf->previous((*par)->ParFromPos(pos));
424 if ((*par)->ParFromPos(pos)->next() == lastbuffer)
427 (*par)->ParFromPos(pos)->PasteParagraph(current_view->buffer()->params);
428 // store the new cursor position
430 pos = lastbuffer->Last();
431 // maybe some pasting
432 if (lastbuffer->next() && paste_the_end) {
433 if (lastbuffer->next()->HasSameLayout(lastbuffer)) {
434 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph(current_view->buffer()->params);
435 } else if (!lastbuffer->next()->Last()) {
436 lastbuffer->next()->MakeSameLayout(lastbuffer);
437 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph(current_view->buffer()->params);
438 } else if (!lastbuffer->Last()) {
439 lastbuffer->MakeSameLayout(lastbuffer->next_);
440 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph(current_view->buffer()->params);
442 lastbuffer->next()->StripLeadingSpaces(tc);
444 // restore the simple cut buffer
445 buf = simple_cut_clone;
450 bool CutAndPaste::pasteSelection(LyXParagraph ** par, LyXParagraph ** endpar,
453 if (!checkPastePossible(*par))
456 if (pos > (*par)->size())
457 pos = (*par)->size();
459 LyXParagraph * tmpbuf;
460 LyXParagraph * tmppar = *par;
463 // There are two cases: cutbuffer only one paragraph or many
465 // only within a paragraph
466 tmpbuf = buf->Clone();
467 // Some provisions should be done here for checking
468 // if we are inserting at the beginning of a
469 // paragraph. If there are a space at the beginning
470 // of the text to insert and we are inserting at
471 // the beginning of the paragraph the space should
473 while (buf->size()) {
474 // This is an attempt to fix the
475 // "never insert a space at the
476 // beginning of a paragraph" problem.
477 if (!tmppos && buf->IsLineSeparator(0)) {
480 buf->CutIntoMinibuffer(current_view->buffer()->params, 0);
482 if (tmppar->InsertFromMinibuffer(tmppos))
488 *endpar = tmppar->next();
493 // make a copy of the simple cut_buffer
495 LyXParagraph * simple_cut_clone = tmpbuf->Clone();
496 LyXParagraph * tmpbuf2 = simple_cut_clone;
497 while (tmpbuf->next()) {
498 tmpbuf = tmpbuf->next();
499 tmpbuf2->next(tmpbuf->Clone());
500 tmpbuf2->next()->previous(tmpbuf2);
501 tmpbuf2 = tmpbuf2->next();
504 // make sure there is no class difference
505 SwitchLayoutsBetweenClasses(textclass, tc, buf);
507 // make the buf exactly the same layout than
508 // the cursor paragraph
509 buf->MakeSameLayout(*par);
511 // find the end of the buffer
512 LyXParagraph * lastbuffer = buf;
513 while (lastbuffer->next())
514 lastbuffer = lastbuffer->next();
516 bool paste_the_end = false;
518 // open the paragraph for inserting the buf
520 if (((*par)->size() > pos) || !(*par)->next()) {
521 (*par)->BreakParagraphConservative(current_view->buffer()->params,
523 paste_the_end = true;
525 // set the end for redoing later
526 *endpar = (*par)->next()->next();
529 lastbuffer->next((*par)->next());
530 (*par)->next()->previous(lastbuffer);
535 if ((*par)->next() == lastbuffer)
538 (*par)->PasteParagraph(current_view->buffer()->params);
539 // store the new cursor position
541 pos = lastbuffer->size();
542 // maybe some pasting
543 if (lastbuffer->next() && paste_the_end) {
544 if (lastbuffer->next()->HasSameLayout(lastbuffer)) {
545 lastbuffer->PasteParagraph(current_view->buffer()->params);
546 } else if (!lastbuffer->next()->size()) {
547 lastbuffer->next()->MakeSameLayout(lastbuffer);
548 lastbuffer->PasteParagraph(current_view->buffer()->params);
549 } else if (!lastbuffer->size()) {
550 lastbuffer->MakeSameLayout(lastbuffer->next());
551 lastbuffer->PasteParagraph(current_view->buffer()->params);
553 lastbuffer->next()->StripLeadingSpaces(tc);
555 // restore the simple cut buffer
556 buf = simple_cut_clone;
564 int CutAndPaste::nrOfParagraphs()
570 LyXParagraph * tmppar = buf;
572 while(tmppar->next_) {
574 tmppar = tmppar->next_;
577 while(tmppar->next()) {
579 tmppar = tmppar->next();
586 int CutAndPaste::SwitchLayoutsBetweenClasses(LyXTextClassList::size_type c1,
587 LyXTextClassList::size_type c2,
591 if (!par || c1 == c2)
594 par = par->FirstPhysicalPar();
597 string name = textclasslist.NameOfLayout(c1, par->layout);
599 pair<bool, LyXTextClass::LayoutList::size_type> pp =
600 textclasslist.NumberOfLayout(c2, name);
603 } else { // layout not found
604 // use default layout "Standard" (0)
609 if (name != textclasslist.NameOfLayout(c2, par->layout)) {
611 string s = _("Layout had to be changed from\n")
613 + textclasslist.NameOfLayout(c2, par->layout)
614 + _("\nbecause of class conversion from\n")
615 + textclasslist.NameOfClass(c1) + _(" to ")
616 + textclasslist.NameOfClass(c2);
617 InsetError * new_inset = new InsetError(s);
618 par->InsertInset(0, new_inset);
631 bool CutAndPaste::checkPastePossible(LyXParagraph * par)
633 bool CutAndPaste::checkPastePossible(LyXParagraph *)
636 if (!buf) return false;
639 // be carefull with footnotes in footnotes
640 if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
641 // check whether the cut_buffer includes a footnote
642 LyXParagraph * tmppar = buf;
643 while (tmppar && tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
644 tmppar = tmppar->next_;
647 WriteAlert(_("Impossible operation"),
648 _("Can't paste float into float!"),