]> git.lyx.org Git - lyx.git/blob - src/CutAndPaste.C
fixed two consecutive spaces handling
[lyx.git] / src / CutAndPaste.C
1 /* This file is part of
2  * ======================================================
3  * 
4  *           LyX, The Document Processor
5  *       
6  *           Copyright 1995-2000 The LyX Team.
7  *
8  * ====================================================== */
9
10 #include <config.h>
11
12 #include "CutAndPaste.h"
13 #include "BufferView.h"
14 #include "buffer.h"
15 #include "lyxparagraph.h"
16 #include "insets/inseterror.h"
17 #include "lyx_gui_misc.h"
18 #include "lyxcursor.h"
19 #include "gettext.h"
20
21 #ifdef __GNUG__
22 #pragma implementation
23 #endif
24
25 using std::pair;
26
27 extern BufferView * current_view;
28
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;
35 // };
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)
39 //
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)
47
48 namespace {
49
50 LyXParagraph * buf = 0;
51 LyXTextClassList::size_type textclass = 0;
52
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)
57 void DeleteBuffer()
58 {
59     if (!buf)
60                 return;
61         
62     LyXParagraph * tmppar;
63         
64     while (buf) {
65                 tmppar =  buf;
66 #ifndef NEW_INSETS
67                 buf = buf->next_;
68 #else
69                 buf = buf->next();
70 #endif
71                 delete tmppar;
72     }
73     buf = 0;
74 }
75
76 } // namespace anon
77
78
79 #ifndef NEW_INSETS
80 bool CutAndPaste::cutSelection(LyXParagraph * startpar, LyXParagraph ** endpar,
81                                int start, int & end, char tc, bool doclear)
82 {
83         if (!startpar || (start > startpar->Last()))
84                 return false;
85         
86         DeleteBuffer();
87         
88         textclass = tc;
89         
90         if (!(*endpar) ||
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);
101                         
102                         buf->InsertFromMinibuffer(buf->Last());
103                 }
104                 end = start - 1;
105         } else {
106                 // more than one paragraph
107                 (*endpar)->BreakParagraphConservative(current_view->buffer()->params,
108                                                       end);
109                 *endpar = (*endpar)->next();
110                 end = 0;
111                 
112                 startpar->BreakParagraphConservative(current_view->buffer()->params,
113                                                      start);
114                 
115                 // store the selection
116                 buf = startpar->ParFromPos(start)->next_;
117                 buf->previous(0);
118                 (*endpar)->previous()->next(0);
119                 
120                 // cut the selection
121                 startpar->ParFromPos(start)->next(*endpar);
122                 
123                 (*endpar)->previous(startpar->ParFromPos(start));
124                 
125                 // care about footnotes
126                 if (buf->footnoteflag) {
127                         LyXParagraph * tmppar = buf;
128                         while (tmppar){
129                                 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
130                                 tmppar = tmppar->next_;
131                         }
132                 }
133                 
134                 // the cut selection should begin with standard layout
135                 buf->Clear(); 
136                 
137                 // paste the paragraphs again, if possible
138                 if (doclear)
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!
144                 }
145         }
146         return true;
147 }
148 #else
149 bool CutAndPaste::cutSelection(LyXParagraph * startpar, LyXParagraph ** endpar,
150                                int start, int & end, char tc, bool doclear)
151 {
152         if (!startpar || (start > startpar->size()))
153                 return false;
154         
155         DeleteBuffer();
156         
157         textclass = tc;
158         
159         if (!(*endpar) ||
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(),
168                                                      start);
169                         startpar->Erase(start);
170                         
171                         buf->InsertFromMinibuffer(buf->size());
172                 }
173                 end = start - 1;
174         } else {
175                 // more than one paragraph
176                 (*endpar)->BreakParagraphConservative(current_view->buffer()->params,
177                                                       end);
178                 *endpar = (*endpar)->next();
179                 end = 0;
180    
181                 startpar->BreakParagraphConservative(current_view->buffer()->params,
182                                                      start);
183                 
184                 // store the selection
185                 buf = startpar->next();
186                 
187                 buf->previous(0);
188                 (*endpar)->previous()->next(0);
189                 
190                 // cut the selection
191                 startpar->next(*endpar);
192                 
193                 (*endpar)->previous(startpar);
194                 
195                 // the cut selection should begin with standard layout
196                 buf->Clear(); 
197                 
198                 // paste the paragraphs again, if possible
199                 if (doclear)
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!
205                 }
206         }
207         return true;
208 }
209 #endif
210
211
212 #ifndef NEW_INSETS
213 bool CutAndPaste::copySelection(LyXParagraph * startpar, LyXParagraph * endpar,
214                                 int start, int end, char tc)
215 {
216         if (!startpar || (start > startpar->Last()))
217                 return false;
218         
219         DeleteBuffer();
220         
221         textclass = tc;
222         
223         if (!(endpar) ||
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(),
233                                                      i);
234                         buf->InsertFromMinibuffer(buf->Last());
235                 }
236         } else {
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;
242                 
243                 while (tmppar != endpar->ParFromPos(end)
244                        && tmppar->next_) {
245                         tmppar = tmppar->next_;
246                         tmppar2->next(tmppar->Clone());
247                         tmppar2->next_->previous(tmppar2);
248                         tmppar2 = tmppar2->next_;
249                 }
250                 tmppar2->next(0);
251                 
252                 // care about footnotes
253                 if (buf->footnoteflag) {
254                         tmppar = buf;
255                         while (tmppar) {
256                                 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
257                                 tmppar = tmppar->next_;
258                         }
259                 }
260                 
261                 // the buf paragraph is too big
262                 LyXParagraph::size_type tmpi2 = startpar->PositionInParFromPos(start);
263                 for (; tmpi2; --tmpi2)
264                         buf->Erase(0);
265                 
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);
270                 }
271         }
272         return true;
273 }
274 #else
275 bool CutAndPaste::copySelection(LyXParagraph * startpar, LyXParagraph * endpar,
276                                 int start, int end, char tc)
277 {
278         if (!startpar || (start > startpar->size()))
279                 return false;
280         
281         DeleteBuffer();
282         
283         textclass = tc;
284         
285         if (!endpar ||
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());
295                 }
296         } else {
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;
302                 
303                 while (tmppar != endpar
304                        && tmppar->next()) {
305                         tmppar = tmppar->next();
306                         tmppar2->next(tmppar->Clone());
307                         tmppar2->next()->previous(tmppar2);
308                         tmppar2 = tmppar2->next();
309                 }
310                 tmppar2->next(0);
311                 
312                 // the buf paragraph is too big
313                 LyXParagraph::size_type tmpi2 = start;
314                 for (; tmpi2; --tmpi2)
315                         buf->Erase(0);
316                 
317                 // now tmppar 2 is too big, delete all after end
318                 tmpi2 = end;
319                 while (tmppar2->size() > tmpi2) {
320                         tmppar2->Erase(tmppar2->size() - 1);
321                 }
322         }
323         return true;
324 }
325 #endif
326
327
328 #ifndef NEW_INSETS
329 bool CutAndPaste::pasteSelection(LyXParagraph ** par, LyXParagraph ** endpar,
330                                  int & pos, char tc)
331 {
332         if (!checkPastePossible(*par))
333                 return false;
334         
335         if (pos > (*par)->Last())
336                 pos = (*par)->Last();
337         
338         LyXParagraph * tmpbuf;
339         LyXParagraph * tmppar = *par;
340         int tmppos = pos;
341         
342         // There are two cases: cutbuffer only one paragraph or many
343         if (!buf->next_) {
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
351                 // be removed.
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)) {
357                                 buf->Erase(0);
358                         } else {
359                                 buf->CutIntoMinibuffer(current_view->buffer()->params, 0);
360                                 buf->Erase(0);
361                                 if (tmppar->InsertFromMinibuffer(tmppos))
362                                         ++tmppos;
363                         }
364                 }
365                 delete buf;
366                 buf = tmpbuf;
367                 *endpar = tmppar->next();
368                 pos = tmppos;
369         } else {
370                 // many paragraphs
371                 
372                 // make a copy of the simple cut_buffer
373                 tmpbuf = buf;
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;
379                 }
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;
388                         }
389                 }
390                 
391                 // make sure there is no class difference
392                 SwitchLayoutsBetweenClasses(textclass, tc, buf);
393                 
394                 // make the buf exactly the same layout than
395                 // the cursor paragraph
396                 buf->MakeSameLayout(*par);
397                 
398                 // find the end of the buffer
399                 LyXParagraph * lastbuffer = buf;
400                 while (lastbuffer->next())
401                         lastbuffer = lastbuffer->next();
402                 
403                 bool paste_the_end = false;
404                 
405                 // open the paragraph for inserting the buf
406                 // if necessary
407                 if (((*par)->Last() > pos) || !(*par)->next()) {
408                         (*par)->BreakParagraphConservative(current_view->buffer()->params,
409                                                            pos);
410                         paste_the_end = true;
411                 }
412                 // set the end for redoing later
413                 *endpar = (*par)->ParFromPos(pos)->next_->next();
414                 
415                 // paste it!
416                 lastbuffer->ParFromPos(lastbuffer->Last())->next(
417                         (*par)->ParFromPos(pos)->next_);
418                 (*par)->ParFromPos(pos)->next()->previous(
419                         lastbuffer->ParFromPos(lastbuffer->Last()));
420                 
421                 (*par)->ParFromPos(pos)->next(buf);
422                 buf->previous((*par)->ParFromPos(pos));
423                 
424                 if ((*par)->ParFromPos(pos)->next() == lastbuffer)
425                         lastbuffer = *par;
426                 
427                 (*par)->ParFromPos(pos)->PasteParagraph(current_view->buffer()->params);
428                 // store the new cursor position
429                 *par = lastbuffer;
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);
441                         } else
442                                 lastbuffer->next()->StripLeadingSpaces(tc);
443                 }
444                 // restore the simple cut buffer
445                 buf = simple_cut_clone;
446         }
447         return true;
448 }
449 #else
450 bool CutAndPaste::pasteSelection(LyXParagraph ** par, LyXParagraph ** endpar,
451                                  int & pos, char tc)
452 {
453         if (!checkPastePossible(*par))
454                 return false;
455         
456         if (pos > (*par)->size())
457                 pos = (*par)->size();
458         
459         LyXParagraph * tmpbuf;
460         LyXParagraph * tmppar = *par;
461         int tmppos = pos;
462         
463         // There are two cases: cutbuffer only one paragraph or many
464         if (!buf->next()) {
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
472                 // be removed.
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)) {
478                                 buf->Erase(0);
479                         } else {
480                                 buf->CutIntoMinibuffer(current_view->buffer()->params, 0);
481                                 buf->Erase(0);
482                                 if (tmppar->InsertFromMinibuffer(tmppos))
483                                         ++tmppos;
484                         }
485                 }
486                 delete buf;
487                 buf = tmpbuf;
488                 *endpar = tmppar->next();
489                 pos = tmppos;
490         } else {
491                 // many paragraphs
492                 
493                 // make a copy of the simple cut_buffer
494                 tmpbuf = buf;
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();
502                 }
503                 
504                 // make sure there is no class difference
505                 SwitchLayoutsBetweenClasses(textclass, tc, buf);
506                 
507                 // make the buf exactly the same layout than
508                 // the cursor paragraph
509                 buf->MakeSameLayout(*par);
510                 
511                 // find the end of the buffer
512                 LyXParagraph * lastbuffer = buf;
513                 while (lastbuffer->next())
514                         lastbuffer = lastbuffer->next();
515                 
516                 bool paste_the_end = false;
517                 
518                 // open the paragraph for inserting the buf
519                 // if necessary
520                 if (((*par)->size() > pos) || !(*par)->next()) {
521                         (*par)->BreakParagraphConservative(current_view->buffer()->params,
522                                                            pos);
523                         paste_the_end = true;
524                 }
525                 // set the end for redoing later
526                 *endpar = (*par)->next()->next();
527                 
528                 // paste it!
529                 lastbuffer->next((*par)->next());
530                 (*par)->next()->previous(lastbuffer);
531                 
532                 (*par)->next(buf);
533                 buf->previous(*par);
534                 
535                 if ((*par)->next() == lastbuffer)
536                         lastbuffer = *par;
537                 
538                 (*par)->PasteParagraph(current_view->buffer()->params);
539                 // store the new cursor position
540                 *par = lastbuffer;
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);
552                         } else
553                                 lastbuffer->next()->StripLeadingSpaces(tc);
554                 }
555                 // restore the simple cut buffer
556                 buf = simple_cut_clone;
557         }
558         
559         return true;
560 }
561 #endif
562
563
564 int CutAndPaste::nrOfParagraphs()
565 {
566         if (!buf)
567                 return 0;
568
569         int n = 1;
570         LyXParagraph * tmppar = buf;
571 #ifndef NEW_INSETS
572         while(tmppar->next_) {
573                 ++n;
574                 tmppar = tmppar->next_;
575         }
576 #else
577         while(tmppar->next()) {
578                 ++n;
579                 tmppar = tmppar->next();
580         }
581 #endif
582         return n;
583 }
584
585
586 int CutAndPaste::SwitchLayoutsBetweenClasses(LyXTextClassList::size_type c1,
587                                              LyXTextClassList::size_type c2,
588                                              LyXParagraph * par)
589 {
590     int ret = 0;
591     if (!par || c1 == c2)
592                 return ret;
593 #ifndef NEW_INSETS
594     par = par->FirstPhysicalPar();
595 #endif
596     while (par) {
597                 string name = textclasslist.NameOfLayout(c1, par->layout);
598                 int lay = 0;
599                 pair<bool, LyXTextClass::LayoutList::size_type> pp =
600                         textclasslist.NumberOfLayout(c2, name);
601                 if (pp.first) {
602                         lay = pp.second;
603                 } else { // layout not found
604                         // use default layout "Standard" (0)
605                         lay = 0;
606                 }
607                 par->layout = lay;
608                 
609                 if (name != textclasslist.NameOfLayout(c2, par->layout)) {
610                         ++ret;
611                         string s = _("Layout had to be changed from\n")
612                                 + name + _(" to ")
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);
619                 }
620 #ifndef NEW_INSETS
621                 par = par->next_;
622 #else
623                 par = par->next();
624 #endif
625     }
626     return ret;
627 }
628
629
630 #ifndef NEW_INSETS
631 bool CutAndPaste::checkPastePossible(LyXParagraph * par)
632 #else
633 bool CutAndPaste::checkPastePossible(LyXParagraph *)
634 #endif
635 {
636     if (!buf) return false;
637
638 #ifndef NEW_INSETS
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_;
645       
646                 if (tmppar) {
647                         WriteAlert(_("Impossible operation"),
648                                            _("Can't paste float into float!"),
649                                            _("Sorry."));
650                         return false;
651                 }
652     }
653 #endif
654     return true;
655 }