]> git.lyx.org Git - lyx.git/blob - src/CutAndPaste.C
Applied Angus patch to compile on DEC C++ and to avoid name clashes
[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 #ifndef NEW_INSETS
176             startpar->ParFromPos(start)->PasteParagraph(current_view->buffer()->params);
177 #else
178             startpar->PasteParagraph(current_view->buffer()->params);
179 #endif
180             (*endpar) = startpar; // this because endpar gets deleted here!
181         }
182     }
183     return true;
184 }
185
186
187 bool CutAndPaste::copySelection(LyXParagraph * startpar, LyXParagraph * endpar,
188                                 int start, int end, char tc)
189 {
190 #ifndef NEW_INSETS
191     if (!startpar || (start > startpar->Last()))
192         return false;
193 #else
194     if (!startpar || (start > startpar->size()))
195         return false;
196 #endif
197
198     DeleteBuffer();
199
200     textclass = tc;
201
202     if (!(endpar) ||
203 #ifndef NEW_INSETS
204         (startpar->ParFromPos(start) ==
205                        (endpar)->ParFromPos(end))
206 #else
207         (startpar == endpar)
208 #endif
209            ) {
210         // only within one paragraph
211         buf = new LyXParagraph;
212         LyXParagraph::size_type i = start;
213 #ifndef NEW_INSETS
214         if (end > startpar->Last())
215             end = startpar->Last();
216 #else
217         if (end > startpar->size())
218             end = startpar->size();
219 #endif
220         for (; i < end; ++i) {
221             startpar->CopyIntoMinibuffer(*current_view->buffer(), i);
222 #ifndef NEW_INSETS
223             buf->InsertFromMinibuffer(buf->Last());
224 #else
225             buf->InsertFromMinibuffer(buf->size());
226 #endif
227         }
228     } else {
229         // copy more than one paragraph
230         // clone the paragraphs within the selection
231 #ifndef NEW_INSETS
232         LyXParagraph * tmppar = startpar->ParFromPos(start);
233 #else
234         LyXParagraph * tmppar = startpar;
235 #endif
236         buf = tmppar->Clone();
237         LyXParagraph * tmppar2 = buf;
238      
239 #ifndef NEW_INSETS
240         while (tmppar != endpar->ParFromPos(end)
241                && tmppar->next_) {
242                 tmppar = tmppar->next_;
243                 tmppar2->next(tmppar->Clone());
244                 tmppar2->next_->previous(tmppar2);
245                 tmppar2 = tmppar2->next_;
246         }
247         tmppar2->next(0);
248 #else
249         while (tmppar != endpar
250                && tmppar->next()) {
251                 tmppar = tmppar->next();
252                 tmppar2->next(tmppar->Clone());
253                 tmppar2->next()->previous(tmppar2);
254                 tmppar2 = tmppar2->next();
255         }
256         tmppar2->next(0);
257 #endif
258
259 #ifndef NEW_INSETS
260         // care about footnotes
261         if (buf->footnoteflag) {
262             tmppar = buf;
263             while (tmppar) {
264                 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
265                 tmppar = tmppar->next_;
266             }
267         }
268 #endif
269         
270         // the buf paragraph is too big
271 #ifndef NEW_INSETS
272         LyXParagraph::size_type tmpi2 = startpar->PositionInParFromPos(start);
273 #else
274         LyXParagraph::size_type tmpi2 = start;
275 #endif
276         for (; tmpi2; --tmpi2)
277             buf->Erase(0);
278         
279         // now tmppar 2 is too big, delete all after end
280 #ifndef NEW_INSETS
281         tmpi2 = endpar->PositionInParFromPos(end);
282 #else
283         tmpi2 = end;
284 #endif
285         while (tmppar2->size() > tmpi2) {
286             tmppar2->Erase(tmppar2->size() - 1);
287         }
288     }
289     return true;
290 }
291
292
293 bool CutAndPaste::pasteSelection(LyXParagraph ** par, LyXParagraph ** endpar,
294                                  int & pos, char tc)
295 {
296     if (!checkPastePossible(*par))
297         return false;
298
299 #ifndef NEW_INSETS
300     if (pos > (*par)->Last())
301         pos = (*par)->Last();
302 #else
303     if (pos > (*par)->size())
304         pos = (*par)->size();
305 #endif
306     
307     LyXParagraph * tmpbuf;
308     LyXParagraph * tmppar = *par;
309     int tmppos = pos;
310
311     // There are two cases: cutbuffer only one paragraph or many
312 #ifndef NEW_INSETS
313     if (!buf->next_) {
314 #else
315     if (!buf->next()) {
316 #endif
317         // only within a paragraph
318         tmpbuf = buf->Clone();
319         // Some provisions should be done here for checking
320         // if we are inserting at the beginning of a
321         // paragraph. If there are a space at the beginning
322         // of the text to insert and we are inserting at
323         // the beginning of the paragraph the space should
324         // be removed.
325         while (buf->size()) {
326                 // This is an attempt to fix the
327                 // "never insert a space at the
328                 // beginning of a paragraph" problem.
329                 if (!tmppos && buf->IsLineSeparator(0)) {
330                         buf->Erase(0);
331                 } else {
332                         buf->CutIntoMinibuffer(current_view->buffer()->params, 0);
333                         buf->Erase(0);
334                         if (tmppar->InsertFromMinibuffer(tmppos))
335                                 ++tmppos;
336                 }
337         }
338         delete buf;
339         buf = tmpbuf;
340         *endpar = tmppar->next();
341         pos = tmppos;
342     } else {
343         // many paragraphs
344
345         // make a copy of the simple cut_buffer
346         tmpbuf = buf;
347         LyXParagraph * simple_cut_clone = tmpbuf->Clone();
348         LyXParagraph * tmpbuf2 = simple_cut_clone;
349 #ifndef NEW_INSETS
350         if ((*par)->footnoteflag) {
351                 tmpbuf->footnoteflag = (*par)->footnoteflag;
352                 tmpbuf->footnotekind = (*par)->footnotekind;
353         }
354         while (tmpbuf->next_) {
355                 tmpbuf = tmpbuf->next_;
356                 tmpbuf2->next(tmpbuf->Clone());
357                 tmpbuf2->next_->previous(tmpbuf2);
358                 tmpbuf2 = tmpbuf2->next_;
359                 if ((*par)->footnoteflag){
360                         tmpbuf->footnoteflag = (*par)->footnoteflag;
361                         tmpbuf->footnotekind = (*par)->footnotekind;
362                 }
363         }
364 #else
365         while (tmpbuf->next()) {
366                 tmpbuf = tmpbuf->next();
367                 tmpbuf2->next(tmpbuf->Clone());
368                 tmpbuf2->next()->previous(tmpbuf2);
369                 tmpbuf2 = tmpbuf2->next();
370         }
371 #endif
372         
373         // make sure there is no class difference
374         SwitchLayoutsBetweenClasses(textclass, tc, buf);
375         
376         // make the buf exactly the same layout than
377         // the cursor paragraph
378         buf->MakeSameLayout(*par);
379         
380         // find the end of the buffer
381         LyXParagraph * lastbuffer = buf;
382         while (lastbuffer->next())
383             lastbuffer = lastbuffer->next();
384         
385         bool paste_the_end = false;
386         
387 #ifndef NEW_INSETS
388         // open the paragraph for inserting the buf
389         // if necessary
390         if (((*par)->Last() > pos) || !(*par)->next()) {
391             (*par)->BreakParagraphConservative(current_view->buffer()->params,
392                                                pos);
393             paste_the_end = true;
394         }
395         // set the end for redoing later
396         *endpar = (*par)->ParFromPos(pos)->next_->next();
397         
398         // paste it!
399         lastbuffer->ParFromPos(lastbuffer->Last())->next(
400             (*par)->ParFromPos(pos)->next_);
401         (*par)->ParFromPos(pos)->next()->previous(
402             lastbuffer->ParFromPos(lastbuffer->Last()));
403         
404         (*par)->ParFromPos(pos)->next(buf);
405         buf->previous((*par)->ParFromPos(pos));
406         
407         if ((*par)->ParFromPos(pos)->next() == lastbuffer)
408             lastbuffer = *par;
409         
410         (*par)->ParFromPos(pos)->PasteParagraph(current_view->buffer()->params);
411         // store the new cursor position
412         *par = lastbuffer;
413         pos = lastbuffer->Last();
414         // maybe some pasting
415         if (lastbuffer->next() && paste_the_end) {
416             if (lastbuffer->next()->HasSameLayout(lastbuffer)) {
417                 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph(current_view->buffer()->params);
418             } else if (!lastbuffer->next()->Last()) {
419                 lastbuffer->next()->MakeSameLayout(lastbuffer);
420                 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph(current_view->buffer()->params);
421             } else if (!lastbuffer->Last()) {
422                 lastbuffer->MakeSameLayout(lastbuffer->next_);
423                 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph(current_view->buffer()->params);
424             } else
425                 lastbuffer->next()->StripLeadingSpaces(tc);
426         }
427         // restore the simple cut buffer
428         buf = simple_cut_clone;
429     }
430 #else
431         // open the paragraph for inserting the buf
432         // if necessary
433         if (((*par)->size() > pos) || !(*par)->next()) {
434             (*par)->BreakParagraphConservative(current_view->buffer()->params,
435                                                pos);
436             paste_the_end = true;
437         }
438         // set the end for redoing later
439         *endpar = (*par)->next()->next();
440         
441         // paste it!
442         lastbuffer->next((*par)->next());
443         (*par)->next()->previous(lastbuffer);
444         
445         (*par)->next(buf);
446         buf->previous(*par);
447         
448         if ((*par)->next() == lastbuffer)
449             lastbuffer = *par;
450         
451         (*par)->PasteParagraph(current_view->buffer()->params);
452         // store the new cursor position
453         *par = lastbuffer;
454         pos = lastbuffer->size();
455         // maybe some pasting
456         if (lastbuffer->next() && paste_the_end) {
457             if (lastbuffer->next()->HasSameLayout(lastbuffer)) {
458                 lastbuffer->PasteParagraph(current_view->buffer()->params);
459             } else if (!lastbuffer->next()->size()) {
460                 lastbuffer->next()->MakeSameLayout(lastbuffer);
461                 lastbuffer->PasteParagraph(current_view->buffer()->params);
462             } else if (!lastbuffer->size()) {
463                 lastbuffer->MakeSameLayout(lastbuffer->next());
464                 lastbuffer->PasteParagraph(current_view->buffer()->params);
465             } else
466                 lastbuffer->next()->StripLeadingSpaces(tc);
467         }
468         // restore the simple cut buffer
469         buf = simple_cut_clone;
470     }
471 #endif
472
473     return true;
474 }
475
476
477 int CutAndPaste::nrOfParagraphs()
478 {
479         if (!buf) return 0;
480
481         int n = 1;
482         LyXParagraph * tmppar = buf;
483 #ifndef NEW_INSETS
484         while(tmppar->next_) {
485                 ++n;
486                 tmppar = tmppar->next_;
487         }
488 #else
489         while(tmppar->next()) {
490                 ++n;
491                 tmppar = tmppar->next();
492         }
493 #endif
494         return n;
495 }
496
497
498 int CutAndPaste::SwitchLayoutsBetweenClasses(LyXTextClassList::size_type c1,
499                                              LyXTextClassList::size_type c2,
500                                              LyXParagraph * par)
501 {
502     int ret = 0;
503     if (!par || c1 == c2)
504         return ret;
505 #ifndef NEW_INSETS
506     par = par->FirstPhysicalPar();
507 #endif
508     while (par) {
509         string name = textclasslist.NameOfLayout(c1, par->layout);
510         int lay = 0;
511         pair<bool, LyXTextClass::LayoutList::size_type> pp =
512             textclasslist.NumberOfLayout(c2, name);
513         if (pp.first) {
514             lay = pp.second;
515         } else { // layout not found
516             // use default layout "Standard" (0)
517             lay = 0;
518         }
519         par->layout = lay;
520         
521         if (name != textclasslist.NameOfLayout(c2, par->layout)) {
522             ++ret;
523             string s = _("Layout had to be changed from\n")
524                     + name + _(" to ")
525                     + textclasslist.NameOfLayout(c2, par->layout)
526                     + _("\nbecause of class conversion from\n")
527                     + textclasslist.NameOfClass(c1) + _(" to ")
528                     + textclasslist.NameOfClass(c2);
529             InsetError * new_inset = new InsetError(s);
530             par->InsertInset(0, new_inset);
531         }
532 #ifndef NEW_INSETS
533         par = par->next_;
534 #else
535         par = par->next();
536 #endif
537     }
538     return ret;
539 }
540
541
542 bool CutAndPaste::checkPastePossible(LyXParagraph * par)
543 {
544     if (!buf) return false;
545
546 #ifndef NEW_INSETS
547     // be carefull with footnotes in footnotes
548     if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
549         // check whether the cut_buffer includes a footnote
550         LyXParagraph * tmppar = buf;
551         while (tmppar && tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
552             tmppar = tmppar->next_;
553       
554         if (tmppar) {
555             WriteAlert(_("Impossible operation"),
556                        _("Can't paste float into float!"),
557                        _("Sorry."));
558             return false;
559         }
560     }
561 #endif
562     return true;
563 }