]> git.lyx.org Git - lyx.git/blob - src/CutAndPaste.C
Added clean CutAndPaste handling with it's own class. Various updates for
[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 "lyxparagraph.h"
14 #include "insets/inseterror.h"
15 #include "lyx_gui_misc.h"
16
17 #ifdef __GNUG__
18 #pragma implementation
19 #endif
20
21 static LyXParagraph * buf = 0;
22
23 CutAndPaste::CutAndPaste()
24 {
25 }
26
27
28 CutAndPaste::~CutAndPaste()
29 {
30 }
31
32 // for now here this should be in another Cut&Paste Class!
33 //
34 void CutAndPaste::DeleteBuffer()
35 {
36     if (!buf)
37         return;
38
39     LyXParagraph * tmppar;
40     
41     while (buf) {
42         tmppar =  buf;
43         buf = buf->next;
44         delete tmppar;
45     }
46     buf = 0;
47 }
48
49 bool CutAndPaste::cutSelection(LyXParagraph *startpar, LyXParagraph **endpar,
50                                int start, int & end, char tc, bool doclear)
51 {
52     if (!startpar || (start > startpar->Last()))
53         return false;
54
55     DeleteBuffer();
56
57     textclass = tc;
58
59     if (!(*endpar) || (startpar->ParFromPos(start) ==
60                        (*endpar)->ParFromPos(end))) {
61         // only within one paragraph
62         buf = new LyXParagraph;
63         LyXParagraph::size_type i = start;
64         if (end > startpar->Last())
65             end = startpar->Last();
66         for (; i < end; ++i) {
67             startpar->CopyIntoMinibuffer(start);
68             /* table stuff -- begin */
69             if (startpar->table && startpar->IsNewline(start)) {
70                 ++start;
71             } else {
72                 /* table stuff -- end */
73                 startpar->Erase(start);
74             }
75             buf->InsertFromMinibuffer(buf->Last());
76         }
77     } else {
78         // more than one paragraph
79         (*endpar)->BreakParagraphConservative(end);
80         *endpar = (*endpar)->Next();
81         end = 0;
82    
83         startpar->BreakParagraphConservative(start);
84
85         // store the selection
86         buf = startpar->ParFromPos(start)->next;
87         buf->previous = 0;
88         (*endpar)->previous->next = 0;
89
90         // cut the selection
91         startpar->ParFromPos(start)->next = (*endpar);
92         
93         (*endpar)->previous = startpar->ParFromPos(start);
94
95         // care about footnotes
96         if (buf->footnoteflag) {
97             LyXParagraph * tmppar = buf;
98             while (tmppar){
99                 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
100                 tmppar = tmppar->next;
101             }
102         }
103
104         // the cut selection should begin with standard layout
105         buf->Clear(); 
106    
107         // paste the paragraphs again, if possible
108         if (doclear)
109             startpar->Next()->ClearParagraph();
110         if (startpar->FirstPhysicalPar()->HasSameLayout(startpar->Next()) || 
111             !startpar->Next()->Last())
112             startpar->ParFromPos(start)->PasteParagraph();
113     }
114     return true;
115 }
116
117 bool CutAndPaste::copySelection(LyXParagraph *startpar, LyXParagraph *endpar,
118                                 int start, int end, char tc)
119 {
120     if (!startpar || (start > startpar->Last()))
121         return false;
122
123     DeleteBuffer();
124
125     textclass = tc;
126
127     if (!(endpar) || (startpar->ParFromPos(start) ==
128                        (endpar)->ParFromPos(end))) {
129         // only within one paragraph
130         buf = new LyXParagraph;
131         LyXParagraph::size_type i = start;
132         if (end > startpar->Last())
133             end = startpar->Last();
134         for (; i < end; ++i) {
135             startpar->CopyIntoMinibuffer(i);
136             buf->InsertFromMinibuffer(buf->Last());
137         }
138     } else {
139         // copy more than one paragraph
140         // clone the paragraphs within the selection
141         LyXParagraph *tmppar = startpar->ParFromPos(start);
142         buf = tmppar->Clone();
143         LyXParagraph *tmppar2 = buf;
144      
145         while (tmppar != endpar->ParFromPos(end)
146                && tmppar->next) {
147             tmppar = tmppar->next;
148             tmppar2->next = tmppar->Clone();
149             tmppar2->next->previous = tmppar2;
150             tmppar2 = tmppar2->next;
151         }
152         tmppar2->next = 0;
153
154         // care about footnotes
155         if (buf->footnoteflag) {
156             tmppar = buf;
157             while (tmppar){
158                 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
159                 tmppar = tmppar->next;
160             }
161         }
162         
163         // the buf paragraph is too big
164         LyXParagraph::size_type tmpi2 = startpar->PositionInParFromPos(start);
165         for (; tmpi2; --tmpi2)
166             buf->Erase(0);
167         
168         // now tmppar 2 is too big, delete all after end
169         
170         tmpi2 = endpar->PositionInParFromPos(end);
171         while (tmppar2->size() > tmpi2) {
172             tmppar2->Erase(tmppar2->size() - 1);
173         }
174     }
175     return true;
176 }
177
178 bool CutAndPaste::pasteSelection(LyXParagraph **par, LyXParagraph **endpar,
179                                  int &pos, char tc)
180 {
181     if (!checkPastePossible(*par, pos))
182         return false;
183
184     if (pos > (*par)->Last())
185         pos = (*par)->Last();
186
187     LyXParagraph * tmpbuf;
188     LyXParagraph * tmppar = *par;
189     int tmppos = pos;
190
191     // There are two cases: cutbuffer only one paragraph or many
192     if (!buf->next) {
193         // only within a paragraph
194         tmpbuf = buf->Clone();
195         /* table stuff -- begin */
196         bool table_too_small = false;
197         if ((*par)->table) {
198             while (buf->size() && !table_too_small) {
199                 if (buf->IsNewline(0)){
200                     while((tmppos < tmppar->Last()) &&
201                           !tmppar->IsNewline(tmppos))
202                         tmppos++;
203                     buf->Erase(0);
204                     if (tmppos < tmppar->Last())
205                         tmppos++;
206                     else
207                         table_too_small = true;
208                 } else {
209                     // This is an attempt to fix the
210                     // "never insert a space at the
211                     // beginning of a paragraph" problem.
212                     if (!tmppos && buf->IsLineSeparator(0)) {
213                         buf->Erase(0);
214                     } else {
215                         buf->CutIntoMinibuffer(0);
216                         buf->Erase(0);
217                         if (tmppar->InsertFromMinibuffer(tmppos))
218                             ++tmppos;
219                     }
220                 }
221             }
222         } else {
223             /* table stuff -- end */
224             // Some provisions should be done here for checking
225             // if we are inserting at the beginning of a
226             // paragraph. If there are a space at the beginning
227             // of the text to insert and we are inserting at
228             // the beginning of the paragraph the space should
229             // be removed.
230             while (buf->size()) {
231                 // This is an attempt to fix the
232                 // "never insert a space at the
233                 // beginning of a paragraph" problem.
234                 if (!tmppos && buf->IsLineSeparator(0)) {
235                     buf->Erase(0);
236                 } else {
237                     buf->CutIntoMinibuffer(0);
238                     buf->Erase(0);
239                     if (tmppar->InsertFromMinibuffer(tmppos))
240                         ++tmppos;
241                 }
242             }
243         }
244         delete buf;
245         buf = tmpbuf;
246         *endpar = tmppar->Next();
247         pos = tmppos;
248     } else {
249         // many paragraphs
250
251         // make a copy of the simple cut_buffer
252         tmpbuf = buf;
253         LyXParagraph * simple_cut_clone = tmpbuf->Clone();
254         LyXParagraph * tmpbuf2 = simple_cut_clone;
255         if ((*par)->footnoteflag){
256             tmpbuf->footnoteflag = (*par)->footnoteflag;
257             tmpbuf->footnotekind = (*par)->footnotekind;
258         }
259         while (tmpbuf->next) {
260             tmpbuf = tmpbuf->next;
261             tmpbuf2->next = tmpbuf->Clone();
262             tmpbuf2->next->previous = tmpbuf2;
263             tmpbuf2 = tmpbuf2->next;
264             if ((*par)->footnoteflag){
265                 tmpbuf->footnoteflag = (*par)->footnoteflag;
266                 tmpbuf->footnotekind = (*par)->footnotekind;
267             }
268         }
269         
270         // make sure there is no class difference
271         SwitchLayoutsBetweenClasses(textclass, tc, buf);
272         
273         // make the buf exactly the same layout than
274         // the cursor paragraph
275         buf->MakeSameLayout(*par);
276         
277         // find the end of the buffer
278         LyXParagraph * lastbuffer = buf;
279         while (lastbuffer->Next())
280             lastbuffer = lastbuffer->Next();
281         
282         bool paste_the_end = false;
283         
284         // open the paragraph for inserting the buf
285         // if necessary
286         if (((*par)->Last() > pos) || !(*par)->Next()) {
287             (*par)->BreakParagraphConservative(pos);
288             paste_the_end = true;
289         }
290         
291         // set the end for redoing later
292         *endpar = (*par)->ParFromPos(pos)->next->Next();
293         
294         // paste it!
295         lastbuffer->ParFromPos(lastbuffer->Last())->next =
296             (*par)->ParFromPos(pos)->next;
297         (*par)->ParFromPos(pos)->next->previous =
298             lastbuffer->ParFromPos(lastbuffer->Last());
299         
300         (*par)->ParFromPos(pos)->next = buf;
301         buf->previous = (*par)->ParFromPos(pos);
302         
303         if ((*par)->ParFromPos(pos)->Next() == lastbuffer)
304             lastbuffer = *par;
305         
306         (*par)->ParFromPos(pos)->PasteParagraph();
307         
308         // store the new cursor position
309         tmppar = lastbuffer;
310         tmppos = lastbuffer->Last();
311         
312         // maybe some pasting
313         if (lastbuffer->Next() && paste_the_end) {
314             if (lastbuffer->Next()->HasSameLayout(lastbuffer)) {
315                 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
316             } else if (!lastbuffer->Next()->Last()) {
317                 lastbuffer->Next()->MakeSameLayout(lastbuffer);
318                 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
319             } else if (!lastbuffer->Last()) {
320                 lastbuffer->MakeSameLayout(lastbuffer->next);
321                 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
322             } else
323                 lastbuffer->Next()->ClearParagraph();
324         }
325         // restore the simple cut buffer
326         buf = simple_cut_clone;
327         pos = tmppos;
328     }
329
330     return true;
331 }
332
333 int CutAndPaste::nrOfParagraphs() const
334 {
335     if (!buf)
336         return 0;
337
338     int n = 1;
339     LyXParagraph *tmppar = buf;
340     while(tmppar->next) {
341         ++n;
342         tmppar = tmppar->next;
343     }
344     return n;
345 }
346
347 int CutAndPaste::SwitchLayoutsBetweenClasses(LyXTextClassList::size_type c1,
348                                              LyXTextClassList::size_type c2,
349                                              LyXParagraph * par)
350 {
351     int ret = 0;
352     if (!par || c1 == c2)
353         return ret;
354     par = par->FirstPhysicalPar();
355     while (par) {
356         string name = textclasslist.NameOfLayout(c1, par->layout);
357         int lay = 0;
358         pair<bool, LyXTextClass::LayoutList::size_type> pp =
359             textclasslist.NumberOfLayout(c2, name);
360         if (pp.first) {
361             lay = pp.second;
362         } else { // layout not found
363             // use default layout "Standard" (0)
364             lay = 0;
365         }
366         par->layout = lay;
367         
368         if (name != textclasslist.NameOfLayout(c2, par->layout)) {
369             ++ret;
370             string s = "Layout had to be changed from\n"
371                 + name + " to "
372                 + textclasslist.NameOfLayout(c2, par->layout)
373                 + "\nbecause of class conversion from\n"
374                 + textclasslist.NameOfClass(c1) + " to "
375                 + textclasslist.NameOfClass(c2);
376             InsetError * new_inset = new InsetError(s);
377             par->InsertChar(0, LyXParagraph::META_INSET);
378             par->InsertInset(0, new_inset);
379         }
380         
381         par = par->next;
382     }
383     return ret;
384 }
385
386 char CutAndPaste::getBufferTextClass()
387 {
388     return textclass;
389 }
390
391 bool CutAndPaste::checkPastePossible(LyXParagraph *par, int)
392 {
393     if (!buf)
394         return false;
395
396     LyXParagraph *tmppar;
397
398     // be carefull with footnotes in footnotes
399     if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
400         // check whether the cut_buffer includes a footnote
401         tmppar = buf;
402         while (tmppar && tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
403             tmppar = tmppar->next;
404       
405         if (tmppar) {
406             WriteAlert(_("Impossible operation"),
407                        _("Can't paste float into float!"),
408                        _("Sorry."));
409             return false;
410         }
411     }
412     /* table stuff -- begin */
413     if (par->table) {
414         if (buf->next) {
415             WriteAlert(_("Impossible operation"),
416                        _("Table cell cannot include more than one paragraph!"),
417                        _("Sorry."));
418             return false;
419         }
420     }
421     /* table stuff -- end */
422     return true;
423 }