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