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