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