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