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