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