]> git.lyx.org Git - lyx.git/blob - src/CutAndPaste.C
aabb0a8b023bc9d124df80403478951184dd11f1
[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
20 #ifdef __GNUG__
21 #pragma implementation
22 #endif
23
24 using std::pair;
25
26 extern BufferView * current_view;
27
28 // Jürgen, note that this means that you cannot currently have a list
29 // of selections cut/copied. So IMHO later we should have a
30 // list/vector/deque that we could store
31 // struct selection_item {
32 //       LyXParagraph * buf;
33 //       LyXTextClassList::size_type textclass;
34 // };
35 // in and some method of choosing beween them (based on the first few chars
36 // in the selection probably.) This would be a nice feature and quite
37 // easy to implement. (Lgb)
38 //
39 // Sure but I just cleaned up this code for now with the same functionality
40 // as before. I also want to add a XClipboard function so that we can copy
41 // text from LyX to some other X-application in the form of ASCII or in the
42 // form of LaTeX (or Docbook depending on the document-class!). Think how nice
43 // it could be to select a math-inset do a "Copy to X-Clipboard as LaTeX" and
44 // then do a middle mouse button click in the application you want and have
45 // the whole formula there in LaTeX-Code. (Jug)
46
47 namespace {
48
49 LyXParagraph * buf = 0;
50 LyXTextClassList::size_type textclass = 0;
51
52 // for now here this should be in another Cut&Paste Class!
53 // Jürgen, I moved this out of CutAndPaste since it does not operate on any
54 // member of the CutAndPaste class and in addition it was private.
55 // Perhaps it even should take a parameter? (Lgb)
56 void DeleteBuffer()
57 {
58     if (!buf)
59                 return;
60         
61     LyXParagraph * tmppar;
62         
63     while (buf) {
64                 tmppar =  buf;
65 #ifndef NEW_INSETS
66                 buf = buf->next_;
67 #else
68                 buf = buf->next();
69 #endif
70                 delete tmppar;
71     }
72     buf = 0;
73 }
74
75 } // namespace anon
76
77 bool CutAndPaste::cutSelection(LyXParagraph * startpar, LyXParagraph ** endpar,
78                                int start, int & end, char tc, bool doclear)
79 {
80 #ifndef NEW_INSETS
81     if (!startpar || (start > startpar->Last()))
82                 return false;
83 #else
84     if (!startpar || (start > startpar->size()))
85                 return false;
86 #endif
87
88     DeleteBuffer();
89
90     textclass = tc;
91
92     if (!(*endpar) ||
93 #ifndef NEW_INSETS
94                 (startpar->ParFromPos(start) ==
95                  (*endpar)->ParFromPos(end))
96 #else
97                 (startpar == (*endpar))
98 #endif
99                 ) {
100                 // only within one paragraph
101                 buf = new LyXParagraph;
102                 LyXParagraph::size_type i = start;
103 #ifndef NEW_INSETS
104                 if (end > startpar->Last())
105                         end = startpar->Last();
106 #else
107                 if (end > startpar->size())
108                         end = startpar->size();
109 #endif
110                 for (; i < end; ++i) {
111                         startpar->CopyIntoMinibuffer(*current_view->buffer(), start);
112                         startpar->Erase(start);
113
114 #ifndef NEW_INSETS
115                         buf->InsertFromMinibuffer(buf->Last());
116 #else
117                         buf->InsertFromMinibuffer(buf->size());
118 #endif
119                 }
120                 end = start-1;
121     } else {
122                 // more than one paragraph
123                 (*endpar)->BreakParagraphConservative(current_view->buffer()->params,
124                                                                                           end);
125                 *endpar = (*endpar)->next();
126                 end = 0;
127    
128                 startpar->BreakParagraphConservative(current_view->buffer()->params,
129                                                                                          start);
130
131                 // store the selection
132 #ifndef NEW_INSETS
133                 buf = startpar->ParFromPos(start)->next_;
134 #else
135                 buf = startpar->next();
136 #endif
137                 buf->previous(0);
138                 (*endpar)->previous()->next(0);
139
140                 // cut the selection
141 #ifndef NEW_INSETS
142                 startpar->ParFromPos(start)->next(*endpar);
143         
144                 (*endpar)->previous(startpar->ParFromPos(start));
145 #else
146                 startpar->next(*endpar);
147         
148                 (*endpar)->previous(startpar);
149 #endif
150
151 #ifndef NEW_INSETS
152                 // care about footnotes
153                 if (buf->footnoteflag) {
154                         LyXParagraph * tmppar = buf;
155                         while (tmppar){
156                                 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
157                                 tmppar = tmppar->next_;
158                         }
159                 }
160 #endif
161
162                 // the cut selection should begin with standard layout
163                 buf->Clear(); 
164    
165                 // paste the paragraphs again, if possible
166                 if (doclear)
167                         startpar->next()->StripLeadingSpaces(textclass);
168 #ifndef NEW_INSETS
169                 if (startpar->FirstPhysicalPar()->HasSameLayout(startpar->next()) ||
170                         !startpar->next()->Last())
171 #else
172                 if (startpar->HasSameLayout(startpar->next()) ||
173                         !startpar->next()->size())
174 #endif
175                 {
176 #ifndef NEW_INSETS
177                         startpar->ParFromPos(start)->PasteParagraph(current_view->buffer()->params);
178 #else
179                         startpar->PasteParagraph(current_view->buffer()->params);
180 #endif
181                         (*endpar) = startpar; // this because endpar gets deleted here!
182                 }
183         }
184         return true;
185 }
186
187
188 bool CutAndPaste::copySelection(LyXParagraph * startpar, LyXParagraph * endpar,
189                                 int start, int end, char tc)
190 {
191 #ifndef NEW_INSETS
192     if (!startpar || (start > startpar->Last()))
193                 return false;
194 #else
195     if (!startpar || (start > startpar->size()))
196                 return false;
197 #endif
198
199     DeleteBuffer();
200
201     textclass = tc;
202
203     if (!(endpar) ||
204 #ifndef NEW_INSETS
205                 (startpar->ParFromPos(start) ==
206                  (endpar)->ParFromPos(end))
207 #else
208                 (startpar == endpar)
209 #endif
210                 ) {
211                 // only within one paragraph
212                 buf = new LyXParagraph;
213                 LyXParagraph::size_type i = start;
214 #ifndef NEW_INSETS
215                 if (end > startpar->Last())
216                         end = startpar->Last();
217 #else
218                 if (end > startpar->size())
219                         end = startpar->size();
220 #endif
221                 for (; i < end; ++i) {
222                         startpar->CopyIntoMinibuffer(*current_view->buffer(), i);
223 #ifndef NEW_INSETS
224                         buf->InsertFromMinibuffer(buf->Last());
225 #else
226                         buf->InsertFromMinibuffer(buf->size());
227 #endif
228                 }
229     } else {
230                 // copy more than one paragraph
231                 // clone the paragraphs within the selection
232 #ifndef NEW_INSETS
233                 LyXParagraph * tmppar = startpar->ParFromPos(start);
234 #else
235                 LyXParagraph * tmppar = startpar;
236 #endif
237                 buf = tmppar->Clone();
238                 LyXParagraph * tmppar2 = buf;
239      
240 #ifndef NEW_INSETS
241                 while (tmppar != endpar->ParFromPos(end)
242                            && tmppar->next_) {
243                         tmppar = tmppar->next_;
244                         tmppar2->next(tmppar->Clone());
245                         tmppar2->next_->previous(tmppar2);
246                         tmppar2 = tmppar2->next_;
247                 }
248                 tmppar2->next(0);
249 #else
250                 while (tmppar != endpar
251                            && tmppar->next()) {
252                         tmppar = tmppar->next();
253                         tmppar2->next(tmppar->Clone());
254                         tmppar2->next()->previous(tmppar2);
255                         tmppar2 = tmppar2->next();
256                 }
257                 tmppar2->next(0);
258 #endif
259
260 #ifndef NEW_INSETS
261                 // care about footnotes
262                 if (buf->footnoteflag) {
263                         tmppar = buf;
264                         while (tmppar) {
265                                 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
266                                 tmppar = tmppar->next_;
267                         }
268                 }
269 #endif
270         
271                 // the buf paragraph is too big
272 #ifndef NEW_INSETS
273                 LyXParagraph::size_type tmpi2 = startpar->PositionInParFromPos(start);
274 #else
275                 LyXParagraph::size_type tmpi2 = start;
276 #endif
277                 for (; tmpi2; --tmpi2)
278                         buf->Erase(0);
279         
280                 // now tmppar 2 is too big, delete all after end
281 #ifndef NEW_INSETS
282                 tmpi2 = endpar->PositionInParFromPos(end);
283 #else
284                 tmpi2 = end;
285 #endif
286                 while (tmppar2->size() > tmpi2) {
287                         tmppar2->Erase(tmppar2->size() - 1);
288                 }
289     }
290     return true;
291 }
292
293
294 bool CutAndPaste::pasteSelection(LyXParagraph ** par, LyXParagraph ** endpar,
295                                  int & pos, char tc)
296 {
297     if (!checkPastePossible(*par))
298                 return false;
299
300 #ifndef NEW_INSETS
301     if (pos > (*par)->Last())
302                 pos = (*par)->Last();
303 #else
304     if (pos > (*par)->size())
305                 pos = (*par)->size();
306 #endif
307     
308     LyXParagraph * tmpbuf;
309     LyXParagraph * tmppar = *par;
310     int tmppos = pos;
311
312     // There are two cases: cutbuffer only one paragraph or many
313 #ifndef NEW_INSETS
314     if (!buf->next_) {
315 #else
316                 if (!buf->next()) {
317 #endif
318                         // only within a paragraph
319                         tmpbuf = buf->Clone();
320                         // Some provisions should be done here for checking
321                         // if we are inserting at the beginning of a
322                         // paragraph. If there are a space at the beginning
323                         // of the text to insert and we are inserting at
324                         // the beginning of the paragraph the space should
325                         // be removed.
326                         while (buf->size()) {
327                                 // This is an attempt to fix the
328                                 // "never insert a space at the
329                                 // beginning of a paragraph" problem.
330                                 if (!tmppos && buf->IsLineSeparator(0)) {
331                                         buf->Erase(0);
332                                 } else {
333                                         buf->CutIntoMinibuffer(current_view->buffer()->params, 0);
334                                         buf->Erase(0);
335                                         if (tmppar->InsertFromMinibuffer(tmppos))
336                                                 ++tmppos;
337                                 }
338                         }
339                         delete buf;
340                         buf = tmpbuf;
341                         *endpar = tmppar->next();
342                         pos = tmppos;
343                 } else {
344                         // many paragraphs
345
346                         // make a copy of the simple cut_buffer
347                         tmpbuf = buf;
348                         LyXParagraph * simple_cut_clone = tmpbuf->Clone();
349                         LyXParagraph * tmpbuf2 = simple_cut_clone;
350 #ifndef NEW_INSETS
351                         if ((*par)->footnoteflag) {
352                                 tmpbuf->footnoteflag = (*par)->footnoteflag;
353                                 tmpbuf->footnotekind = (*par)->footnotekind;
354                         }
355                         while (tmpbuf->next_) {
356                                 tmpbuf = tmpbuf->next_;
357                                 tmpbuf2->next(tmpbuf->Clone());
358                                 tmpbuf2->next_->previous(tmpbuf2);
359                                 tmpbuf2 = tmpbuf2->next_;
360                                 if ((*par)->footnoteflag){
361                                         tmpbuf->footnoteflag = (*par)->footnoteflag;
362                                         tmpbuf->footnotekind = (*par)->footnotekind;
363                                 }
364                         }
365 #else
366                         while (tmpbuf->next()) {
367                                 tmpbuf = tmpbuf->next();
368                                 tmpbuf2->next(tmpbuf->Clone());
369                                 tmpbuf2->next()->previous(tmpbuf2);
370                                 tmpbuf2 = tmpbuf2->next();
371                         }
372 #endif
373         
374                         // make sure there is no class difference
375                         SwitchLayoutsBetweenClasses(textclass, tc, buf);
376         
377                         // make the buf exactly the same layout than
378                         // the cursor paragraph
379                         buf->MakeSameLayout(*par);
380         
381                         // find the end of the buffer
382                         LyXParagraph * lastbuffer = buf;
383                         while (lastbuffer->next())
384                                 lastbuffer = lastbuffer->next();
385         
386                         bool paste_the_end = false;
387         
388 #ifndef NEW_INSETS
389                         // open the paragraph for inserting the buf
390                         // if necessary
391                         if (((*par)->Last() > pos) || !(*par)->next()) {
392                                 (*par)->BreakParagraphConservative(current_view->buffer()->params,
393                                                                                                    pos);
394                                 paste_the_end = true;
395                         }
396                         // set the end for redoing later
397                         *endpar = (*par)->ParFromPos(pos)->next_->next();
398         
399                         // paste it!
400                         lastbuffer->ParFromPos(lastbuffer->Last())->next(
401                                 (*par)->ParFromPos(pos)->next_);
402                         (*par)->ParFromPos(pos)->next()->previous(
403                                 lastbuffer->ParFromPos(lastbuffer->Last()));
404         
405                         (*par)->ParFromPos(pos)->next(buf);
406                         buf->previous((*par)->ParFromPos(pos));
407         
408                         if ((*par)->ParFromPos(pos)->next() == lastbuffer)
409                                 lastbuffer = *par;
410         
411                         (*par)->ParFromPos(pos)->PasteParagraph(current_view->buffer()->params);
412                         // store the new cursor position
413                         *par = lastbuffer;
414                         pos = lastbuffer->Last();
415                         // maybe some pasting
416                         if (lastbuffer->next() && paste_the_end) {
417                                 if (lastbuffer->next()->HasSameLayout(lastbuffer)) {
418                                         lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph(current_view->buffer()->params);
419                                 } else if (!lastbuffer->next()->Last()) {
420                                         lastbuffer->next()->MakeSameLayout(lastbuffer);
421                                         lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph(current_view->buffer()->params);
422                                 } else if (!lastbuffer->Last()) {
423                                         lastbuffer->MakeSameLayout(lastbuffer->next_);
424                                         lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph(current_view->buffer()->params);
425                                 } else
426                                         lastbuffer->next()->StripLeadingSpaces(tc);
427                         }
428                         // restore the simple cut buffer
429                         buf = simple_cut_clone;
430                 }
431 #else
432                 // open the paragraph for inserting the buf
433                 // if necessary
434                 if (((*par)->size() > pos) || !(*par)->next()) {
435                         (*par)->BreakParagraphConservative(current_view->buffer()->params,
436                                                                                            pos);
437                         paste_the_end = true;
438                 }
439                 // set the end for redoing later
440                 *endpar = (*par)->next()->next();
441         
442                 // paste it!
443                 lastbuffer->next((*par)->next());
444                 (*par)->next()->previous(lastbuffer);
445         
446                 (*par)->next(buf);
447                 buf->previous(*par);
448         
449                 if ((*par)->next() == lastbuffer)
450                         lastbuffer = *par;
451         
452                 (*par)->PasteParagraph(current_view->buffer()->params);
453                 // store the new cursor position
454                 *par = lastbuffer;
455                 pos = lastbuffer->size();
456                 // maybe some pasting
457                 if (lastbuffer->next() && paste_the_end) {
458                         if (lastbuffer->next()->HasSameLayout(lastbuffer)) {
459                                 lastbuffer->PasteParagraph(current_view->buffer()->params);
460                         } else if (!lastbuffer->next()->size()) {
461                                 lastbuffer->next()->MakeSameLayout(lastbuffer);
462                                 lastbuffer->PasteParagraph(current_view->buffer()->params);
463                         } else if (!lastbuffer->size()) {
464                                 lastbuffer->MakeSameLayout(lastbuffer->next());
465                                 lastbuffer->PasteParagraph(current_view->buffer()->params);
466                         } else
467                                 lastbuffer->next()->StripLeadingSpaces(tc);
468                 }
469                 // restore the simple cut buffer
470                 buf = simple_cut_clone;
471     }
472 #endif
473
474     return true;
475 }
476
477
478 int CutAndPaste::nrOfParagraphs()
479 {
480         if (!buf)
481                 return 0;
482
483         int n = 1;
484         LyXParagraph * tmppar = buf;
485 #ifndef NEW_INSETS
486         while(tmppar->next_) {
487                 ++n;
488                 tmppar = tmppar->next_;
489         }
490 #else
491         while(tmppar->next()) {
492                 ++n;
493                 tmppar = tmppar->next();
494         }
495 #endif
496         return n;
497 }
498
499
500 int CutAndPaste::SwitchLayoutsBetweenClasses(LyXTextClassList::size_type c1,
501                                              LyXTextClassList::size_type c2,
502                                              LyXParagraph * par)
503 {
504     int ret = 0;
505     if (!par || c1 == c2)
506                 return ret;
507 #ifndef NEW_INSETS
508     par = par->FirstPhysicalPar();
509 #endif
510     while (par) {
511                 string name = textclasslist.NameOfLayout(c1, par->layout);
512                 int lay = 0;
513                 pair<bool, LyXTextClass::LayoutList::size_type> pp =
514                         textclasslist.NumberOfLayout(c2, name);
515                 if (pp.first) {
516                         lay = pp.second;
517                 } else { // layout not found
518                         // use default layout "Standard" (0)
519                         lay = 0;
520                 }
521                 par->layout = lay;
522                 
523                 if (name != textclasslist.NameOfLayout(c2, par->layout)) {
524                         ++ret;
525                         string s = _("Layout had to be changed from\n")
526                                 + name + _(" to ")
527                                 + textclasslist.NameOfLayout(c2, par->layout)
528                                 + _("\nbecause of class conversion from\n")
529                                 + textclasslist.NameOfClass(c1) + _(" to ")
530                                 + textclasslist.NameOfClass(c2);
531                         InsetError * new_inset = new InsetError(s);
532                         par->InsertInset(0, new_inset);
533                 }
534 #ifndef NEW_INSETS
535                 par = par->next_;
536 #else
537                 par = par->next();
538 #endif
539     }
540     return ret;
541 }
542
543
544 #ifndef NEW_INSETS
545 bool CutAndPaste::checkPastePossible(LyXParagraph * par)
546 #else
547 bool CutAndPaste::checkPastePossible(LyXParagraph *)
548 #endif
549 {
550     if (!buf) return false;
551
552 #ifndef NEW_INSETS
553     // be carefull with footnotes in footnotes
554     if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
555                 // check whether the cut_buffer includes a footnote
556                 LyXParagraph * tmppar = buf;
557                 while (tmppar && tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
558                         tmppar = tmppar->next_;
559       
560                 if (tmppar) {
561                         WriteAlert(_("Impossible operation"),
562                                            _("Can't paste float into float!"),
563                                            _("Sorry."));
564                         return false;
565                 }
566     }
567 #endif
568     return true;
569 }