]> git.lyx.org Git - lyx.git/blob - src/insets/insettext.C
some fitcursor work
[lyx.git] / src / insets / insettext.C
1 /**
2  * \file insettext.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Jürgen Vigna
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "insettext.h"
14 #include "insetnewline.h"
15
16 #include "buffer.h"
17 #include "bufferparams.h"
18 #include "BufferView.h"
19 #include "CutAndPaste.h"
20 #include "cursor.h"
21 #include "debug.h"
22 #include "dispatchresult.h"
23 #include "errorlist.h"
24 #include "funcrequest.h"
25 #include "gettext.h"
26 #include "intl.h"
27 #include "LColor.h"
28 #include "lyxfind.h"
29 #include "lyxlex.h"
30 #include "lyxrc.h"
31 #include "lyxtext.h"
32 #include "metricsinfo.h"
33 #include "output_docbook.h"
34 #include "output_latex.h"
35 #include "output_linuxdoc.h"
36 #include "output_plaintext.h"
37 #include "paragraph.h"
38 #include "paragraph_funcs.h"
39 #include "ParagraphParameters.h"
40 #include "rowpainter.h"
41 #include "sgml.h"
42 #include "texrow.h"
43 #include "undo.h"
44
45 #include "frontends/Alert.h"
46 #include "frontends/font_metrics.h"
47 #include "frontends/LyXView.h"
48 #include "frontends/Painter.h"
49
50 #include "support/lyxalgo.h" // lyx::count
51
52 #include <boost/bind.hpp>
53
54 using bv_funcs::replaceSelection;
55
56 using lyx::pos_type;
57
58 using lyx::graphics::PreviewLoader;
59
60 using lyx::support::isStrUnsignedInt;
61 using lyx::support::strToUnsignedInt;
62
63 using std::endl;
64 using std::for_each;
65 using std::max;
66 using std::string;
67 using std::auto_ptr;
68 using std::ostream;
69 using std::vector;
70
71
72 InsetText::InsetText(BufferParams const & bp)
73         : UpdatableInset(),
74           paragraphs(1),
75           autoBreakRows_(false),
76           drawFrame_(NEVER),
77           frame_color_(LColor::insetframe),
78           text_(0, this, true, paragraphs)
79 {
80         textwidth_ = 0; // broken
81         paragraphs.begin()->layout(bp.getLyXTextClass().defaultLayout());
82         if (bp.tracking_changes)
83                 paragraphs.begin()->trackChanges();
84         init();
85 }
86
87
88 InsetText::InsetText(InsetText const & in)
89         : UpdatableInset(in),
90           text_(in.text_.bv_owner, this, true, paragraphs)
91 {
92         // this is ugly...
93         operator=(in);
94 }
95
96
97 void InsetText::operator=(InsetText const & in)
98 {
99         UpdatableInset::operator=(in);
100         paragraphs = in.paragraphs;
101         autoBreakRows_ = in.autoBreakRows_;
102         drawFrame_ = in.drawFrame_;
103         frame_color_ = in.frame_color_;
104         textwidth_ = in.textwidth_;
105         text_ = LyXText(in.text_.bv_owner, this, true, paragraphs);
106         init();
107 }
108
109
110 void InsetText::init()
111 {
112         ParagraphList::iterator pit = paragraphs.begin();
113         ParagraphList::iterator end = paragraphs.end();
114         for (; pit != end; ++pit)
115                 pit->setInsetOwner(this);
116         text_.paragraphs_ = &paragraphs;
117         no_selection = true;
118         old_par = -1;
119         in_insetAllowed = false;
120         mouse_x = 0;
121         mouse_y = 0;
122 }
123
124
125 void InsetText::clear(bool just_mark_erased)
126 {
127         if (just_mark_erased) {
128                 ParagraphList::iterator it = paragraphs.begin();
129                 ParagraphList::iterator end = paragraphs.end();
130                 for (; it != end; ++it)
131                         it->markErased();
132                 return;
133         }
134
135         // This is a gross hack...
136         LyXLayout_ptr old_layout = paragraphs.begin()->layout();
137
138         paragraphs.clear();
139         paragraphs.push_back(Paragraph());
140         paragraphs.begin()->setInsetOwner(this);
141         paragraphs.begin()->layout(old_layout);
142 }
143
144
145 auto_ptr<InsetBase> InsetText::clone() const
146 {
147         return auto_ptr<InsetBase>(new InsetText(*this));
148 }
149
150
151 void InsetText::write(Buffer const & buf, ostream & os) const
152 {
153         os << "Text\n";
154         writeParagraphData(buf, os);
155 }
156
157
158 void InsetText::writeParagraphData(Buffer const & buf, ostream & os) const
159 {
160         ParagraphList::const_iterator it = paragraphs.begin();
161         ParagraphList::const_iterator end = paragraphs.end();
162         Paragraph::depth_type dth = 0;
163         for (; it != end; ++it) {
164                 it->write(buf, os, buf.params(), dth);
165         }
166 }
167
168
169 void InsetText::read(Buffer const & buf, LyXLex & lex)
170 {
171         string token;
172         Paragraph::depth_type depth = 0;
173
174         clear(false);
175
176 #warning John, look here. Doesnt make much sense.
177         if (buf.params().tracking_changes)
178                 paragraphs.begin()->trackChanges();
179
180         // delete the initial paragraph
181         Paragraph oldpar = *paragraphs.begin();
182         paragraphs.clear();
183         ParagraphList::iterator pit = paragraphs.begin();
184
185         while (lex.isOK()) {
186                 lex.nextToken();
187                 token = lex.getString();
188                 if (token.empty())
189                         continue;
190                 if (token == "\\end_inset") {
191                         break;
192                 }
193
194                 if (token == "\\end_document") {
195                         lex.printError("\\end_document read in inset! Error in document!");
196                         return;
197                 }
198
199                 // FIXME: ugly.
200                 const_cast<Buffer&>(buf).readParagraph(lex, token, paragraphs, pit, depth);
201         }
202
203         pit = paragraphs.begin();
204         ParagraphList::iterator const end = paragraphs.end();
205         for (; pit != end; ++pit)
206                 pit->setInsetOwner(this);
207
208         if (token != "\\end_inset") {
209                 lex.printError("Missing \\end_inset at this point. "
210                                            "Read: `$$Token'");
211         }
212
213         // sanity check
214         // ensure we have at least one par.
215         if (paragraphs.empty())
216                 paragraphs.push_back(oldpar);
217 }
218
219
220 void InsetText::metrics(MetricsInfo & mi, Dimension & dim) const
221 {
222         //lyxerr << "InsetText::metrics: width: " << mi.base.textwidth << endl;
223         textwidth_ = max(40, mi.base.textwidth - 30);
224         BufferView * bv = mi.base.bv;
225         setViewCache(bv);
226         text_.metrics(mi, dim);
227         dim.asc += TEXT_TO_INSET_OFFSET;
228         dim.des += TEXT_TO_INSET_OFFSET;
229         dim.wid += 2 * TEXT_TO_INSET_OFFSET;
230         dim_ = dim;
231 }
232
233
234 int InsetText::textWidth() const
235 {
236         return textwidth_;
237 }
238
239
240 void InsetText::draw(PainterInfo & pi, int x, int y) const
241 {
242         // update our idea of where we are. Clearly, we should
243         // not have to know this information.
244         xo_ = x;
245         yo_ = y;
246
247         int const start_x = x;
248
249         BufferView * bv = pi.base.bv;
250         Painter & pain = pi.pain;
251
252         // repaint the background if needed
253         if (backgroundColor() != LColor::background)
254                 clearInset(bv, start_x + TEXT_TO_INSET_OFFSET, y);
255
256         bv->hideCursor();
257
258         if (!owner())
259                 x += scroll();
260
261         x += TEXT_TO_INSET_OFFSET;
262
263         paintTextInset(*bv, text_, x, y);
264
265         if (drawFrame_ == ALWAYS || drawFrame_ == LOCKED)
266                 drawFrame(pain, start_x);
267 }
268
269
270 void InsetText::drawFrame(Painter & pain, int x) const
271 {
272         int const ttoD2 = TEXT_TO_INSET_OFFSET / 2;
273         int const frame_x = x + ttoD2;
274         int const frame_y = yo_ - dim_.asc + ttoD2;
275         int const frame_w = dim_.wid - TEXT_TO_INSET_OFFSET;
276         int const frame_h = dim_.asc + dim_.des - TEXT_TO_INSET_OFFSET;
277         pain.rectangle(frame_x, frame_y, frame_w, frame_h, frameColor());
278 }
279
280
281 void InsetText::updateLocal(BufferView * bv, bool /*mark_dirty*/)
282 {
283         if (!bv)
284                 return;
285
286         if (!autoBreakRows_ && paragraphs.size() > 1)
287                 collapseParagraphs(bv);
288
289         if (!text_.selection.set())
290                 text_.selection.cursor = text_.cursor;
291
292 //      bv->fitCursor();
293         bv->update();
294         bv->owner()->view_state_changed();
295         bv->owner()->updateMenubar();
296         bv->owner()->updateToolbar();
297         if (old_par != text_.cursor.par()) {
298                 bv->owner()->setLayout(cpar()->layout()->name());
299                 old_par = text_.cursor.par();
300         }
301 }
302
303
304 string const InsetText::editMessage() const
305 {
306         return _("Opened Text Inset");
307 }
308
309
310 void InsetText::sanitizeEmptyText(BufferView * bv)
311 {
312         if (paragraphs.size() == 1
313                         && paragraphs.begin()->empty() 
314                         && bv->getParentLanguage(this) != text_.current_font.language()) {
315                 LyXFont font(LyXFont::ALL_IGNORE);
316                 font.setLanguage(bv->getParentLanguage(this));
317                 setFont(bv, font, false);
318         }
319 }
320
321
322 extern LCursor theTempCursor;
323
324 void InsetText::lfunMousePress(FuncRequest const & cmd)
325 {
326         lyxerr << "InsetText::lfunMousePress, inset: " << this << endl;
327         no_selection = true;
328
329         // use this to check mouse motion for selection
330         mouse_x = cmd.x;
331         mouse_y = cmd.y;
332
333         BufferView * bv = cmd.view();
334         no_selection = false;
335         text_.clearSelection();
336
337         // set global cursor
338         bv->cursor() = theTempCursor;
339         lyxerr << "new global cursor: \n" << bv->cursor() << endl;
340         text_.setCursorFromCoordinates(cmd.x, cmd.y);
341 }
342
343
344 void InsetText::lfunMouseMotion(FuncRequest const & cmd)
345 {
346         lyxerr << "InsetText::lfunMouseMotion, inset: " << this << endl;
347         if (no_selection || (mouse_x == cmd.x && mouse_y == cmd.y))
348                 return;
349
350         BufferView * bv = cmd.view();
351         LyXCursor cur = text_.cursor;
352         text_.setCursorFromCoordinates(cmd.x, cmd.y + dim_.asc);
353         bv->x_target(text_.cursor.x());
354         if (cur != text_.cursor) {
355                 text_.setSelection();
356                 updateLocal(bv, false);
357         }
358 }
359
360
361 void InsetText::lfunMouseRelease(FuncRequest const &)
362 {
363         lyxerr << "InsetText::lfunMouseRelease, inset: " << this << endl;
364         no_selection = true;
365 }
366
367
368 void InsetText::edit(BufferView * bv, bool left)
369 {
370         lyxerr << "InsetText: edit left/right" << endl;
371         setViewCache(bv);
372
373         old_par = -1;
374
375         if (left)
376                 text_.setCursorIntern(0, 0);
377         else
378                 text_.setCursor(paragraphs.size() - 1, paragraphs.back().size());
379
380         sanitizeEmptyText(bv);
381         updateLocal(bv, false);
382         bv->updateParagraphDialog();
383 }
384
385
386 void InsetText::edit(BufferView * bv, int x, int y)
387 {
388         lyxerr << "InsetText::edit xy" << endl;
389         old_par = -1;
390         sanitizeEmptyText(bv);
391         text_.setCursorFromCoordinates(x, y + dim_.asc);
392         text_.cursor.x(text_.cursor.x());
393         bv->x_target(text_.cursor.x());
394
395         text_.clearSelection();
396         finishUndo();
397
398         updateLocal(bv, false);
399         bv->updateParagraphDialog();
400 }
401
402
403 DispatchResult InsetText::priv_dispatch(FuncRequest const & cmd,
404         idx_type &, pos_type &)
405 {
406         lyxerr << "InsetText::priv_dispatch (begin), act: "
407                 << cmd.action << " " << endl;
408         BufferView * bv = cmd.view();
409         setViewCache(bv);
410         DispatchResult result;
411         result.dispatched(true);
412
413         bool was_empty = paragraphs.begin()->empty() && paragraphs.size() == 1;
414         if (cmd.action != LFUN_MOUSE_PRESS
415                         && cmd.action != LFUN_MOUSE_MOTION
416                         && cmd.action != LFUN_MOUSE_RELEASE)
417                 no_selection = false;
418
419         switch (cmd.action) {
420         case LFUN_MOUSE_PRESS:
421                 lfunMousePress(cmd);
422                 result = DispatchResult(true, true);    
423                 break;
424
425         case LFUN_MOUSE_MOTION:
426                 lfunMouseMotion(cmd);
427                 result = DispatchResult(true, true);
428                 break;
429
430         case LFUN_MOUSE_RELEASE:
431                 lfunMouseRelease(cmd);
432                 result = DispatchResult(true, true);
433                 break;
434
435         case LFUN_SELFINSERT:
436                 if (bv->buffer()->isReadonly()) {
437                         // setErrorMessage(N_("Document is read only"));
438                         break;
439                 }
440                 if (!cmd.argument.empty()) {
441                         /* Automatically delete the currently selected
442                          * text and replace it with what is being
443                          * typed in now. Depends on lyxrc settings
444                          * "auto_region_delete", which defaults to
445                          * true (on). */
446 #if 0
447                         // This should not be needed here and is also WRONG!
448                         recordUndo(bv, Undo::INSERT, text_.cursorPar());
449 #endif
450                         bv->switchKeyMap();
451
452                         if (lyxrc.auto_region_delete && text_.selection.set())
453                                 text_.cutSelection(false, false);
454                         text_.clearSelection();
455
456                         for (string::size_type i = 0; i < cmd.argument.length(); ++i)
457                                 bv->owner()->getIntl().getTransManager().
458                                         TranslateAndInsert(cmd.argument[i], &text_);
459                 }
460                 text_.selection.cursor = text_.cursor;
461                 result.dispatched(true);
462                 result.update(true);
463                 break;
464
465         case LFUN_RIGHT:
466                 result = moveRight(bv);
467                 finishUndo();
468                 break;
469
470         case LFUN_LEFT:
471                 finishUndo();
472                 result = moveLeft(bv);
473                 break;
474
475         case LFUN_DOWN:
476                 finishUndo();
477                 result = moveDown(bv);
478                 break;
479
480         case LFUN_UP:
481                 finishUndo();
482                 result = moveUp(bv);
483                 break;
484
485         case LFUN_PRIOR:
486                 if (crow() == text_.firstRow()) {
487                         result.val(FINISHED_UP);
488                 } else {
489                         text_.cursorPrevious();
490                         text_.clearSelection();
491                         result.dispatched(true);
492                 }
493                 break;
494
495         case LFUN_NEXT:
496                 if (crow() == text_.lastRow()) {
497                         result.val(FINISHED_DOWN);
498                 } else {
499                         text_.cursorNext();
500                         text_.clearSelection();
501                         result.dispatched(true);
502                 }
503                 break;
504
505         case LFUN_BACKSPACE:
506                 if (text_.selection.set())
507                         text_.cutSelection(true, false);
508                 else
509                         text_.backspace();
510 #warning should be also set dispatched here?
511                 result.update(true);
512                 break;
513
514         case LFUN_DELETE:
515                 if (text_.selection.set())
516                         text_.cutSelection(true, false);
517                 else
518                         text_.Delete();
519 #warning should be also set dispatched here?
520                 result.update(true);
521                 break;
522
523         case LFUN_PASTE:
524                 if (!autoBreakRows_) {
525                         if (CutAndPaste::nrOfParagraphs() > 1) {
526 #ifdef WITH_WARNINGS
527 #warning FIXME horrendously bad UI
528 #endif
529                                 Alert::error(_("Paste failed"),
530                                         _("Cannot include more than one paragraph."));
531                         }
532                 } else {
533                         replaceSelection(bv->getLyXText());
534                         size_t sel_index = 0;
535                         string const & arg = cmd.argument;
536                         if (isStrUnsignedInt(arg)) {
537 #warning FIXME Check if the arg is in the domain of available selections.
538                                 sel_index = strToUnsignedInt(arg);
539                         }
540                         text_.pasteSelection(sel_index);
541                         // bug 393
542                         text_.clearSelection();
543 #warning should be also set dispatched here?
544                         result.update(true);
545                 }
546                 break;
547
548         case LFUN_BREAKPARAGRAPH:
549                 if (!autoBreakRows_) {
550                         result.dispatched(true);
551                         result.update(true);
552                 } else {
553                         replaceSelection(bv->getLyXText());
554                         text_.breakParagraph(paragraphs, 0);
555 #warning should be also set dispatched here?
556                         result.update(true);
557                 }
558                 break;
559
560         case LFUN_BREAKPARAGRAPHKEEPLAYOUT:
561                 if (!autoBreakRows_) {
562                         result.dispatched(true);
563                         result.update(true);
564                 } else {
565                         replaceSelection(bv->getLyXText());
566                         text_.breakParagraph(paragraphs, 1);
567 #warning should be also set dispatched here?
568                         result.update(true);
569                 }
570                 break;
571
572         case LFUN_BREAKLINE: {
573                 if (!autoBreakRows_) {
574                         result.dispatched(true);
575                         result.update(true);
576                 } else {
577                         replaceSelection(bv->getLyXText());
578                         auto_ptr<InsetNewline> ins(new InsetNewline);
579                         text_.insertInset(ins.release());
580 #warning should be also set dispatched here?
581                         result.update(true);
582                 }
583                 break;
584         }
585
586         case LFUN_LAYOUT:
587                 // do not set layouts on non breakable textinsets
588                 if (autoBreakRows_) {
589                         string cur_layout = cpar()->layout()->name();
590
591                         // Derive layout number from given argument (string)
592                         // and current buffer's textclass (number).
593                         LyXTextClass const & tclass =
594                                 bv->buffer()->params().getLyXTextClass();
595                         string layout = cmd.argument;
596                         bool hasLayout = tclass.hasLayout(layout);
597
598                         // If the entry is obsolete, use the new one instead.
599                         if (hasLayout) {
600                                 string const & obs = tclass[layout]->obsoleted_by();
601                                 if (!obs.empty())
602                                         layout = obs;
603                         }
604
605                         // see if we found the layout number:
606                         if (!hasLayout) {
607                                 FuncRequest lf(LFUN_MESSAGE, N_("Layout ") + cmd.argument + N_(" not known"));
608                                 bv->owner()->dispatch(lf);
609                                 break;
610                         }
611
612                         if (cur_layout != layout) {
613                                 cur_layout = layout;
614                                 text_.setLayout(layout);
615                                 bv->owner()->setLayout(cpar()->layout()->name());
616 #warning should be also set dispatched here?
617                                 result.update(true);
618                         }
619                 } else {
620                         // reset the layout box
621                         bv->owner()->setLayout(cpar()->layout()->name());
622                 }
623                 break;
624
625         default:
626                 result = text_.dispatch(cmd);
627                 break;
628         }
629
630         /// If the action has deleted all text in the inset, we need to change the
631         // language to the language of the surronding text.
632         if (!was_empty && paragraphs.begin()->empty() &&
633             paragraphs.size() == 1) {
634                 LyXFont font(LyXFont::ALL_IGNORE);
635                 font.setLanguage(bv->getParentLanguage(this));
636                 setFont(bv, font, false);
637         }
638
639         lyxerr << "InsetText::priv_dispatch (end)" << endl;
640         return result;
641 }
642
643
644 int InsetText::latex(Buffer const & buf, ostream & os,
645                      OutputParams const & runparams) const
646 {
647         TexRow texrow;
648         latexParagraphs(buf, paragraphs, os, texrow, runparams);
649         return texrow.rows();
650 }
651
652
653 int InsetText::plaintext(Buffer const & buf, ostream & os,
654                      OutputParams const & runparams) const
655 {
656         ParagraphList::const_iterator beg = paragraphs.begin();
657         ParagraphList::const_iterator end = paragraphs.end();
658         ParagraphList::const_iterator it = beg;
659         for (; it != end; ++it)
660                 asciiParagraph(buf, *it, os, runparams, it == beg);
661
662         //FIXME: Give the total numbers of lines
663         return 0;
664 }
665
666
667 int InsetText::linuxdoc(Buffer const & buf, ostream & os,
668                         OutputParams const & runparams) const
669 {
670         linuxdocParagraphs(buf, paragraphs, os, runparams);
671         return 0;
672 }
673
674
675 int InsetText::docbook(Buffer const & buf, ostream & os,
676                        OutputParams const & runparams) const
677 {
678         docbookParagraphs(buf, paragraphs, os, runparams);
679         return 0;
680 }
681
682
683 void InsetText::validate(LaTeXFeatures & features) const
684 {
685         for_each(paragraphs.begin(), paragraphs.end(),
686                  boost::bind(&Paragraph::validate, _1, boost::ref(features)));
687 }
688
689
690 void InsetText::getCursorPos(BufferView *, int & x, int & y) const
691 {
692         x = cx() - xo_;
693         y = cy();
694 }
695
696
697 int InsetText::insetInInsetY() const
698 {
699         return 0;
700 }
701
702
703 void InsetText::fitInsetCursor(BufferView * bv) const
704 {
705         LyXFont const font = text_.getFont(cpar(), cpos());
706         int const asc = font_metrics::maxAscent(font);
707         int const desc = font_metrics::maxDescent(font);
708         bv->fitLockedInsetCursor(cx(), cy(), asc, desc);
709 }
710
711
712 DispatchResult InsetText::moveRight(BufferView * bv)
713 {
714         if (text_.cursorPar()->isRightToLeftPar(bv->buffer()->params()))
715                 return moveLeftIntern(bv, false, true, false);
716         else
717                 return moveRightIntern(bv, true, true, false);
718 }
719
720
721 DispatchResult InsetText::moveLeft(BufferView * bv)
722 {
723         if (text_.cursorPar()->isRightToLeftPar(bv->buffer()->params()))
724                 return moveRightIntern(bv, true, true, false);
725         else
726                 return moveLeftIntern(bv, false, true, false);
727 }
728
729
730 DispatchResult InsetText::moveRightIntern(BufferView * bv, bool front,
731                            bool activate_inset, bool selecting)
732 {
733         ParagraphList::iterator c_par = cpar();
734         if (boost::next(c_par) == paragraphs.end() && cpos() >= c_par->size())
735                 return DispatchResult(false, FINISHED_RIGHT);
736         if (activate_inset && checkAndActivateInset(bv, front))
737                 return DispatchResult(true, true);
738         text_.cursorRight(bv);
739         if (!selecting)
740                 text_.clearSelection();
741         return DispatchResult(true);
742 }
743
744
745 DispatchResult InsetText::moveLeftIntern(BufferView * bv, bool front,
746                           bool activate_inset, bool selecting)
747 {
748         if (cpar() == paragraphs.begin() && cpos() <= 0)
749                 return DispatchResult(false, FINISHED);
750         text_.cursorLeft(bv);
751         if (!selecting)
752                 text_.clearSelection();
753         if (activate_inset && checkAndActivateInset(bv, front))
754                 return DispatchResult(true, true);
755         return DispatchResult(true);
756 }
757
758
759 DispatchResult InsetText::moveUp(BufferView * bv)
760 {
761         if (crow() == text_.firstRow())
762                 return DispatchResult(false, FINISHED_UP);
763         text_.cursorUp(bv);
764         text_.clearSelection();
765         return DispatchResult(true);
766 }
767
768
769 DispatchResult InsetText::moveDown(BufferView * bv)
770 {
771         if (crow() == text_.lastRow())
772                 return DispatchResult(false, FINISHED_DOWN);
773         text_.cursorDown(bv);
774         text_.clearSelection();
775         return DispatchResult(true);
776 }
777
778
779 bool InsetText::insertInset(BufferView * bv, InsetOld * inset)
780 {
781         inset->setOwner(this);
782         text_.insertInset(inset);
783 //      bv->fitCursor();
784         updateLocal(bv, true);
785         return true;
786 }
787
788
789 bool InsetText::insetAllowed(InsetOld::Code code) const
790 {
791         // in_insetAllowed is a really gross hack,
792         // to allow us to call the owner's insetAllowed
793         // without stack overflow, which can happen
794         // when the owner uses InsetCollapsable::insetAllowed()
795         bool ret = true;
796         if (in_insetAllowed)
797                 return ret;
798         in_insetAllowed = true;
799         if (owner())
800                 ret = owner()->insetAllowed(code);
801         in_insetAllowed = false;
802         return ret;
803 }
804
805
806 bool InsetText::showInsetDialog(BufferView *) const
807 {
808         return false;
809 }
810
811
812 void InsetText::getLabelList(Buffer const & buffer,
813                              std::vector<string> & list) const
814 {
815         ParagraphList::const_iterator pit = paragraphs.begin();
816         ParagraphList::const_iterator pend = paragraphs.end();
817         for (; pit != pend; ++pit) {
818                 InsetList::const_iterator beg = pit->insetlist.begin();
819                 InsetList::const_iterator end = pit->insetlist.end();
820                 for (; beg != end; ++beg)
821                         beg->inset->getLabelList(buffer, list);
822         }
823 }
824
825
826 void InsetText::setFont(BufferView * bv, LyXFont const & font, bool toggleall,
827                         bool selectall)
828 {
829         if ((paragraphs.size() == 1 && paragraphs.begin()->empty())
830             || cpar()->empty()) {
831                 text_.setFont(font, toggleall);
832                 return;
833         }
834
835         if (text_.selection.set())
836                 text_.recUndo(text_.cursor.par());
837
838         if (selectall) {
839                 text_.cursorTop();
840                 text_.selection.cursor = text_.cursor;
841                 text_.cursorBottom();
842                 text_.setSelection();
843         }
844
845         text_.toggleFree(font, toggleall);
846
847         if (selectall)
848                 text_.clearSelection();
849
850 //      bv->fitCursor();
851         updateLocal(bv, true);
852 }
853
854
855 bool InsetText::checkAndActivateInset(BufferView * bv, bool front)
856 {
857         if (cpos() == cpar()->size())
858                 return false;
859         InsetOld * inset = cpar()->getInset(cpos());
860         if (!isHighlyEditableInset(inset))
861                 return false;
862         inset->edit(bv, front);
863         updateLocal(bv, false);
864         return true;
865 }
866
867
868 void InsetText::markNew(bool track_changes)
869 {
870         ParagraphList::iterator pit = paragraphs.begin();
871         ParagraphList::iterator end = paragraphs.end();
872         for (; pit != end; ++pit) {
873                 if (track_changes) {
874                         pit->trackChanges();
875                 } else {
876                         // no-op when not tracking
877                         pit->cleanChanges();
878                 }
879         }
880 }
881
882
883 void InsetText::setText(string const & data, LyXFont const & font)
884 {
885         clear(false);
886         for (unsigned int i = 0; i < data.length(); ++i)
887                 paragraphs.begin()->insertChar(i, data[i], font);
888 }
889
890
891 void InsetText::setAutoBreakRows(bool flag)
892 {
893         if (flag != autoBreakRows_) {
894                 autoBreakRows_ = flag;
895                 if (!flag)
896                         removeNewlines();
897         }
898 }
899
900
901 void InsetText::setDrawFrame(DrawFrame how)
902 {
903         drawFrame_ = how;
904 }
905
906
907 LColor_color InsetText::frameColor() const
908 {
909         return LColor::color(frame_color_);
910 }
911
912
913 void InsetText::setFrameColor(LColor_color col)
914 {
915         frame_color_ = col;
916 }
917
918
919 int InsetText::cx() const
920 {
921         return text_.cursor.x() + xo_ + TEXT_TO_INSET_OFFSET;
922 }
923
924
925 int InsetText::cy() const
926 {
927         return text_.cursor.y() - dim_.asc + TEXT_TO_INSET_OFFSET;
928 }
929
930
931 pos_type InsetText::cpos() const
932 {
933         return text_.cursor.pos();
934 }
935
936
937 ParagraphList::iterator InsetText::cpar() const
938 {
939         return text_.cursorPar();
940 }
941
942
943 RowList::iterator InsetText::crow() const
944 {
945         return cpar()->getRow(cpos());
946 }
947
948
949 void InsetText::setViewCache(BufferView const * bv) const
950 {
951         if (bv) {
952                 if (bv != text_.bv_owner) {
953                         //lyxerr << "setting view cache from "
954                         //      << text_.bv_owner << " to " << bv << "\n";
955                         text_.init(const_cast<BufferView *>(bv));
956                 }
957                 text_.bv_owner = const_cast<BufferView *>(bv);
958         }
959 }
960
961
962 void InsetText::deleteLyXText(BufferView * bv) const
963 {
964         /// then remove all LyXText in text-insets
965         for_each(const_cast<ParagraphList&>(paragraphs).begin(),
966                  const_cast<ParagraphList&>(paragraphs).end(),
967                  boost::bind(&Paragraph::deleteInsetsLyXText, _1, bv));
968 }
969
970
971 void InsetText::removeNewlines()
972 {
973         ParagraphList::iterator it = paragraphs.begin();
974         ParagraphList::iterator end = paragraphs.end();
975         for (; it != end; ++it)
976                 for (int i = 0; i < it->size(); ++i)
977                         if (it->isNewline(i))
978                                 it->erase(i);
979 }
980
981
982 int InsetText::scroll(bool /*recursive*/) const
983 {
984         return UpdatableInset::scroll(false);
985 }
986
987
988 void InsetText::clearSelection(BufferView *)
989 {
990         text_.clearSelection();
991 }
992
993
994 void InsetText::clearInset(BufferView * bv, int start_x, int baseline) const
995 {
996         Painter & pain = bv->painter();
997         int w = dim_.wid;
998         int h = dim_.asc + dim_.des;
999         int ty = baseline - dim_.asc;
1000
1001         if (ty < 0) {
1002                 h += ty;
1003                 ty = 0;
1004         }
1005         if (ty + h > pain.paperHeight())
1006                 h = pain.paperHeight();
1007         if (xo_ + w > pain.paperWidth())
1008                 w = pain.paperWidth();
1009         pain.fillRectangle(start_x + 1, ty + 1, w - 3, h - 1, backgroundColor());
1010 }
1011
1012
1013 ParagraphList * InsetText::getParagraphs(int i) const
1014 {
1015         return (i == 0) ? const_cast<ParagraphList*>(&paragraphs) : 0;
1016 }
1017
1018
1019 LyXText * InsetText::getText(int i) const
1020 {
1021         return (i == 0) ? const_cast<LyXText*>(&text_) : 0;
1022 }
1023
1024
1025 bool InsetText::checkInsertChar(LyXFont & font)
1026 {
1027         return owner() ? owner()->checkInsertChar(font) : true;
1028 }
1029
1030
1031 void InsetText::collapseParagraphs(BufferView * bv)
1032 {
1033         while (paragraphs.size() > 1) {
1034                 ParagraphList::iterator const first = paragraphs.begin();
1035                 ParagraphList::iterator second = first;
1036                 ++second;
1037                 size_t const first_par_size = first->size();
1038
1039                 if (!first->empty() &&
1040                     !second->empty() &&
1041                     !first->isSeparator(first_par_size - 1)) {
1042                         first->insertChar(first_par_size, ' ');
1043                 }
1044
1045 #warning probably broken
1046                 if (text_.selection.set()) {
1047                         if (text_.selection.start.par() == 1) {
1048                                 text_.selection.start.par(1);
1049                                 text_.selection.start.pos(text_.selection.start.pos() + first_par_size);
1050                         }
1051                         if (text_.selection.end.par() == 2) {
1052                                 text_.selection.end.par(1);
1053                                 text_.selection.end.pos(text_.selection.end.pos() + first_par_size);
1054                         }
1055                 }
1056
1057                 mergeParagraph(bv->buffer()->params(), paragraphs, first);
1058         }
1059 }
1060
1061
1062 void InsetText::getDrawFont(LyXFont & font) const
1063 {
1064         if (owner())
1065                 owner()->getDrawFont(font);
1066 }
1067
1068
1069 void InsetText::appendParagraphs(Buffer * buffer, ParagraphList & plist)
1070 {
1071 #warning FIXME Check if Changes stuff needs changing here. (Lgb)
1072 // And it probably does. You have to take a look at this John. (Lgb)
1073 #warning John, have a look here. (Lgb)
1074         ParagraphList::iterator pit = plist.begin();
1075         ParagraphList::iterator ins = paragraphs.insert(paragraphs.end(), *pit);
1076         ++pit;
1077         mergeParagraph(buffer->params(), paragraphs, boost::prior(ins));
1078
1079         ParagraphList::iterator pend = plist.end();
1080         for (; pit != pend; ++pit)
1081                 paragraphs.push_back(*pit);
1082 }
1083
1084
1085 void InsetText::addPreview(PreviewLoader & loader) const
1086 {
1087         ParagraphList::const_iterator pit = paragraphs.begin();
1088         ParagraphList::const_iterator pend = paragraphs.end();
1089
1090         for (; pit != pend; ++pit) {
1091                 InsetList::const_iterator it  = pit->insetlist.begin();
1092                 InsetList::const_iterator end = pit->insetlist.end();
1093                 for (; it != end; ++it)
1094                         it->inset->addPreview(loader);
1095         }
1096 }