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