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