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