]> git.lyx.org Git - lyx.git/blob - src/CutAndPaste.C
work around for bug reported by Mario Morandini
[lyx.git] / src / CutAndPaste.C
1 /* This file is part of
2  * ======================================================
3  *
4  *           LyX, The Document Processor
5  *
6  *           Copyright 1995-2001 The LyX Team.
7  *
8  * ====================================================== */
9
10 #include <config.h>
11
12 #ifdef __GNUG__
13 #pragma implementation
14 #endif
15
16 #include "CutAndPaste.h"
17 //#include "debug.h"
18 #include "BufferView.h"
19 #include "buffer.h"
20 #include "paragraph.h"
21 #include "ParagraphParameters.h"
22 #include "lyxtext.h"
23 #include "lyxcursor.h"
24 #include "gettext.h"
25 #include "iterators.h"
26 #include "lyxtextclasslist.h"
27
28 #include "insets/inseterror.h"
29
30 using std::pair;
31 using lyx::pos_type;
32 using lyx::textclass_type;
33
34 extern BufferView * current_view;
35
36 // Jürgen, note that this means that you cannot currently have a list
37 // of selections cut/copied. So IMHO later we should have a
38 // list/vector/deque that we could store
39 // struct selection_item {
40 //       Paragraph * buf;
41 //       LyXTextClassList::size_type textclass;
42 // };
43 // in and some method of choosing beween them (based on the first few chars
44 // in the selection probably.) This would be a nice feature and quite
45 // easy to implement. (Lgb)
46 //
47 // Sure but I just cleaned up this code for now with the same functionality
48 // as before. I also want to add a XClipboard function so that we can copy
49 // text from LyX to some other X-application in the form of ASCII or in the
50 // form of LaTeX (or Docbook depending on the document-class!). Think how nice
51 // it could be to select a math-inset do a "Copy to X-Clipboard as LaTeX" and
52 // then do a middle mouse button click in the application you want and have
53 // the whole formula there in LaTeX-Code. (Jug)
54
55 namespace {
56
57 Paragraph * buf = 0;
58 textclass_type textclass = 0;
59
60 // for now here this should be in another Cut&Paste Class!
61 // Jürgen, I moved this out of CutAndPaste since it does not operate on any
62 // member of the CutAndPaste class and in addition it was private.
63 // Perhaps it even should take a parameter? (Lgb)
64 void DeleteBuffer()
65 {
66         if (!buf)
67                 return;
68
69         Paragraph * tmppar;
70
71         while (buf) {
72                 tmppar =  buf;
73                 buf = buf->next();
74                 delete tmppar;
75         }
76         buf = 0;
77 }
78
79 } // namespace anon
80
81
82 bool CutAndPaste::cutSelection(Paragraph * startpar, Paragraph ** endpar,
83                                int start, int & end, char tc, bool doclear,
84                                                            bool realcut)
85 {
86         if (!startpar || (start > startpar->size()))
87                 return false;
88
89         if (realcut)
90                 DeleteBuffer();
91
92         textclass = tc;
93
94         if (!(*endpar) || startpar == (*endpar)) {
95                 // only within one paragraph
96                 if (realcut) {
97                         buf = new Paragraph;
98                         buf->layout(startpar->layout());
99                 }
100                 pos_type i = start;
101                 if (end > startpar->size())
102                         end = startpar->size();
103                 for (; i < end; ++i) {
104                         if (realcut)
105                                 startpar->copyIntoMinibuffer(*current_view->buffer(),
106                                                              start);
107                         startpar->erase(start);
108                         if (realcut)
109                                 buf->insertFromMinibuffer(buf->size());
110                 }
111                 end = start - 1;
112         } else {
113                 // more than one paragraph
114                 (*endpar)->breakParagraphConservative(current_view->buffer()->params,
115                                                       end);
116                 *endpar = (*endpar)->next();
117                 end = 0;
118
119                 startpar->breakParagraphConservative(current_view->buffer()->params,
120                                                      start);
121
122                 // store the selection
123                 if (realcut) {
124                         buf = startpar->next();
125                         buf->previous(0);
126                 } else {
127                         startpar->next()->previous(0);
128                 }
129                 (*endpar)->previous()->next(0);
130
131                 // cut the selection
132                 startpar->next(*endpar);
133
134                 (*endpar)->previous(startpar);
135
136                 // the cut selection should begin with standard layout
137                 if (realcut) {
138                         buf->params().clear();
139                         buf->bibkey = 0;
140                         buf->layout(textclasslist[current_view->buffer()->params.textclass].defaultLayoutName());
141                 }
142
143                 // paste the paragraphs again, if possible
144                 if (doclear)
145                         startpar->next()->stripLeadingSpaces(textclass);
146                 if (startpar->hasSameLayout(startpar->next()) ||
147                     !startpar->next()->size()) {
148                         startpar->pasteParagraph(current_view->buffer()->params);
149                         (*endpar) = startpar; // this because endpar gets deleted here!
150                 }
151                 // this paragraph's are of noone's owner!
152                 Paragraph * p = buf;
153                 while (p) {
154                         p->setInsetOwner(0);
155                         p = p->next();
156                 }
157         }
158         return true;
159 }
160
161
162 bool CutAndPaste::copySelection(Paragraph * startpar, Paragraph * endpar,
163                                 int start, int end, char tc)
164 {
165         if (!startpar || (start > startpar->size()))
166                 return false;
167
168         DeleteBuffer();
169
170         textclass = tc;
171
172         if (!endpar || startpar == endpar) {
173                 // only within one paragraph
174                 buf = new Paragraph;
175                 buf->layout(startpar->layout());
176                 pos_type i = start;
177                 if (end > startpar->size())
178                         end = startpar->size();
179                 for (; i < end; ++i) {
180                         startpar->copyIntoMinibuffer(*current_view->buffer(), i);
181                         buf->insertFromMinibuffer(buf->size());
182                 }
183         } else {
184                 // copy more than one paragraph
185                 // clone the paragraphs within the selection
186                 Paragraph * tmppar = startpar;
187                 buf = new Paragraph(*tmppar, false);
188                 Paragraph * tmppar2 = buf;
189
190                 while (tmppar != endpar
191                        && tmppar->next()) {
192                         tmppar = tmppar->next();
193                         tmppar2->next(new Paragraph(*tmppar, false));
194                         tmppar2->next()->previous(tmppar2);
195                         tmppar2 = tmppar2->next();
196                 }
197                 tmppar2->next(0);
198
199                 // the buf paragraph is too big
200                 pos_type tmpi2 = start;
201                 for (; tmpi2; --tmpi2)
202                         buf->erase(0);
203
204                 // now tmppar 2 is too big, delete all after end
205                 tmpi2 = end;
206                 while (tmppar2->size() > tmpi2) {
207                         tmppar2->erase(tmppar2->size() - 1);
208                 }
209                 // this paragraph's are of noone's owner!
210                 tmppar = buf;
211                 while (tmppar) {
212                         tmppar->setInsetOwner(0);
213                         tmppar = tmppar->next();
214                 }
215         }
216         return true;
217 }
218
219
220 bool CutAndPaste::pasteSelection(Paragraph ** par, Paragraph ** endpar,
221                                  int & pos, char tc)
222 {
223         if (!checkPastePossible(*par))
224                 return false;
225
226         if (pos > (*par)->size())
227                 pos = (*par)->size();
228
229 #if 0
230         // Paragraph * tmpbuf;
231         Paragraph * tmppar = *par;
232         int tmppos = pos;
233
234         // There are two cases: cutbuffer only one paragraph or many
235         if (!buf->next()) {
236                 // only within a paragraph
237                 Paragraph * tmpbuf = new Paragraph(*buf, false);
238
239                 // Some provisions should be done here for checking
240                 // if we are inserting at the beginning of a
241                 // paragraph. If there are a space at the beginning
242                 // of the text to insert and we are inserting at
243                 // the beginning of the paragraph the space should
244                 // be removed.
245                 while (buf->size()) {
246                         // This is an attempt to fix the
247                         // "never insert a space at the
248                         // beginning of a paragraph" problem.
249                         if (!tmppos && buf->isLineSeparator(0)) {
250                                 buf->erase(0);
251                         } else {
252                                 buf->cutIntoMinibuffer(current_view->buffer()->params, 0);
253                                 buf->erase(0);
254                                 if (tmppar->insertFromMinibuffer(tmppos))
255                                         ++tmppos;
256                         }
257                 }
258                 delete buf;
259                 buf = tmpbuf;
260                 *endpar = tmppar->next();
261                 pos = tmppos;
262         } else
263 #endif
264         {
265                 // many paragraphs
266
267                 // make a copy of the simple cut_buffer
268                 Paragraph * tmpbuf = buf;
269                 Paragraph * simple_cut_clone = new Paragraph(*tmpbuf, false);
270                 Paragraph * tmpbuf2 = simple_cut_clone;
271
272                 while (tmpbuf->next()) {
273                         tmpbuf = tmpbuf->next();
274                         tmpbuf2->next(new Paragraph(*tmpbuf, false));
275                         tmpbuf2->next()->previous(tmpbuf2);
276                         tmpbuf2 = tmpbuf2->next();
277                 }
278
279                 // now remove all out of the buffer which is NOT allowed in the
280                 // new environment and set also another font if that is required
281                 tmpbuf = buf;
282                 int depth_delta = (*par)->params().depth() - tmpbuf->params().depth();
283                 // temporary set *par as previous of tmpbuf as we might have to realize
284                 // the font.
285                 tmpbuf->previous(*par);
286  
287                 // make sure there is no class difference
288                 SwitchLayoutsBetweenClasses(textclass, tc, tmpbuf,
289                                             current_view->buffer()->params);
290                 
291                 Paragraph::depth_type max_depth = (*par)->getMaxDepthAfter(current_view->buffer());
292  
293                 while(tmpbuf) {
294                         // if we have a negative jump so that the depth would go below
295                         // 0 depth then we have to redo the delta to this new max depth
296                         // level so that subsequent paragraphs are aligned correctly to
297                         // this paragraph at level 0.
298                         if ((static_cast<int>(tmpbuf->params().depth()) + depth_delta) < 0)
299                                 depth_delta = 0;
300                         // set the right depth so that we are not too deep or shallow.
301                         tmpbuf->params().depth(tmpbuf->params().depth() + depth_delta);
302                         if (tmpbuf->params().depth() > max_depth)
303                                 tmpbuf->params().depth(max_depth);
304                         // only set this from the 2nd on as the 2nd depends for maxDepth
305                         // still on *par
306                         if (tmpbuf->previous() != (*par))
307                                 max_depth = tmpbuf->getMaxDepthAfter(current_view->buffer());
308                         // set the inset owner of this paragraph
309                         tmpbuf->setInsetOwner((*par)->inInset());
310                         for(pos_type i = 0; i < tmpbuf->size(); ++i) {
311                                 if (tmpbuf->getChar(i) == Paragraph::META_INSET) {
312                                         if (!(*par)->insetAllowed(tmpbuf->getInset(i)->lyxCode()))
313                                         {
314                                                 tmpbuf->erase(i--);
315                                         }
316                                 } else {
317                                         LyXFont f1 = tmpbuf->getFont(current_view->buffer()->params,i);
318                                         LyXFont f2 = f1;
319                                         if (!(*par)->checkInsertChar(f1)) {
320                                                 tmpbuf->erase(i--);
321                                         } else if (f1 != f2) {
322                                                 tmpbuf->setFont(i, f1);
323                                         }
324                                 }
325                         }
326                         tmpbuf = tmpbuf->next();
327                 }
328                 // now reset it to 0
329                 buf->previous(0);
330
331                 // make the buf exactly the same layout than
332                 // the cursor paragraph
333                 buf->makeSameLayout(*par);
334
335                 // find the end of the buffer
336                 Paragraph * lastbuffer = buf;
337                 while (lastbuffer->next())
338                         lastbuffer = lastbuffer->next();
339
340                 bool paste_the_end = false;
341
342                 // open the paragraph for inserting the buf
343                 // if necessary
344                 if (((*par)->size() > pos) || !(*par)->next()) {
345                         (*par)->breakParagraphConservative(current_view->buffer()->params,
346                                                            pos);
347                         paste_the_end = true;
348                 }
349                 // set the end for redoing later
350                 *endpar = (*par)->next()->next();
351
352                 // paste it!
353                 lastbuffer->next((*par)->next());
354                 (*par)->next()->previous(lastbuffer);
355
356                 (*par)->next(buf);
357                 buf->previous(*par);
358
359                 if ((*par)->next() == lastbuffer)
360                         lastbuffer = *par;
361
362                 (*par)->pasteParagraph(current_view->buffer()->params);
363                 // store the new cursor position
364                 *par = lastbuffer;
365                 pos = lastbuffer->size();
366                 // maybe some pasting
367                 if (lastbuffer->next() && paste_the_end) {
368                         if (lastbuffer->next()->hasSameLayout(lastbuffer)) {
369                                 lastbuffer->pasteParagraph(current_view->buffer()->params);
370                         } else if (!lastbuffer->next()->size()) {
371                                 lastbuffer->next()->makeSameLayout(lastbuffer);
372                                 lastbuffer->pasteParagraph(current_view->buffer()->params);
373                         } else if (!lastbuffer->size()) {
374                                 lastbuffer->makeSameLayout(lastbuffer->next());
375                                 lastbuffer->pasteParagraph(current_view->buffer()->params);
376                         } else
377                                 lastbuffer->next()->stripLeadingSpaces(tc);
378                 }
379                 // restore the simple cut buffer
380                 buf = simple_cut_clone;
381         }
382
383         return true;
384 }
385
386
387 int CutAndPaste::nrOfParagraphs()
388 {
389         if (!buf)
390                 return 0;
391
392         int n = 1;
393         Paragraph * tmppar = buf;
394         while (tmppar->next()) {
395                 ++n;
396                 tmppar = tmppar->next();
397         }
398         return n;
399 }
400
401
402 int CutAndPaste::SwitchLayoutsBetweenClasses(textclass_type c1,
403                                              textclass_type c2,
404                                              Paragraph * par,
405                                              BufferParams const & /*bparams*/)
406 {
407         int ret = 0;
408         if (!par || c1 == c2)
409                 return ret;
410
411         ParIterator end = ParIterator();
412         for (ParIterator it = ParIterator(par); it != end; ++it) {
413                 par = *it;
414                 string const name = par->layout();
415                 LyXTextClass const & tclass = textclasslist[c2];
416
417                 bool hasLayout = tclass.hasLayout(name);
418
419                 string lay = tclass.defaultLayoutName();
420                 if (hasLayout) {
421                         lay = name;
422                 } else {
423                         // not found: use default layout
424                         lay = tclass.defaultLayoutName();
425                 }
426                 par->layout(lay);
427
428                 if (name != par->layout()) {
429                         ++ret;
430                         string const s = _("Layout had to be changed from\n")
431                                 + name + _(" to ")
432                                 + par->layout()
433                                 + _("\nbecause of class conversion from\n")
434                                 + textclasslist[c1].name() + _(" to ")
435                                 + textclasslist[c2].name();
436                         InsetError * new_inset = new InsetError(s);
437                         LyXText * txt = current_view->getLyXText();
438                         LyXCursor cur = txt->cursor;
439                         txt->setCursorIntern(current_view, par, 0);
440                         txt->insertInset(current_view, new_inset);
441                         txt->fullRebreak(current_view);
442                         txt->setCursorIntern(current_view, cur.par(), cur.pos());
443                 }
444         }
445         return ret;
446 }
447
448
449 bool CutAndPaste::checkPastePossible(Paragraph *)
450 {
451         if (!buf) return false;
452
453         return true;
454 }