1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995-2001 The LyX Team.
8 * ====================================================== */
13 #pragma implementation
16 #include "CutAndPaste.h"
18 #include "BufferView.h"
20 #include "paragraph.h"
21 #include "ParagraphParameters.h"
23 #include "lyxcursor.h"
25 #include "iterators.h"
26 #include "lyxtextclasslist.h"
27 #include "undo_funcs.h"
28 #include "paragraph_funcs.h"
30 #include "insets/inseterror.h"
32 #include "BoostFormat.h"
36 using lyx::textclass_type;
38 extern BufferView * current_view;
40 // Jürgen, note that this means that you cannot currently have a list
41 // of selections cut/copied. So IMHO later we should have a
42 // list/vector/deque that we could store
43 // struct selection_item {
45 // LyXTextClassList::size_type textclass;
47 // in and some method of choosing beween them (based on the first few chars
48 // in the selection probably.) This would be a nice feature and quite
49 // easy to implement. (Lgb)
51 // Sure but I just cleaned up this code for now with the same functionality
52 // as before. I also want to add a XClipboard function so that we can copy
53 // text from LyX to some other X-application in the form of ASCII or in the
54 // form of LaTeX (or Docbook depending on the document-class!). Think how nice
55 // it could be to select a math-inset do a "Copy to X-Clipboard as LaTeX" and
56 // then do a middle mouse button click in the application you want and have
57 // the whole formula there in LaTeX-Code. (Jug)
62 textclass_type textclass = 0;
64 // for now here this should be in another Cut&Paste Class!
65 // Jürgen, I moved this out of CutAndPaste since it does not operate on any
66 // member of the CutAndPaste class and in addition it was private.
67 // Perhaps it even should take a parameter? (Lgb)
86 bool CutAndPaste::cutSelection(Paragraph * startpar, Paragraph ** endpar,
87 int start, int & end, char tc, bool doclear,
90 if (!startpar || (start > startpar->size()))
98 if (!(*endpar) || startpar == (*endpar)) {
99 // only within one paragraph
102 buf->layout(startpar->layout());
105 if (end > startpar->size())
106 end = startpar->size();
107 for (; i < end; ++i) {
109 startpar->copyIntoMinibuffer(*current_view->buffer(),
111 startpar->erase(start);
113 buf->insertFromMinibuffer(buf->size());
117 // more than one paragraph
118 breakParagraphConservative(current_view->buffer()->params,
121 *endpar = (*endpar)->next();
124 breakParagraphConservative(current_view->buffer()->params,
128 // store the selection
130 buf = startpar->next();
133 startpar->next()->previous(0);
135 (*endpar)->previous()->next(0);
138 startpar->next(*endpar);
140 (*endpar)->previous(startpar);
142 // the cut selection should begin with standard layout
144 buf->params().clear();
146 buf->layout(current_view->buffer()->params.getLyXTextClass().defaultLayout());
149 // paste the paragraphs again, if possible
151 startpar->next()->stripLeadingSpaces();
152 if (startpar->hasSameLayout(startpar->next()) ||
153 startpar->next()->empty()) {
154 mergeParagraph(current_view->buffer()->params, startpar);
155 (*endpar) = startpar; // this because endpar gets deleted here!
157 // this paragraph's are of noone's owner!
168 bool CutAndPaste::copySelection(Paragraph * startpar, Paragraph * endpar,
169 int start, int end, char tc)
171 if (!startpar || (start > startpar->size()))
178 if (!endpar || startpar == endpar) {
179 // only within one paragraph
181 buf->layout(startpar->layout());
183 if (end > startpar->size())
184 end = startpar->size();
185 for (; i < end; ++i) {
186 startpar->copyIntoMinibuffer(*current_view->buffer(), i);
187 buf->insertFromMinibuffer(buf->size());
190 // copy more than one paragraph
191 // clone the paragraphs within the selection
192 Paragraph * tmppar = startpar;
193 buf = new Paragraph(*tmppar, false);
194 Paragraph * tmppar2 = buf;
196 while (tmppar != endpar
198 tmppar = tmppar->next();
199 tmppar2->next(new Paragraph(*tmppar, false));
200 tmppar2->next()->previous(tmppar2);
201 tmppar2 = tmppar2->next();
205 // the buf paragraph is too big
206 pos_type tmpi2 = start;
207 for (; tmpi2; --tmpi2)
210 // now tmppar 2 is too big, delete all after end
212 while (tmppar2->size() > tmpi2) {
213 tmppar2->erase(tmppar2->size() - 1);
215 // this paragraph's are of noone's owner!
218 tmppar->setInsetOwner(0);
219 tmppar = tmppar->next();
226 bool CutAndPaste::pasteSelection(Paragraph ** par, Paragraph ** endpar,
229 if (!checkPastePossible(*par))
232 if (pos > (*par)->size())
233 pos = (*par)->size();
236 // Paragraph * tmpbuf;
237 Paragraph * tmppar = *par;
240 // There are two cases: cutbuffer only one paragraph or many
242 // only within a paragraph
243 Paragraph * tmpbuf = new Paragraph(*buf, false);
245 // Some provisions should be done here for checking
246 // if we are inserting at the beginning of a
247 // paragraph. If there are a space at the beginning
248 // of the text to insert and we are inserting at
249 // the beginning of the paragraph the space should
251 while (buf->size()) {
252 // This is an attempt to fix the
253 // "never insert a space at the
254 // beginning of a paragraph" problem.
255 if (!tmppos && buf->isLineSeparator(0)) {
258 buf->cutIntoMinibuffer(current_view->buffer()->params, 0);
260 if (tmppar->insertFromMinibuffer(tmppos))
266 *endpar = tmppar->next();
273 // make a copy of the simple cut_buffer
274 Paragraph * tmpbuf = buf;
275 Paragraph * simple_cut_clone = new Paragraph(*tmpbuf, false);
276 Paragraph * tmpbuf2 = simple_cut_clone;
278 while (tmpbuf->next()) {
279 tmpbuf = tmpbuf->next();
280 tmpbuf2->next(new Paragraph(*tmpbuf, false));
281 tmpbuf2->next()->previous(tmpbuf2);
282 tmpbuf2 = tmpbuf2->next();
285 // now remove all out of the buffer which is NOT allowed in the
286 // new environment and set also another font if that is required
288 int depth_delta = (*par)->params().depth() - tmpbuf->params().depth();
289 // temporary set *par as previous of tmpbuf as we might have to realize
291 tmpbuf->previous(*par);
293 // make sure there is no class difference
294 SwitchLayoutsBetweenClasses(textclass, tc, tmpbuf,
295 current_view->buffer()->params);
297 Paragraph::depth_type max_depth = (*par)->getMaxDepthAfter();
300 // if we have a negative jump so that the depth would go below
301 // 0 depth then we have to redo the delta to this new max depth
302 // level so that subsequent paragraphs are aligned correctly to
303 // this paragraph at level 0.
304 if ((static_cast<int>(tmpbuf->params().depth()) + depth_delta) < 0)
306 // set the right depth so that we are not too deep or shallow.
307 tmpbuf->params().depth(tmpbuf->params().depth() + depth_delta);
308 if (tmpbuf->params().depth() > max_depth)
309 tmpbuf->params().depth(max_depth);
310 // only set this from the 2nd on as the 2nd depends for maxDepth
312 if (tmpbuf->previous() != (*par))
313 max_depth = tmpbuf->getMaxDepthAfter();
314 // set the inset owner of this paragraph
315 tmpbuf->setInsetOwner((*par)->inInset());
316 for(pos_type i = 0; i < tmpbuf->size(); ++i) {
317 if (tmpbuf->getChar(i) == Paragraph::META_INSET) {
318 if (!(*par)->insetAllowed(tmpbuf->getInset(i)->lyxCode()))
323 LyXFont f1 = tmpbuf->getFont(current_view->buffer()->params,i);
325 if (!(*par)->checkInsertChar(f1)) {
327 } else if (f1 != f2) {
328 tmpbuf->setFont(i, f1);
332 tmpbuf = tmpbuf->next();
337 // make the buf exactly the same layout than
338 // the cursor paragraph
339 buf->makeSameLayout(*par);
341 // find the end of the buffer
342 Paragraph * lastbuffer = buf;
343 while (lastbuffer->next())
344 lastbuffer = lastbuffer->next();
346 bool paste_the_end = false;
348 // open the paragraph for inserting the buf
350 if (((*par)->size() > pos) || !(*par)->next()) {
351 breakParagraphConservative(
352 current_view->buffer()->params,
355 paste_the_end = true;
357 // set the end for redoing later
358 *endpar = (*par)->next()->next();
361 lastbuffer->next((*par)->next());
362 (*par)->next()->previous(lastbuffer);
367 if ((*par)->next() == lastbuffer)
370 mergeParagraph(current_view->buffer()->params, *par);
371 // store the new cursor position
373 pos = lastbuffer->size();
374 // maybe some pasting
375 if (lastbuffer->next() && paste_the_end) {
376 if (lastbuffer->next()->hasSameLayout(lastbuffer)) {
377 mergeParagraph(current_view->buffer()->params, lastbuffer);
378 } else if (lastbuffer->next()->empty()) {
379 lastbuffer->next()->makeSameLayout(lastbuffer);
380 mergeParagraph(current_view->buffer()->params, lastbuffer);
381 } else if (lastbuffer->empty()) {
382 lastbuffer->makeSameLayout(lastbuffer->next());
383 mergeParagraph(current_view->buffer()->params, lastbuffer);
385 lastbuffer->next()->stripLeadingSpaces();
387 // restore the simple cut buffer
388 buf = simple_cut_clone;
395 int CutAndPaste::nrOfParagraphs()
401 Paragraph * tmppar = buf;
402 while (tmppar->next()) {
404 tmppar = tmppar->next();
410 int CutAndPaste::SwitchLayoutsBetweenClasses(textclass_type c1,
413 BufferParams const & /*bparams*/)
416 if (!par || c1 == c2)
419 LyXTextClass const & tclass1 = textclasslist[c1];
420 LyXTextClass const & tclass2 = textclasslist[c2];
421 ParIterator end = ParIterator();
422 for (ParIterator it = ParIterator(par); it != end; ++it) {
424 string const name = par->layout()->name();
425 bool hasLayout = tclass2.hasLayout(name);
428 par->layout(tclass2[name]);
430 par->layout(tclass2.defaultLayout());
432 if (!hasLayout && name != tclass1.defaultLayoutName()) {
435 boost::format fmt(_("Layout had to be changed from\n"
437 "because of class conversion from\n"
440 % par->layout()->name()
444 string const s = fmt.str();
446 string const s = _("Layout had to be changed from\n")
448 + par->layout()->name()
449 + _("\nbecause of class conversion from\n")
450 + tclass1.name() + _(" to ")
454 InsetError * new_inset = new InsetError(s);
455 LyXText * txt = current_view->getLyXText();
456 LyXCursor cur = txt->cursor;
457 txt->setCursorIntern(current_view, par, 0);
458 txt->insertInset(current_view, new_inset);
459 txt->fullRebreak(current_view);
460 txt->setCursorIntern(current_view, cur.par(), cur.pos());
468 bool CutAndPaste::checkPastePossible(Paragraph *)
470 if (!buf) return false;