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