1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995-2001 The LyX Team.
8 * ====================================================== */
12 #include "CutAndPaste.h"
13 #include "BufferView.h"
15 #include "paragraph.h"
16 #include "ParagraphParameters.h"
18 #include "lyxcursor.h"
20 #include "iterators.h"
21 #include "lyxtextclasslist.h"
22 #include "undo_funcs.h"
23 #include "paragraph_funcs.h"
26 #include "insets/inseterror.h"
28 #include "support/BoostFormat.h"
33 using lyx::textclass_type;
35 extern BufferView * current_view;
37 // Jürgen, note that this means that you cannot currently have a list
38 // of selections cut/copied. So IMHO later we should have a
39 // list/vector/deque that we could store
40 // struct selection_item {
42 // LyXTextClassList::size_type textclass;
44 // in and some method of choosing beween them (based on the first few chars
45 // in the selection probably.) This would be a nice feature and quite
46 // easy to implement. (Lgb)
48 // Sure but I just cleaned up this code for now with the same functionality
49 // as before. I also want to add a XClipboard function so that we can copy
50 // text from LyX to some other X-application in the form of ASCII or in the
51 // form of LaTeX (or Docbook depending on the document-class!). Think how nice
52 // it could be to select a math-inset do a "Copy to X-Clipboard as LaTeX" and
53 // then do a middle mouse button click in the application you want and have
54 // the whole formula there in LaTeX-Code. (Jug)
59 ParagraphList paragraphs;
60 textclass_type textclass = 0;
64 typedef std::pair<ParagraphList::iterator, int> pitPosPair;
66 pitPosPair CutAndPaste::cutSelection(ParagraphList & pars,
67 ParagraphList::iterator startpit,
68 ParagraphList::iterator endpit,
69 int startpos, int endpos,
70 textclass_type tc, bool doclear)
72 copySelection(&*startpit, &*endpit, startpos, endpos, tc);
73 return eraseSelection(pars, startpit, endpit, startpos,
78 pitPosPair CutAndPaste::eraseSelection(ParagraphList & pars,
79 ParagraphList::iterator startpit,
80 ParagraphList::iterator endpit,
81 int startpos, int endpos, bool doclear)
83 if (startpit == pars.end() || (startpos > startpit->size()))
84 return pitPosPair(endpit, endpos);
86 if (endpit == pars.end() || startpit == endpit) {
87 endpos -= startpit->erase(startpos, endpos);
88 return pitPosPair(endpit, endpos);
91 // clear end/begin fragments of the first/last par in selection
92 bool all_erased = true;
94 startpit->erase(startpos, startpit->size());
95 if (startpit->size() != startpos)
98 endpos -= endpit->erase(0, endpos);
102 // Loop through the deleted pars if any, erasing as needed
104 ParagraphList::iterator pit = boost::next(startpit);
106 while (pit != endpit && pit != pars.end()) {
107 ParagraphList::iterator const next = boost::next(pit);
108 // "erase" the contents of the par
109 pit->erase(0, pit->size());
111 // remove the par if it's now empty
118 #if 0 // FIXME: why for cut but not copy ?
119 // the cut selection should begin with standard layout
121 buf->params().clear();
123 buf->layout(textclasslist[buffer->params.textclass].defaultLayoutName());
127 if (boost::next(startpit) == pars.end())
128 return pitPosPair(endpit, endpos);
131 boost::next(startpit)->stripLeadingSpaces();
134 // paste the paragraphs again, if possible
136 (startpit->hasSameLayout(*boost::next(startpit)) ||
137 boost::next(startpit)->empty())) {
138 #warning current_view used here.
139 // should we pass buffer or buffer->params around?
140 Buffer * buffer = current_view->buffer();
141 mergeParagraph(buffer->params, pars, &*startpit);
142 // this because endpar gets deleted here!
147 return pitPosPair(endpit, endpos);
152 bool CutAndPaste::copySelection(Paragraph * startpar, Paragraph * endpar,
153 int start, int end, textclass_type tc)
155 if (!startpar || (start > startpar->size()))
162 if (!endpar || startpar == endpar) {
163 // only within one paragraph
164 ParagraphList::iterator buf =
165 paragraphs.insert(paragraphs.begin(), new Paragraph);
167 buf->layout(startpar->layout());
169 if (end > startpar->size())
170 end = startpar->size();
171 for (; i < end; ++i) {
172 startpar->copyIntoMinibuffer(*current_view->buffer(), i);
173 buf->insertFromMinibuffer(buf->size());
176 // copy more than one paragraph
177 // clone the paragraphs within the selection
178 Paragraph * tmppar = startpar;
180 while (tmppar != endpar) {
181 Paragraph * newpar = new Paragraph(*tmppar, false);
183 newpar->cleanChanges();
184 newpar->setInsetOwner(0);
186 paragraphs.push_back(newpar);
187 tmppar = tmppar->next();
190 // The first paragraph is too big.
191 Paragraph & front = paragraphs.front();
192 pos_type tmpi2 = start;
193 for (; tmpi2; --tmpi2)
196 // Now last paragraph is too big, delete all after end.
197 Paragraph & back = paragraphs.back();
199 while (back.size() > tmpi2) {
200 back.erase(back.size() - 1);
207 bool CutAndPaste::pasteSelection(Paragraph ** par, Paragraph ** endpar,
208 int & pos, textclass_type tc)
210 if (!checkPastePossible())
213 if (pos > (*par)->size())
214 pos = (*par)->size();
218 // make a copy of the simple cut_buffer
220 ParagraphList::iterator it = paragraphs.begin();
222 ParagraphList simple_cut_clone;
223 simple_cut_clone.insert(simple_cut_clone.begin(),
224 new Paragraph(*it, false));
226 ParagraphList::iterator end = paragraphs.end();
227 while (boost::next(it) != end) {
229 simple_cut_clone.insert(simple_cut_clone.end(),
230 new Paragraph(*it, false));
233 // Later we want it done like this:
234 ParagraphList simple_cut_clone(paragraphs.begin(),
237 // now remove all out of the buffer which is NOT allowed in the
238 // new environment and set also another font if that is required
239 ParagraphList::iterator tmpbuf = paragraphs.begin();
240 int depth_delta = (*par)->params().depth() - tmpbuf->params().depth();
241 // Temporary set *par as previous of tmpbuf as we might have
242 // to realize the font.
243 tmpbuf->previous(*par);
245 // make sure there is no class difference
246 SwitchLayoutsBetweenClasses(textclass, tc, &*tmpbuf,
247 current_view->buffer()->params);
249 Paragraph::depth_type max_depth = (*par)->getMaxDepthAfter();
251 while (tmpbuf != paragraphs.end()) {
252 // If we have a negative jump so that the depth would
253 // go below 0 depth then we have to redo the delta to
254 // this new max depth level so that subsequent
255 // paragraphs are aligned correctly to this paragraph
257 if ((int(tmpbuf->params().depth()) + depth_delta) < 0)
259 // set the right depth so that we are not too deep or shallow.
260 tmpbuf->params().depth(tmpbuf->params().depth() + depth_delta);
261 if (tmpbuf->params().depth() > max_depth)
262 tmpbuf->params().depth(max_depth);
263 // only set this from the 2nd on as the 2nd depends for maxDepth
265 if (tmpbuf->previous() != (*par))
266 max_depth = tmpbuf->getMaxDepthAfter();
267 // set the inset owner of this paragraph
268 tmpbuf->setInsetOwner((*par)->inInset());
269 for (pos_type i = 0; i < tmpbuf->size(); ++i) {
270 if (tmpbuf->getChar(i) == Paragraph::META_INSET) {
271 if (!(*par)->insetAllowed(tmpbuf->getInset(i)->lyxCode())) {
275 LyXFont f1 = tmpbuf->getFont(current_view->buffer()->params, i, outerFont(tmpbuf, current_view->buffer()->paragraphs));
277 if (!(*par)->checkInsertChar(f1)) {
279 } else if (f1 != f2) {
280 tmpbuf->setFont(i, f1);
284 tmpbuf = tmpbuf->next();
288 paragraphs.begin()->previous(0);
290 // make the buf exactly the same layout than
291 // the cursor paragraph
292 paragraphs.begin()->makeSameLayout(**par);
294 // find the end of the buffer
295 ParagraphList::iterator lastbuffer = paragraphs.begin();
296 while (boost::next(lastbuffer) != paragraphs.end())
299 bool paste_the_end = false;
301 // open the paragraph for inserting the buf
303 if (((*par)->size() > pos) || !(*par)->next()) {
304 breakParagraphConservative(
305 current_view->buffer()->params, current_view->buffer()->paragraphs, *par, pos);
306 paste_the_end = true;
308 // set the end for redoing later
309 *endpar = (*par)->next()->next();
312 lastbuffer->next((*par)->next());
313 (*par)->next()->previous(&*lastbuffer);
315 (*par)->next(&*paragraphs.begin());
316 paragraphs.begin()->previous(*par);
318 if ((*par)->next() == lastbuffer)
321 mergeParagraph(current_view->buffer()->params,
322 current_view->buffer()->paragraphs, *par);
323 // store the new cursor position
325 pos = lastbuffer->size();
326 // maybe some pasting
327 if (lastbuffer->next() && paste_the_end) {
328 if (lastbuffer->next()->hasSameLayout(*lastbuffer)) {
329 mergeParagraph(current_view->buffer()->params,
330 current_view->buffer()->paragraphs, lastbuffer);
331 } else if (!lastbuffer->next()->size()) {
332 lastbuffer->next()->makeSameLayout(*lastbuffer);
333 mergeParagraph(current_view->buffer()->params, current_view->buffer()->paragraphs, lastbuffer);
334 } else if (!lastbuffer->size()) {
335 lastbuffer->makeSameLayout(*lastbuffer->next());
336 mergeParagraph(current_view->buffer()->params,
337 current_view->buffer()->paragraphs, lastbuffer);
339 lastbuffer->next()->stripLeadingSpaces();
341 // restore the simple cut buffer
342 paragraphs = simple_cut_clone;
348 int CutAndPaste::nrOfParagraphs()
350 return paragraphs.size();
354 int CutAndPaste::SwitchLayoutsBetweenClasses(textclass_type c1,
357 BufferParams const & /*bparams*/)
360 if (!par || c1 == c2)
363 LyXTextClass const & tclass1 = textclasslist[c1];
364 LyXTextClass const & tclass2 = textclasslist[c2];
365 ParIterator end = ParIterator();
366 for (ParIterator it = ParIterator(par); it != end; ++it) {
368 string const name = par->layout()->name();
369 bool hasLayout = tclass2.hasLayout(name);
372 par->layout(tclass2[name]);
374 par->layout(tclass2.defaultLayout());
376 if (!hasLayout && name != tclass1.defaultLayoutName()) {
379 boost::format fmt(_("Layout had to be changed from\n"
381 "because of class conversion from\n"
384 % par->layout()->name()
388 string const s = fmt.str();
390 string const s = _("Layout had to be changed from\n")
392 + par->layout()->name()
393 + _("\nbecause of class conversion from\n")
394 + tclass1.name() + _(" to ")
398 InsetError * new_inset = new InsetError(s);
399 LyXText * txt = current_view->getLyXText();
400 LyXCursor cur = txt->cursor;
401 txt->setCursorIntern(par, 0);
402 txt->insertInset(new_inset);
404 txt->setCursorIntern(cur.par(), cur.pos());
412 bool CutAndPaste::checkPastePossible()
414 return !paragraphs.empty();