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