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