]> git.lyx.org Git - lyx.git/blob - src/CutAndPaste.C
renaming, fix citation (still no search though)
[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 #include "gettext.h"
20
21 #ifdef __GNUG__
22 #pragma implementation
23 #endif
24
25 using std::pair;
26
27 extern BufferView * current_view;
28
29 // Jürgen, note that this means that you cannot currently have a list
30 // of selections cut/copied. So IMHO later we should have a
31 // list/vector/deque that we could store
32 // struct selection_item {
33 //       LyXParagraph * buf;
34 //       LyXTextClassList::size_type textclass;
35 // };
36 // in and some method of choosing beween them (based on the first few chars
37 // in the selection probably.) This would be a nice feature and quite
38 // easy to implement. (Lgb)
39 //
40 // Sure but I just cleaned up this code for now with the same functionality
41 // as before. I also want to add a XClipboard function so that we can copy
42 // text from LyX to some other X-application in the form of ASCII or in the
43 // form of LaTeX (or Docbook depending on the document-class!). Think how nice
44 // it could be to select a math-inset do a "Copy to X-Clipboard as LaTeX" and
45 // then do a middle mouse button click in the application you want and have
46 // the whole formula there in LaTeX-Code. (Jug)
47
48 namespace {
49
50 LyXParagraph * buf = 0;
51 LyXTextClassList::size_type textclass = 0;
52
53 // for now here this should be in another Cut&Paste Class!
54 // Jürgen, I moved this out of CutAndPaste since it does not operate on any
55 // member of the CutAndPaste class and in addition it was private.
56 // Perhaps it even should take a parameter? (Lgb)
57 void DeleteBuffer()
58 {
59     if (!buf)
60                 return;
61         
62     LyXParagraph * tmppar;
63         
64     while (buf) {
65                 tmppar =  buf;
66 #ifndef NEW_INSETS
67                 buf = buf->next_;
68 #else
69                 buf = buf->next();
70 #endif
71                 delete tmppar;
72     }
73     buf = 0;
74 }
75
76 } // namespace anon
77
78 bool CutAndPaste::cutSelection(LyXParagraph * startpar, LyXParagraph ** endpar,
79                                int start, int & end, char tc, bool doclear)
80 {
81 #ifndef NEW_INSETS
82     if (!startpar || (start > startpar->Last()))
83                 return false;
84 #else
85     if (!startpar || (start > startpar->size()))
86                 return false;
87 #endif
88
89     DeleteBuffer();
90
91     textclass = tc;
92
93     if (!(*endpar) ||
94 #ifndef NEW_INSETS
95                 (startpar->ParFromPos(start) ==
96                  (*endpar)->ParFromPos(end))
97 #else
98                 (startpar == (*endpar))
99 #endif
100                 ) {
101                 // only within one paragraph
102                 buf = new LyXParagraph;
103                 LyXParagraph::size_type i = start;
104 #ifndef NEW_INSETS
105                 if (end > startpar->Last())
106                         end = startpar->Last();
107 #else
108                 if (end > startpar->size())
109                         end = startpar->size();
110 #endif
111                 for (; i < end; ++i) {
112                         startpar->CopyIntoMinibuffer(*current_view->buffer(), start);
113                         startpar->Erase(start);
114
115 #ifndef NEW_INSETS
116                         buf->InsertFromMinibuffer(buf->Last());
117 #else
118                         buf->InsertFromMinibuffer(buf->size());
119 #endif
120                 }
121                 end = start-1;
122     } else {
123                 // more than one paragraph
124                 (*endpar)->BreakParagraphConservative(current_view->buffer()->params,
125                                                                                           end);
126                 *endpar = (*endpar)->next();
127                 end = 0;
128    
129                 startpar->BreakParagraphConservative(current_view->buffer()->params,
130                                                                                          start);
131
132                 // store the selection
133 #ifndef NEW_INSETS
134                 buf = startpar->ParFromPos(start)->next_;
135 #else
136                 buf = startpar->next();
137 #endif
138                 buf->previous(0);
139                 (*endpar)->previous()->next(0);
140
141                 // cut the selection
142 #ifndef NEW_INSETS
143                 startpar->ParFromPos(start)->next(*endpar);
144         
145                 (*endpar)->previous(startpar->ParFromPos(start));
146 #else
147                 startpar->next(*endpar);
148         
149                 (*endpar)->previous(startpar);
150 #endif
151
152 #ifndef NEW_INSETS
153                 // care about footnotes
154                 if (buf->footnoteflag) {
155                         LyXParagraph * tmppar = buf;
156                         while (tmppar){
157                                 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
158                                 tmppar = tmppar->next_;
159                         }
160                 }
161 #endif
162
163                 // the cut selection should begin with standard layout
164                 buf->Clear(); 
165    
166                 // paste the paragraphs again, if possible
167                 if (doclear)
168                         startpar->next()->StripLeadingSpaces(textclass);
169 #ifndef NEW_INSETS
170                 if (startpar->FirstPhysicalPar()->HasSameLayout(startpar->next()) ||
171                         !startpar->next()->Last())
172 #else
173                 if (startpar->HasSameLayout(startpar->next()) ||
174                         !startpar->next()->size())
175 #endif
176                 {
177 #ifndef NEW_INSETS
178                         startpar->ParFromPos(start)->PasteParagraph(current_view->buffer()->params);
179 #else
180                         startpar->PasteParagraph(current_view->buffer()->params);
181 #endif
182                         (*endpar) = startpar; // this because endpar gets deleted here!
183                 }
184         }
185         return true;
186 }
187
188
189 bool CutAndPaste::copySelection(LyXParagraph * startpar, LyXParagraph * endpar,
190                                 int start, int end, char tc)
191 {
192 #ifndef NEW_INSETS
193     if (!startpar || (start > startpar->Last()))
194                 return false;
195 #else
196     if (!startpar || (start > startpar->size()))
197                 return false;
198 #endif
199
200     DeleteBuffer();
201
202     textclass = tc;
203
204     if (!(endpar) ||
205 #ifndef NEW_INSETS
206                 (startpar->ParFromPos(start) ==
207                  (endpar)->ParFromPos(end))
208 #else
209                 (startpar == endpar)
210 #endif
211                 ) {
212                 // only within one paragraph
213                 buf = new LyXParagraph;
214                 LyXParagraph::size_type i = start;
215 #ifndef NEW_INSETS
216                 if (end > startpar->Last())
217                         end = startpar->Last();
218 #else
219                 if (end > startpar->size())
220                         end = startpar->size();
221 #endif
222                 for (; i < end; ++i) {
223                         startpar->CopyIntoMinibuffer(*current_view->buffer(), i);
224 #ifndef NEW_INSETS
225                         buf->InsertFromMinibuffer(buf->Last());
226 #else
227                         buf->InsertFromMinibuffer(buf->size());
228 #endif
229                 }
230     } else {
231                 // copy more than one paragraph
232                 // clone the paragraphs within the selection
233 #ifndef NEW_INSETS
234                 LyXParagraph * tmppar = startpar->ParFromPos(start);
235 #else
236                 LyXParagraph * tmppar = startpar;
237 #endif
238                 buf = tmppar->Clone();
239                 LyXParagraph * tmppar2 = buf;
240      
241 #ifndef NEW_INSETS
242                 while (tmppar != endpar->ParFromPos(end)
243                            && tmppar->next_) {
244                         tmppar = tmppar->next_;
245                         tmppar2->next(tmppar->Clone());
246                         tmppar2->next_->previous(tmppar2);
247                         tmppar2 = tmppar2->next_;
248                 }
249                 tmppar2->next(0);
250 #else
251                 while (tmppar != endpar
252                            && tmppar->next()) {
253                         tmppar = tmppar->next();
254                         tmppar2->next(tmppar->Clone());
255                         tmppar2->next()->previous(tmppar2);
256                         tmppar2 = tmppar2->next();
257                 }
258                 tmppar2->next(0);
259 #endif
260
261 #ifndef NEW_INSETS
262                 // care about footnotes
263                 if (buf->footnoteflag) {
264                         tmppar = buf;
265                         while (tmppar) {
266                                 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
267                                 tmppar = tmppar->next_;
268                         }
269                 }
270 #endif
271         
272                 // the buf paragraph is too big
273 #ifndef NEW_INSETS
274                 LyXParagraph::size_type tmpi2 = startpar->PositionInParFromPos(start);
275 #else
276                 LyXParagraph::size_type tmpi2 = start;
277 #endif
278                 for (; tmpi2; --tmpi2)
279                         buf->Erase(0);
280         
281                 // now tmppar 2 is too big, delete all after end
282 #ifndef NEW_INSETS
283                 tmpi2 = endpar->PositionInParFromPos(end);
284 #else
285                 tmpi2 = end;
286 #endif
287                 while (tmppar2->size() > tmpi2) {
288                         tmppar2->Erase(tmppar2->size() - 1);
289                 }
290     }
291     return true;
292 }
293
294
295 bool CutAndPaste::pasteSelection(LyXParagraph ** par, LyXParagraph ** endpar,
296                                  int & pos, char tc)
297 {
298     if (!checkPastePossible(*par))
299                 return false;
300
301 #ifndef NEW_INSETS
302     if (pos > (*par)->Last())
303                 pos = (*par)->Last();
304 #else
305     if (pos > (*par)->size())
306                 pos = (*par)->size();
307 #endif
308     
309     LyXParagraph * tmpbuf;
310     LyXParagraph * tmppar = *par;
311     int tmppos = pos;
312
313     // There are two cases: cutbuffer only one paragraph or many
314 #ifndef NEW_INSETS
315     if (!buf->next_) {
316 #else
317                 if (!buf->next()) {
318 #endif
319                         // only within a paragraph
320                         tmpbuf = buf->Clone();
321                         // Some provisions should be done here for checking
322                         // if we are inserting at the beginning of a
323                         // paragraph. If there are a space at the beginning
324                         // of the text to insert and we are inserting at
325                         // the beginning of the paragraph the space should
326                         // be removed.
327                         while (buf->size()) {
328                                 // This is an attempt to fix the
329                                 // "never insert a space at the
330                                 // beginning of a paragraph" problem.
331                                 if (!tmppos && buf->IsLineSeparator(0)) {
332                                         buf->Erase(0);
333                                 } else {
334                                         buf->CutIntoMinibuffer(current_view->buffer()->params, 0);
335                                         buf->Erase(0);
336                                         if (tmppar->InsertFromMinibuffer(tmppos))
337                                                 ++tmppos;
338                                 }
339                         }
340                         delete buf;
341                         buf = tmpbuf;
342                         *endpar = tmppar->next();
343                         pos = tmppos;
344                 } else {
345                         // many paragraphs
346
347                         // make a copy of the simple cut_buffer
348                         tmpbuf = buf;
349                         LyXParagraph * simple_cut_clone = tmpbuf->Clone();
350                         LyXParagraph * tmpbuf2 = simple_cut_clone;
351 #ifndef NEW_INSETS
352                         if ((*par)->footnoteflag) {
353                                 tmpbuf->footnoteflag = (*par)->footnoteflag;
354                                 tmpbuf->footnotekind = (*par)->footnotekind;
355                         }
356                         while (tmpbuf->next_) {
357                                 tmpbuf = tmpbuf->next_;
358                                 tmpbuf2->next(tmpbuf->Clone());
359                                 tmpbuf2->next_->previous(tmpbuf2);
360                                 tmpbuf2 = tmpbuf2->next_;
361                                 if ((*par)->footnoteflag){
362                                         tmpbuf->footnoteflag = (*par)->footnoteflag;
363                                         tmpbuf->footnotekind = (*par)->footnotekind;
364                                 }
365                         }
366 #else
367                         while (tmpbuf->next()) {
368                                 tmpbuf = tmpbuf->next();
369                                 tmpbuf2->next(tmpbuf->Clone());
370                                 tmpbuf2->next()->previous(tmpbuf2);
371                                 tmpbuf2 = tmpbuf2->next();
372                         }
373 #endif
374         
375                         // make sure there is no class difference
376                         SwitchLayoutsBetweenClasses(textclass, tc, buf);
377         
378                         // make the buf exactly the same layout than
379                         // the cursor paragraph
380                         buf->MakeSameLayout(*par);
381         
382                         // find the end of the buffer
383                         LyXParagraph * lastbuffer = buf;
384                         while (lastbuffer->next())
385                                 lastbuffer = lastbuffer->next();
386         
387                         bool paste_the_end = false;
388         
389 #ifndef NEW_INSETS
390                         // open the paragraph for inserting the buf
391                         // if necessary
392                         if (((*par)->Last() > pos) || !(*par)->next()) {
393                                 (*par)->BreakParagraphConservative(current_view->buffer()->params,
394                                                                                                    pos);
395                                 paste_the_end = true;
396                         }
397                         // set the end for redoing later
398                         *endpar = (*par)->ParFromPos(pos)->next_->next();
399         
400                         // paste it!
401                         lastbuffer->ParFromPos(lastbuffer->Last())->next(
402                                 (*par)->ParFromPos(pos)->next_);
403                         (*par)->ParFromPos(pos)->next()->previous(
404                                 lastbuffer->ParFromPos(lastbuffer->Last()));
405         
406                         (*par)->ParFromPos(pos)->next(buf);
407                         buf->previous((*par)->ParFromPos(pos));
408         
409                         if ((*par)->ParFromPos(pos)->next() == lastbuffer)
410                                 lastbuffer = *par;
411         
412                         (*par)->ParFromPos(pos)->PasteParagraph(current_view->buffer()->params);
413                         // store the new cursor position
414                         *par = lastbuffer;
415                         pos = lastbuffer->Last();
416                         // maybe some pasting
417                         if (lastbuffer->next() && paste_the_end) {
418                                 if (lastbuffer->next()->HasSameLayout(lastbuffer)) {
419                                         lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph(current_view->buffer()->params);
420                                 } else if (!lastbuffer->next()->Last()) {
421                                         lastbuffer->next()->MakeSameLayout(lastbuffer);
422                                         lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph(current_view->buffer()->params);
423                                 } else if (!lastbuffer->Last()) {
424                                         lastbuffer->MakeSameLayout(lastbuffer->next_);
425                                         lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph(current_view->buffer()->params);
426                                 } else
427                                         lastbuffer->next()->StripLeadingSpaces(tc);
428                         }
429                         // restore the simple cut buffer
430                         buf = simple_cut_clone;
431                 }
432 #else
433                 // open the paragraph for inserting the buf
434                 // if necessary
435                 if (((*par)->size() > pos) || !(*par)->next()) {
436                         (*par)->BreakParagraphConservative(current_view->buffer()->params,
437                                                                                            pos);
438                         paste_the_end = true;
439                 }
440                 // set the end for redoing later
441                 *endpar = (*par)->next()->next();
442         
443                 // paste it!
444                 lastbuffer->next((*par)->next());
445                 (*par)->next()->previous(lastbuffer);
446         
447                 (*par)->next(buf);
448                 buf->previous(*par);
449         
450                 if ((*par)->next() == lastbuffer)
451                         lastbuffer = *par;
452         
453                 (*par)->PasteParagraph(current_view->buffer()->params);
454                 // store the new cursor position
455                 *par = lastbuffer;
456                 pos = lastbuffer->size();
457                 // maybe some pasting
458                 if (lastbuffer->next() && paste_the_end) {
459                         if (lastbuffer->next()->HasSameLayout(lastbuffer)) {
460                                 lastbuffer->PasteParagraph(current_view->buffer()->params);
461                         } else if (!lastbuffer->next()->size()) {
462                                 lastbuffer->next()->MakeSameLayout(lastbuffer);
463                                 lastbuffer->PasteParagraph(current_view->buffer()->params);
464                         } else if (!lastbuffer->size()) {
465                                 lastbuffer->MakeSameLayout(lastbuffer->next());
466                                 lastbuffer->PasteParagraph(current_view->buffer()->params);
467                         } else
468                                 lastbuffer->next()->StripLeadingSpaces(tc);
469                 }
470                 // restore the simple cut buffer
471                 buf = simple_cut_clone;
472     }
473 #endif
474
475     return true;
476 }
477
478
479 int CutAndPaste::nrOfParagraphs()
480 {
481         if (!buf)
482                 return 0;
483
484         int n = 1;
485         LyXParagraph * tmppar = buf;
486 #ifndef NEW_INSETS
487         while(tmppar->next_) {
488                 ++n;
489                 tmppar = tmppar->next_;
490         }
491 #else
492         while(tmppar->next()) {
493                 ++n;
494                 tmppar = tmppar->next();
495         }
496 #endif
497         return n;
498 }
499
500
501 int CutAndPaste::SwitchLayoutsBetweenClasses(LyXTextClassList::size_type c1,
502                                              LyXTextClassList::size_type c2,
503                                              LyXParagraph * par)
504 {
505     int ret = 0;
506     if (!par || c1 == c2)
507                 return ret;
508 #ifndef NEW_INSETS
509     par = par->FirstPhysicalPar();
510 #endif
511     while (par) {
512                 string name = textclasslist.NameOfLayout(c1, par->layout);
513                 int lay = 0;
514                 pair<bool, LyXTextClass::LayoutList::size_type> pp =
515                         textclasslist.NumberOfLayout(c2, name);
516                 if (pp.first) {
517                         lay = pp.second;
518                 } else { // layout not found
519                         // use default layout "Standard" (0)
520                         lay = 0;
521                 }
522                 par->layout = lay;
523                 
524                 if (name != textclasslist.NameOfLayout(c2, par->layout)) {
525                         ++ret;
526                         string s = _("Layout had to be changed from\n")
527                                 + name + _(" to ")
528                                 + textclasslist.NameOfLayout(c2, par->layout)
529                                 + _("\nbecause of class conversion from\n")
530                                 + textclasslist.NameOfClass(c1) + _(" to ")
531                                 + textclasslist.NameOfClass(c2);
532                         InsetError * new_inset = new InsetError(s);
533                         par->InsertInset(0, new_inset);
534                 }
535 #ifndef NEW_INSETS
536                 par = par->next_;
537 #else
538                 par = par->next();
539 #endif
540     }
541     return ret;
542 }
543
544
545 #ifndef NEW_INSETS
546 bool CutAndPaste::checkPastePossible(LyXParagraph * par)
547 #else
548 bool CutAndPaste::checkPastePossible(LyXParagraph *)
549 #endif
550 {
551     if (!buf) return false;
552
553 #ifndef NEW_INSETS
554     // be carefull with footnotes in footnotes
555     if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
556                 // check whether the cut_buffer includes a footnote
557                 LyXParagraph * tmppar = buf;
558                 while (tmppar && tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
559                         tmppar = tmppar->next_;
560       
561                 if (tmppar) {
562                         WriteAlert(_("Impossible operation"),
563                                            _("Can't paste float into float!"),
564                                            _("Sorry."));
565                         return false;
566                 }
567     }
568 #endif
569     return true;
570 }