]> git.lyx.org Git - lyx.git/blob - src/insets/insettext.C
9e8c0a868df617695821b44345bf6af7bb8debb5
[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
15 #include "buffer.h"
16 #include "BufferView.h"
17 #include "CutAndPaste.h"
18 #include "debug.h"
19 #include "dimension.h"
20 #include "funcrequest.h"
21 #include "gettext.h"
22 #include "errorlist.h"
23 #include "intl.h"
24 #include "LaTeXFeatures.h"
25 #include "LColor.h"
26 #include "Lsstream.h"
27 #include "lyxfont.h"
28 #include "lyxcursor.h"
29 #include "lyxfind.h"
30 #include "lyxlex.h"
31 #include "lyxrow.h"
32 #include "lyxrc.h"
33 #include "lyxtext.h"
34 #include "paragraph.h"
35 #include "ParagraphParameters.h"
36 #include "trans_mgr.h"
37 #include "undo_funcs.h"
38 #include "WordLangTuple.h"
39 #include "paragraph_funcs.h"
40 #include "sgml.h"
41 #include "rowpainter.h"
42 #include "insetnewline.h"
43 #include "metricsinfo.h"
44
45 #include "frontends/Alert.h"
46 #include "frontends/Dialogs.h"
47 #include "frontends/font_metrics.h"
48 #include "frontends/LyXView.h"
49 #include "frontends/Painter.h"
50 #include "frontends/screen.h"
51
52 #include "support/textutils.h"
53 #include "support/LAssert.h"
54 #include "support/lstrings.h"
55 #include "support/lyxalgo.h" // lyx::count
56
57 #include <boost/bind.hpp>
58
59 #include <fstream>
60 #include <algorithm>
61 #include <cstdlib>
62 //#include <csignal>
63
64 using std::ostream;
65 using std::ifstream;
66 using std::endl;
67 using std::min;
68 using std::max;
69 using std::make_pair;
70 using std::vector;
71 using std::pair;
72 using std::for_each;
73
74 using lyx::pos_type;
75 using lyx::textclass_type;
76
77
78 // These functions should probably go into bufferview_funcs somehow (Jug)
79
80 void InsetText::saveLyXTextState(LyXText * t) const
81 {
82         // check if my paragraphs are still valid
83         ParagraphList::iterator it = const_cast<ParagraphList&>(paragraphs).begin();
84         ParagraphList::iterator end = const_cast<ParagraphList&>(paragraphs).end();
85         for (; it != end; ++it) {
86                 if (it == t->cursor.par())
87                         break;
88         }
89
90         if (it != end && t->cursor.pos() <= it->size()) {
91                 sstate.lpar = t->cursor.par();
92                 sstate.pos = t->cursor.pos();
93                 sstate.boundary = t->cursor.boundary();
94                 sstate.selstartpar = t->selection.start.par();
95                 sstate.selstartpos = t->selection.start.pos();
96                 sstate.selstartboundary = t->selection.start.boundary();
97                 sstate.selendpar = t->selection.end.par();
98                 sstate.selendpos = t->selection.end.pos();
99                 sstate.selendboundary = t->selection.end.boundary();
100                 sstate.selection = t->selection.set();
101                 sstate.mark_set = t->selection.mark();
102         } else {
103                 sstate.lpar = const_cast<ParagraphList&>(paragraphs).end();
104         }
105 }
106
107
108 void InsetText::restoreLyXTextState(LyXText * t) const
109 {
110         if (sstate.lpar == const_cast<ParagraphList&>(paragraphs).end())
111                 return;
112
113         t->selection.set(true);
114         /* at this point just to avoid the DEPM when setting the cursor */
115         t->selection.mark(sstate.mark_set);
116         if (sstate.selection) {
117                 t->setCursor(sstate.selstartpar, sstate.selstartpos,
118                              true, sstate.selstartboundary);
119                 t->selection.cursor = t->cursor;
120                 t->setCursor(sstate.selendpar, sstate.selendpos,
121                              true, sstate.selendboundary);
122                 t->setSelection();
123                 t->setCursor(sstate.lpar, sstate.pos);
124         } else {
125                 t->setCursor(sstate.lpar, sstate.pos, true, sstate.boundary);
126                 t->selection.cursor = t->cursor;
127                 t->selection.set(false);
128         }
129 }
130
131
132 InsetText::InnerCache::InnerCache(boost::shared_ptr<LyXText> t)
133 {
134         text = t;
135         remove = false;
136 }
137
138
139 InsetText::InsetText(BufferParams const & bp)
140         : UpdatableInset(), lt(0), in_update(false), do_resize(0),
141           do_reinit(false)
142 {
143         paragraphs.push_back(Paragraph());
144         paragraphs.begin()->layout(bp.getLyXTextClass().defaultLayout());
145         if (bp.tracking_changes)
146                 paragraphs.begin()->trackChanges();
147         init(0);
148 }
149
150
151 InsetText::InsetText(InsetText const & in)
152         : UpdatableInset(in), lt(0), in_update(false), do_resize(0),
153           do_reinit(false)
154 {
155         init(&in);
156 }
157
158
159 InsetText & InsetText::operator=(InsetText const & it)
160 {
161         init(&it);
162         return * this;
163 }
164
165
166 void InsetText::init(InsetText const * ins)
167 {
168         if (ins) {
169                 setParagraphData(ins->paragraphs);
170                 autoBreakRows = ins->autoBreakRows;
171                 drawFrame_ = ins->drawFrame_;
172                 frame_color = ins->frame_color;
173         } else {
174                 for_each(paragraphs.begin(), paragraphs.end(),
175                          boost::bind(&Paragraph::setInsetOwner, _1, this));
176
177                 the_locking_inset = 0;
178                 drawFrame_ = NEVER;
179                 frame_color = LColor::insetframe;
180                 autoBreakRows = false;
181         }
182         top_y = 0;
183         old_max_width = 0;
184         no_selection = true;
185         need_update = FULL;
186         drawTextXOffset = 0;
187         drawTextYOffset = 0;
188         locked = false;
189         old_par = paragraphs.end();
190         last_drawn_width = -1;
191         cached_bview = 0;
192         sstate.lpar = paragraphs.end();
193         in_insetAllowed = false;
194 }
195
196
197 void InsetText::clear(bool just_mark_erased)
198 {
199         if (just_mark_erased) {
200                 ParagraphList::iterator it = paragraphs.begin();
201                 ParagraphList::iterator end = paragraphs.end();
202                 for (; it != end; ++it) {
203                         it->markErased();
204                 }
205                 need_update = FULL;
206                 return;
207         }
208
209         // This is a gross hack...
210         LyXLayout_ptr old_layout = paragraphs.begin()->layout();
211
212         paragraphs.clear();
213         paragraphs.push_back(Paragraph());
214         paragraphs.begin()->setInsetOwner(this);
215         paragraphs.begin()->layout(old_layout);
216
217         reinitLyXText();
218         need_update = INIT;
219 }
220
221
222 InsetBase * InsetText::clone() const
223 {
224         return new InsetText(*this);
225 }
226
227
228 void InsetText::write(Buffer const * buf, ostream & os) const
229 {
230         os << "Text\n";
231         writeParagraphData(buf, os);
232 }
233
234
235 void InsetText::writeParagraphData(Buffer const * buf, ostream & os) const
236 {
237         ParagraphList::const_iterator it = paragraphs.begin();
238         ParagraphList::const_iterator end = paragraphs.end();
239         Paragraph::depth_type dth = 0;
240         for (; it != end; ++it) {
241                 it->write(buf, os, buf->params, dth);
242         }
243 }
244
245
246 void InsetText::read(Buffer const * buf, LyXLex & lex)
247 {
248         string token;
249         Paragraph::depth_type depth = 0;
250
251         clear(false);
252
253         if (buf->params.tracking_changes)
254                 paragraphs.begin()->trackChanges();
255
256         // delete the initial paragraph
257         paragraphs.clear();
258         ParagraphList::iterator pit = paragraphs.begin();
259
260         while (lex.isOK()) {
261                 lex.nextToken();
262                 token = lex.getString();
263                 if (token.empty())
264                         continue;
265                 if (token == "\\end_inset") {
266                         break;
267                 }
268
269                 if (token == "\\the_end") {
270                         lex.printError("\\the_end read in inset! Error in document!");
271                         return;
272                 }
273
274                 // FIXME: ugly.
275                 const_cast<Buffer*>(buf)->readParagraph(lex, token, paragraphs, pit, depth);
276         }
277
278         pit = paragraphs.begin();
279         ParagraphList::iterator const end = paragraphs.end();
280         for (; pit != end; ++pit)
281                 pit->setInsetOwner(this);
282
283         if (token != "\\end_inset") {
284                 lex.printError("Missing \\end_inset at this point. "
285                                            "Read: `$$Token'");
286         }
287         need_update = FULL;
288 }
289
290
291 void InsetText::metrics(MetricsInfo & mi, Dimension & dim) const
292 {
293         BufferView * bv = mi.base.bv;
294         LyXText * text = getLyXText(bv);
295         dim.asc = text->rows().begin()->ascent_of_text() + TEXT_TO_INSET_OFFSET;
296         dim.des = text->height - dim.asc + TEXT_TO_INSET_OFFSET;
297         dim.wid = max(textWidth(bv), int(text->width)) + 2 * TEXT_TO_INSET_OFFSET;
298         dim.wid = max(dim.wid, 10);
299         dim_ = dim;
300 }
301
302
303 int InsetText::textWidth(BufferView * bv, bool fordraw) const
304 {
305         int w = autoBreakRows ? getMaxWidth(bv, this) : -1;
306
307         if (fordraw)
308                 return max(w - 2 * TEXT_TO_INSET_OFFSET,
309                            (int)getLyXText(bv)->width);
310
311         if (w < 0)
312                 return -1;
313
314         return w - 2 * TEXT_TO_INSET_OFFSET;
315 }
316
317
318 void InsetText::draw(PainterInfo & pi, int x, int baseline) const
319 {
320         if (nodraw())
321                 return;
322
323         // update our idea of where we are. Clearly, we should
324         // not have to know this information.
325         if (top_x != x)
326                 top_x = x;
327
328         int const start_x = x;
329
330         BufferView * bv = pi.base.bv;
331         Painter & pain = pi.pain;
332
333         // repaint the background if needed
334         if (backgroundColor() != LColor::background)
335                 clearInset(bv, start_x + TEXT_TO_INSET_OFFSET, baseline);
336
337         // no draw is necessary !!!
338         if (drawFrame_ == LOCKED && !locked && paragraphs.begin()->empty()) {
339                 top_baseline = baseline;
340                 need_update = NONE;
341                 return;
342         }
343
344         if (!owner())
345                 x += scroll();
346
347         top_baseline = baseline;
348         top_y = baseline - dim_.asc;
349
350         if (last_drawn_width != dim_.wid) {
351                 need_update |= FULL;
352                 last_drawn_width = dim_.wid;
353         }
354
355         if (the_locking_inset && (cpar(bv) == inset_par)
356                 && (cpos(bv) == inset_pos)) {
357                 inset_x = cix(bv) - int(x) + drawTextXOffset;
358                 inset_y = ciy(bv) + drawTextYOffset;
359         }
360
361         bool clear = false;
362         if (!lt) {
363                 lt = getLyXText(bv);
364                 clear = true;
365         }
366         x += TEXT_TO_INSET_OFFSET;
367
368         RowList::iterator rowit = lt->rows().begin();
369         RowList::iterator end = lt->rows().end();
370
371         int y_offset = baseline - rowit->ascent_of_text();
372         int ph = pain.paperHeight();
373         int first = 0;
374         int y = y_offset;
375         while ((rowit != end) && ((y + rowit->height()) <= 0)) {
376                 y += rowit->height();
377                 first += rowit->height();
378                 ++rowit;
379         }
380         if (y_offset < 0) {
381                 lt->top_y(-y_offset);
382                 first = y;
383                 y_offset = 0;
384         } else {
385                 lt->top_y(first);
386                 first = 0;
387         }
388
389         int yf = y_offset + first;
390         y = 0;
391
392         bv->hideCursor();
393
394         while ((rowit != end) && (yf < ph)) {
395                 RowPainter rp(*bv, *lt, rowit);
396                 rp.paint(y + y_offset + first, int(x), y + lt->top_y());
397                 y += rowit->height();
398                 yf += rowit->height();
399                 ++rowit;
400         }
401
402         lt->clearPaint();
403
404         if ((drawFrame_ == ALWAYS) || (drawFrame_ == LOCKED && locked)) {
405                 drawFrame(pain, int(start_x));
406         }
407
408         x += dim_.wid - TEXT_TO_INSET_OFFSET;
409
410         if (need_update != INIT) {
411                 need_update = NONE;
412         }
413         if (clear)
414                 lt = 0;
415 }
416
417
418 void InsetText::drawFrame(Painter & pain, int x) const
419 {
420         static int const ttoD2 = TEXT_TO_INSET_OFFSET / 2;
421         frame_x = x + ttoD2;
422         frame_y = top_baseline - dim_.asc + ttoD2;
423         frame_w = dim_.wid - TEXT_TO_INSET_OFFSET;
424         frame_h = dim_.asc + dim_.des - TEXT_TO_INSET_OFFSET;
425         pain.rectangle(frame_x, frame_y, frame_w, frame_h,
426                        frame_color);
427 }
428
429
430 void InsetText::update(BufferView * bv, bool reinit)
431 {
432         if (in_update) {
433                 if (reinit && owner()) {
434                         reinitLyXText();
435                         owner()->update(bv, true);
436                 }
437                 return;
438         }
439         in_update = true;
440
441         if (reinit || need_update == INIT) {
442                 need_update = FULL;
443                 // we should put this call where we set need_update to INIT!
444                 reinitLyXText();
445                 if (owner())
446                         owner()->update(bv, true);
447                 in_update = false;
448
449                 int nw = getMaxWidth(bv, this);
450                 if (nw > 0 && old_max_width != nw) {
451                         need_update |= INIT;
452                         old_max_width = nw;
453                 }
454                 return;
455         }
456
457         if (!autoBreakRows && paragraphs.size() > 1)
458                 collapseParagraphs(bv);
459
460         if (the_locking_inset) {
461                 inset_x = cix(bv) - top_x + drawTextXOffset;
462                 inset_y = ciy(bv) + drawTextYOffset;
463                 the_locking_inset->update(bv, reinit);
464         }
465
466         bool clear = false;
467         if (!lt) {
468                 lt = getLyXText(bv);
469                 clear = true;
470         }
471         if ((need_update & CURSOR_PAR) && (lt->refreshStatus() == LyXText::REFRESH_NONE) &&
472                 the_locking_inset) {
473                 lt->updateInset(the_locking_inset);
474         }
475
476         if (lt->refreshStatus() == LyXText::REFRESH_AREA)
477                 need_update |= FULL;
478         if (clear)
479                 lt = 0;
480
481         in_update = false;
482
483         int nw = getMaxWidth(bv, this);
484         if (nw > 0 && old_max_width != nw) {
485                 need_update |= INIT;
486                 old_max_width = nw;
487         }
488 }
489
490
491 void InsetText::setUpdateStatus(BufferView * bv, int what) const
492 {
493         // this does nothing dangerous so use only a localized buffer
494         LyXText * llt = getLyXText(bv);
495
496         need_update |= what;
497         // we have to redraw us full if our LyXText REFRESH_AREA or
498         // if we don't break row so that we only have one row to update!
499         if ((llt->refreshStatus() == LyXText::REFRESH_AREA) ||
500             (!autoBreakRows &&
501              (llt->refreshStatus() == LyXText::REFRESH_ROW)))
502         {
503                 need_update |= FULL;
504         } else if (llt->refreshStatus() == LyXText::REFRESH_ROW) {
505                 need_update |= CURSOR_PAR;
506         }
507
508         // this to not draw a selection when we redraw all of it!
509         if (need_update & CURSOR && !(need_update & SELECTION)) {
510                 if (llt->selection.set())
511                         need_update = FULL;
512                 llt->clearSelection();
513         }
514 }
515
516
517 void InsetText::updateLocal(BufferView * bv, int what, bool mark_dirty)
518 {
519         if (!autoBreakRows && paragraphs.size() > 1)
520                 collapseParagraphs(bv);
521
522         bool clear = false;
523         if (!lt) {
524                 lt = getLyXText(bv);
525                 clear = true;
526         }
527         lt->partialRebreak();
528         setUpdateStatus(bv, what);
529         bool flag = mark_dirty ||
530                 (((need_update != CURSOR) && (need_update != NONE)) ||
531                  (lt->refreshStatus() != LyXText::REFRESH_NONE) || lt->selection.set());
532         if (!lt->selection.set())
533                 lt->selection.cursor = lt->cursor;
534
535         bv->fitCursor();
536
537         if (flag) {
538                 lt->postPaint(0);
539                 bv->updateInset(const_cast<InsetText *>(this));
540         }
541
542         if (clear)
543                 lt = 0;
544
545         if (need_update == CURSOR)
546                 need_update = NONE;
547         bv->owner()->view_state_changed();
548         bv->owner()->updateMenubar();
549         bv->owner()->updateToolbar();
550         if (old_par != cpar(bv)) {
551                 bv->owner()->setLayout(cpar(bv)->layout()->name());
552                 old_par = cpar(bv);
553         }
554 }
555
556
557 string const InsetText::editMessage() const
558 {
559         return _("Opened Text Inset");
560 }
561
562
563 void InsetText::insetUnlock(BufferView * bv)
564 {
565         if (the_locking_inset) {
566                 the_locking_inset->insetUnlock(bv);
567                 the_locking_inset = 0;
568                 updateLocal(bv, CURSOR_PAR, false);
569         }
570         no_selection = true;
571         locked = false;
572         int code = NONE;
573         bool clear = false;
574
575         if (!lt) {
576                 lt = getLyXText(bv);
577                 clear = true;
578         }
579         if (lt->selection.set()) {
580                 lt->clearSelection();
581                 code = FULL;
582         } else if (owner()) {
583                 bv->owner()->setLayout(owner()->getLyXText(bv)
584                                        ->cursor.par()->layout()->name());
585         } else
586                 bv->owner()->setLayout(bv->text->cursor.par()->layout()->name());
587         // hack for deleteEmptyParMech
588         ParagraphList::iterator first_par = paragraphs.begin();
589         if (!first_par->empty()) {
590                 lt->setCursor(first_par, 0);
591         } else if (paragraphs.size() > 1) {
592                 lt->setCursor(boost::next(first_par), 0);
593         }
594         if (clear)
595                 lt = 0;
596 #if 0
597         updateLocal(bv, code, false);
598 #else
599         if (code != NONE)
600                 setUpdateStatus(bv, code);
601 #endif
602 }
603
604
605 void InsetText::lockInset(BufferView * bv)
606 {
607         locked = true;
608         the_locking_inset = 0;
609         inset_pos = inset_x = inset_y = 0;
610         inset_boundary = false;
611         inset_par = paragraphs.end();
612         old_par = paragraphs.end();
613         bool clear = false;
614         if (!lt) {
615                 lt = getLyXText(bv);
616                 clear = true;
617         }
618         lt->setCursor(paragraphs.begin(), 0);
619         lt->clearSelection();
620         finishUndo();
621         // If the inset is empty set the language of the current font to the
622         // language to the surronding text (if different).
623         if (paragraphs.begin()->empty() && paragraphs.size() == 1 &&
624                 bv->getParentLanguage(this) != lt->current_font.language()) {
625                 LyXFont font(LyXFont::ALL_IGNORE);
626                 font.setLanguage(bv->getParentLanguage(this));
627                 setFont(bv, font, false);
628         }
629         if (clear)
630                 lt = 0;
631         int code = CURSOR;
632         if (drawFrame_ == LOCKED)
633                 code = CURSOR|DRAW_FRAME;
634         setUpdateStatus(bv, code);
635 }
636
637
638 void InsetText::lockInset(BufferView * bv, UpdatableInset * inset)
639 {
640         the_locking_inset = inset;
641         inset_x = cix(bv) - top_x + drawTextXOffset;
642         inset_y = ciy(bv) + drawTextYOffset;
643         inset_pos = cpos(bv);
644         inset_par = cpar(bv);
645         inset_boundary = cboundary(bv);
646         updateLocal(bv, CURSOR, false);
647 }
648
649
650 bool InsetText::lockInsetInInset(BufferView * bv, UpdatableInset * inset)
651 {
652         lyxerr[Debug::INSETS] << "InsetText::LockInsetInInset("
653                               << inset << "): ";
654         if (!inset)
655                 return false;
656         if (!the_locking_inset) {
657                 ParagraphList::iterator pit = paragraphs.begin();
658                 ParagraphList::iterator pend = paragraphs.end();
659
660                 int const id = inset->id();
661                 for (; pit != pend; ++pit) {
662                         InsetList::iterator it =
663                                 pit->insetlist.begin();
664                         InsetList::iterator const end =
665                                 pit->insetlist.end();
666                         for (; it != end; ++it) {
667                                 if (it->inset == inset) {
668                                         getLyXText(bv)->setCursorIntern(pit, it->pos);
669                                         lockInset(bv, inset);
670                                         return true;
671                                 }
672                                 if (it->inset->getInsetFromID(id)) {
673                                         getLyXText(bv)->setCursorIntern(pit, it->pos);
674                                         it->inset->localDispatch(FuncRequest(bv, LFUN_INSET_EDIT));
675                                         return the_locking_inset->lockInsetInInset(bv, inset);
676                                 }
677                         }
678                 }
679                 return false;
680         }
681         if (inset == cpar(bv)->getInset(cpos(bv))) {
682                 lyxerr[Debug::INSETS] << "OK" << endl;
683                 lockInset(bv, inset);
684                 return true;
685         } else if (the_locking_inset && (the_locking_inset == inset)) {
686                 if (cpar(bv) == inset_par && cpos(bv) == inset_pos) {
687                         lyxerr[Debug::INSETS] << "OK" << endl;
688                         inset_x = cix(bv) - top_x + drawTextXOffset;
689                         inset_y = ciy(bv) + drawTextYOffset;
690                 } else {
691                         lyxerr[Debug::INSETS] << "cursor.pos != inset_pos" << endl;
692                 }
693         } else if (the_locking_inset) {
694                 lyxerr[Debug::INSETS] << "MAYBE" << endl;
695                 return the_locking_inset->lockInsetInInset(bv, inset);
696         }
697         lyxerr[Debug::INSETS] << "NOT OK" << endl;
698         return false;
699 }
700
701
702 bool InsetText::unlockInsetInInset(BufferView * bv, UpdatableInset * inset,
703                                    bool lr)
704 {
705         if (!the_locking_inset)
706                 return false;
707         if (the_locking_inset == inset) {
708                 the_locking_inset->insetUnlock(bv);
709                 getLyXText(bv)->updateInset(inset);
710                 the_locking_inset = 0;
711                 if (lr)
712                         moveRightIntern(bv, true, false);
713                 old_par = paragraphs.end(); // force layout setting
714                 if (scroll())
715                         scroll(bv, 0.0F);
716                 else
717                         updateLocal(bv, CURSOR, false);
718                 return true;
719         }
720         return the_locking_inset->unlockInsetInInset(bv, inset, lr);
721 }
722
723
724 bool InsetText::updateInsetInInset(BufferView * bv, Inset * inset)
725 {
726         if (!autoBreakRows && paragraphs.size() > 1)
727                 collapseParagraphs(bv);
728
729         if (inset == this)
730                 return true;
731
732         bool clear = false;
733         if (!lt) {
734                 lt = getLyXText(bv);
735                 clear = true;
736         }
737         if (inset->owner() != this) {
738                 int ustat = CURSOR_PAR;
739                 bool found = false;
740                 UpdatableInset * tl_inset = the_locking_inset;
741                 if (tl_inset)
742                         found = tl_inset->updateInsetInInset(bv, inset);
743                 if (!found) {
744                         tl_inset = static_cast<UpdatableInset *>(inset);
745                         while(tl_inset->owner() && tl_inset->owner() != this)
746                                 tl_inset = static_cast<UpdatableInset *>(tl_inset->owner());
747                         if (!tl_inset->owner())
748                                 return false;
749                         found = tl_inset->updateInsetInInset(bv, inset);
750                         ustat = FULL;
751                 }
752                 if (found)
753                         lt->updateInset(tl_inset);
754                 if (clear)
755                         lt = 0;
756                 if (found)
757                         setUpdateStatus(bv, ustat);
758                 return found;
759         }
760         bool found = lt->updateInset(inset);
761         if (clear)
762                 lt = 0;
763         if (found) {
764                 setUpdateStatus(bv, CURSOR_PAR);
765                 if (the_locking_inset &&
766                     cpar(bv) == inset_par && cpos(bv) == inset_pos)
767                 {
768                         inset_x = cix(bv) - top_x + drawTextXOffset;
769                         inset_y = ciy(bv) + drawTextYOffset;
770                 }
771         }
772         return found;
773 }
774
775
776 void InsetText::lfunMousePress(FuncRequest const & cmd)
777 {
778         no_selection = true;
779
780         // use this to check mouse motion for selection!
781         mouse_x = cmd.x;
782         mouse_y = cmd.y;
783
784         BufferView * bv = cmd.view();
785         FuncRequest cmd1 = cmd;
786         cmd1.x -= inset_x;
787         cmd1.y -= inset_y;
788         if (!locked)
789                 lockInset(bv);
790
791         int tmp_x = cmd.x - drawTextXOffset;
792         int tmp_y = cmd.y + dim_.asc - getLyXText(bv)->top_y();
793         Inset * inset = getLyXText(bv)->checkInsetHit(tmp_x, tmp_y);
794
795         if (the_locking_inset) {
796                 if (the_locking_inset == inset) {
797                         the_locking_inset->localDispatch(cmd1);
798                         return;
799                 }
800                 // otherwise only unlock the_locking_inset
801                 the_locking_inset->insetUnlock(bv);
802                 the_locking_inset = 0;
803         }
804         if (!inset)
805                 no_selection = false;
806
807         if (bv->theLockingInset()) {
808                 if (isHighlyEditableInset(inset)) {
809                         // We just have to lock the inset before calling a
810                         // PressEvent on it!
811                         UpdatableInset * uinset = static_cast<UpdatableInset*>(inset);
812                         if (!bv->lockInset(uinset)) {
813                                 lyxerr[Debug::INSETS] << "Cannot lock inset" << endl;
814                         }
815                         inset->localDispatch(cmd1);
816                         if (the_locking_inset)
817                                 updateLocal(bv, CURSOR, false);
818                         return;
819                 }
820         }
821         if (!inset) {
822                 bool paste_internally = false;
823                 if (cmd.button() == mouse_button::button2 && getLyXText(bv)->selection.set()) {
824                         localDispatch(FuncRequest(bv, LFUN_COPY));
825                         paste_internally = true;
826                 }
827                 bool clear = false;
828                 if (!lt) {
829                         lt = getLyXText(bv);
830                         clear = true;
831                 }
832                 int old_top_y = lt->top_y();
833
834                 lt->setCursorFromCoordinates(cmd.x - drawTextXOffset,
835                                              cmd.y + dim_.asc);
836                 // set the selection cursor!
837                 lt->selection.cursor = lt->cursor;
838                 lt->cursor.x_fix(lt->cursor.x());
839
840                 if (lt->selection.set()) {
841                         lt->clearSelection();
842                         updateLocal(bv, FULL, false);
843                 } else {
844                         lt->clearSelection();
845                         updateLocal(bv, CURSOR, false);
846                 }
847
848                 if (clear)
849                         lt = 0;
850
851                 bv->owner()->setLayout(cpar(bv)->layout()->name());
852
853                 // we moved the view we cannot do mouse selection in this case!
854                 if (getLyXText(bv)->top_y() != old_top_y)
855                         no_selection = true;
856                 old_par = cpar(bv);
857                 // Insert primary selection with middle mouse
858                 // if there is a local selection in the current buffer,
859                 // insert this
860                 if (cmd.button() == mouse_button::button2) {
861                         if (paste_internally)
862                                 localDispatch(FuncRequest(bv, LFUN_PASTE));
863                         else
864                                 localDispatch(FuncRequest(bv, LFUN_PASTESELECTION, "paragraph"));
865                 }
866         } else {
867                 getLyXText(bv)->clearSelection();
868         }
869 }
870
871
872 bool InsetText::lfunMouseRelease(FuncRequest const & cmd)
873 {
874         BufferView * bv = cmd.view();
875         FuncRequest cmd1 = cmd;
876         cmd1.x -= inset_x;
877         cmd1.y -= inset_y;
878
879         no_selection = true;
880         if (the_locking_inset)
881                 return the_locking_inset->localDispatch(cmd1);
882
883         int tmp_x = cmd.x - drawTextXOffset;
884         int tmp_y = cmd.y + dim_.asc - getLyXText(bv)->top_y();
885         Inset * inset = getLyXText(bv)->checkInsetHit(tmp_x, tmp_y);
886         bool ret = false;
887         if (inset) {
888 // This code should probably be removed now. Simple insets
889 // (!highlyEditable) can actually take the localDispatch,
890 // and turn it into edit() if necessary. But we still
891 // need to deal properly with the whole relative vs.
892 // absolute mouse co-ords thing in a realiable, sensible way
893 #if 0
894                 if (isHighlyEditableInset(inset))
895                         ret = inset->localDispatch(cmd1);
896                 else {
897                         inset_x = cix(bv) - top_x + drawTextXOffset;
898                         inset_y = ciy(bv) + drawTextYOffset;
899                         cmd1.x = cmd.x - inset_x;
900                         cmd1.y = cmd.x - inset_y;
901                         inset->edit(bv, cmd1.x, cmd1.y, cmd.button());
902                         ret = true;
903                 }
904 #endif
905                 ret = inset->localDispatch(cmd1);
906                 updateLocal(bv, CURSOR_PAR, false);
907
908         }
909         return ret;
910 }
911
912
913 void InsetText::lfunMouseMotion(FuncRequest const & cmd)
914 {
915         FuncRequest cmd1 = cmd;
916         cmd1.x -= inset_x;
917         cmd1.y -= inset_y;
918
919         if (the_locking_inset) {
920                 the_locking_inset->localDispatch(cmd1);
921                 return;
922         }
923
924         if (no_selection || (mouse_x == cmd.x && mouse_y == cmd.y))
925                 return;
926
927         BufferView * bv = cmd.view();
928         bool clear = false;
929         if (!lt) {
930                 lt = getLyXText(bv);
931                 clear = true;
932         }
933         LyXCursor cur = lt->cursor;
934         lt->setCursorFromCoordinates
935                 (cmd.x - drawTextXOffset, cmd.y + dim_.asc);
936         lt->cursor.x_fix(lt->cursor.x());
937         if (cur == lt->cursor) {
938                 if (clear)
939                         lt = 0;
940                 return;
941         }
942         lt->setSelection();
943         bool flag = (lt->toggle_cursor.par() != lt->toggle_end_cursor.par() ||
944                                  lt->toggle_cursor.pos() != lt->toggle_end_cursor.pos());
945         if (clear)
946                 lt = 0;
947         if (flag) {
948                 updateLocal(bv, SELECTION, false);
949         }
950 }
951
952
953 Inset::RESULT InsetText::localDispatch(FuncRequest const & cmd)
954 {
955         BufferView * bv = cmd.view();
956
957         if (cmd.action == LFUN_INSET_EDIT) {
958                 UpdatableInset::localDispatch(cmd);
959
960                 if (!bv->lockInset(this)) {
961                         lyxerr[Debug::INSETS] << "Cannot lock inset" << endl;
962                         return DISPATCHED;
963                 }
964
965                 locked = true;
966                 the_locking_inset = 0;
967                 inset_pos = inset_x = inset_y = 0;
968                 inset_boundary = false;
969                 inset_par = paragraphs.end();
970                 old_par = paragraphs.end();
971
972                 bool clear = false;
973                 if (!lt) {
974                         lt = getLyXText(bv);
975                         clear = true;
976                 }
977
978                 if (cmd.argument.size()) {
979                         if (cmd.argument == "left")
980                                 lt->setCursor(paragraphs.begin(), 0);
981                         else {
982                                 ParagraphList::iterator it = paragraphs.begin();
983                                 ParagraphList::iterator end = paragraphs.end();
984                                 while (boost::next(it) != end)
985                                         ++it;
986                 //              int const pos = (p->size() ? p->size()-1 : p->size());
987                                 lt->setCursor(it, it->size());
988                         }
989                 } else {
990                         int tmp_y = (cmd.y < 0) ? 0 : cmd.y;
991                         // we put here -1 and not button as now the button in the
992                         // edit call should not be needed we will fix this in 1.3.x
993                         // cycle hopefully (Jug 20020509)
994                         // FIXME: GUII I've changed this to none: probably WRONG
995                         if (!checkAndActivateInset(bv, cmd.x, tmp_y, mouse_button::none)) {
996                                 lt->setCursorFromCoordinates(cmd.x - drawTextXOffset,
997                                                                         cmd.y + dim_.asc);
998                                 lt->cursor.x_fix(lt->cursor.x());
999                         }
1000                 }
1001
1002                 lt->clearSelection();
1003                 finishUndo();
1004
1005                 // If the inset is empty set the language of the current font to the
1006                 // language to the surronding text (if different).
1007                 if (paragraphs.begin()->empty() &&
1008                     paragraphs.size() == 1 &&
1009                     bv->getParentLanguage(this) != lt->current_font.language())
1010                 {
1011                         LyXFont font(LyXFont::ALL_IGNORE);
1012                         font.setLanguage(bv->getParentLanguage(this));
1013                         setFont(bv, font, false);
1014                 }
1015
1016                 if (clear)
1017                         lt = 0;
1018                 int code = CURSOR;
1019                 if (drawFrame_ == LOCKED)
1020                         code = CURSOR | DRAW_FRAME;
1021
1022                 updateLocal(bv, code, false);
1023                 // Tell the paragraph dialog that we've entered an insettext.
1024                 bv->dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1025                 return DISPATCHED;
1026         }
1027
1028
1029         switch (cmd.action) {
1030                 case LFUN_MOUSE_PRESS:
1031                         lfunMousePress(cmd);
1032                         return DISPATCHED;
1033                 case LFUN_MOUSE_MOTION:
1034                         lfunMouseMotion(cmd);
1035                         return DISPATCHED;
1036                 case LFUN_MOUSE_RELEASE:
1037                         return lfunMouseRelease(cmd) ? DISPATCHED : UNDISPATCHED;
1038                 default:
1039                         break;
1040         }
1041
1042         bool was_empty = (paragraphs.begin()->empty() &&
1043                           paragraphs.size() == 1);
1044
1045         no_selection = false;
1046         RESULT result = UpdatableInset::localDispatch(cmd);
1047         if (result != UNDISPATCHED)
1048                 return DISPATCHED;
1049
1050         result = DISPATCHED;
1051         if (cmd.action < 0 && cmd.argument.empty())
1052                 return FINISHED;
1053
1054         if (the_locking_inset) {
1055                 result = the_locking_inset->localDispatch(cmd);
1056                 if (result == DISPATCHED_NOUPDATE)
1057                         return result;
1058                 else if (result == DISPATCHED) {
1059                         updateLocal(bv, CURSOR_PAR, false);
1060                         return result;
1061                 } else if (result >= FINISHED) {
1062                         switch (result) {
1063                         case FINISHED_RIGHT:
1064                                 moveRightIntern(bv, false, false);
1065                                 result = DISPATCHED;
1066                                 break;
1067                         case FINISHED_UP:
1068                                 if ((result = moveUp(bv)) >= FINISHED) {
1069                                         updateLocal(bv, CURSOR, false);
1070                                         bv->unlockInset(this);
1071                                 }
1072                                 break;
1073                         case FINISHED_DOWN:
1074                                 if ((result = moveDown(bv)) >= FINISHED) {
1075                                         updateLocal(bv, CURSOR, false);
1076                                         bv->unlockInset(this);
1077                                 }
1078                                 break;
1079                         default:
1080                                 result = DISPATCHED;
1081                                 break;
1082                         }
1083                         the_locking_inset = 0;
1084                         updateLocal(bv, CURSOR, false);
1085                         // make sure status gets reset immediately
1086                         bv->owner()->clearMessage();
1087                         return result;
1088                 }
1089         }
1090         bool clear = false;
1091         if (!lt) {
1092                 lt = getLyXText(bv);
1093                 clear = true;
1094         }
1095         int updwhat = 0;
1096         int updflag = false;
1097
1098         // what type of update to do on a cursor movement
1099         int cursor_update = CURSOR;
1100
1101         if (lt->selection.set())
1102                 cursor_update = SELECTION;
1103
1104         switch (cmd.action) {
1105
1106         // Normal chars
1107         case LFUN_SELFINSERT:
1108                 if (bv->buffer()->isReadonly()) {
1109 //          setErrorMessage(N_("Document is read only"));
1110                         break;
1111                 }
1112                 if (!cmd.argument.empty()) {
1113                         /* Automatically delete the currently selected
1114                          * text and replace it with what is being
1115                          * typed in now. Depends on lyxrc settings
1116                          * "auto_region_delete", which defaults to
1117                          * true (on). */
1118 #if 0
1119                         // This should not be needed here and is also WRONG!
1120                         setUndo(bv, Undo::INSERT, lt->cursor.par());
1121 #endif
1122                         bv->switchKeyMap();
1123                         if (lyxrc.auto_region_delete) {
1124                                 if (lt->selection.set()) {
1125                                         lt->cutSelection(false, false);
1126                                 }
1127                         }
1128                         lt->clearSelection();
1129                         for (string::size_type i = 0; i < cmd.argument.length(); ++i) {
1130                                 bv->owner()->getIntl().getTransManager().
1131                                         TranslateAndInsert(cmd.argument[i], lt);
1132                         }
1133                 }
1134                 lt->selection.cursor = lt->cursor;
1135                 updwhat = CURSOR | CURSOR_PAR;
1136                 updflag = true;
1137                 result = DISPATCHED_NOUPDATE;
1138                 break;
1139
1140         // cursor movements that need special handling
1141
1142         case LFUN_RIGHT:
1143                 result = moveRight(bv);
1144                 finishUndo();
1145                 updwhat = cursor_update;
1146                 break;
1147         case LFUN_LEFT:
1148                 finishUndo();
1149                 result = moveLeft(bv);
1150                 updwhat = cursor_update;
1151                 break;
1152         case LFUN_DOWN:
1153                 finishUndo();
1154                 result = moveDown(bv);
1155                 updwhat = cursor_update;
1156                 break;
1157         case LFUN_UP:
1158                 finishUndo();
1159                 result = moveUp(bv);
1160                 updwhat = cursor_update;
1161                 break;
1162
1163         case LFUN_PRIOR:
1164                 if (crow(bv) == lt->rows().begin())
1165                         result = FINISHED_UP;
1166                 else {
1167                         lt->cursorPrevious();
1168                         lt->clearSelection();
1169                         result = DISPATCHED_NOUPDATE;
1170                 }
1171                 updwhat = cursor_update;
1172                 break;
1173
1174         case LFUN_NEXT:
1175                 if (boost::next(crow(bv)) == lt->rows().end())
1176                         result = FINISHED_DOWN;
1177                 else {
1178                         lt->cursorNext();
1179                         lt->clearSelection();
1180                         result = DISPATCHED_NOUPDATE;
1181                 }
1182                 updwhat = cursor_update;
1183                 break;
1184
1185         case LFUN_BACKSPACE: {
1186                 if (lt->selection.set())
1187                         lt->cutSelection(true, false);
1188                 else
1189                         lt->backspace();
1190                 updwhat = CURSOR_PAR;
1191                 updflag = true;
1192                 break;
1193         }
1194
1195         case LFUN_DELETE: {
1196                 if (lt->selection.set()) {
1197                         lt->cutSelection(true, false);
1198                 } else {
1199                         lt->Delete();
1200                 }
1201                 updwhat = CURSOR_PAR;
1202                 updflag = true;
1203                 break;
1204         }
1205
1206         case LFUN_CUT: {
1207                 lt->cutSelection(true, true);
1208                 updwhat = CURSOR_PAR;
1209                 updflag = true;
1210                 break;
1211         }
1212
1213         case LFUN_COPY:
1214                 finishUndo();
1215                 lt->copySelection();
1216                 updwhat = CURSOR_PAR;
1217                 break;
1218
1219         case LFUN_PASTESELECTION:
1220         {
1221                 string const clip(bv->getClipboard());
1222
1223                 if (clip.empty())
1224                         break;
1225                 if (cmd.argument == "paragraph") {
1226                         lt->insertStringAsParagraphs(clip);
1227                 } else {
1228                         lt->insertStringAsLines(clip);
1229                 }
1230                 // bug 393
1231                 lt->clearSelection();
1232
1233                 updwhat = CURSOR_PAR;
1234                 updflag = true;
1235                 break;
1236         }
1237
1238         case LFUN_PASTE: {
1239                 if (!autoBreakRows) {
1240                         if (CutAndPaste::nrOfParagraphs() > 1) {
1241 #ifdef WITH_WARNINGS
1242 #warning FIXME horrendously bad UI
1243 #endif
1244                                 Alert::error(_("Paste failed"), _("Cannot include more than one paragraph."));
1245                                 break;
1246                         }
1247                 }
1248
1249                 size_t sel_index = 0;
1250                 string const & arg = cmd.argument;
1251                 if (isStrUnsignedInt(arg)) {
1252                         size_t const paste_arg = strToUnsignedInt(arg);
1253 #warning FIXME Check if the arg is in the domain of available selections.
1254                         sel_index = paste_arg;
1255                 }
1256                 lt->pasteSelection(sel_index);
1257                 // bug 393
1258                 lt->clearSelection();
1259                 updwhat = CURSOR_PAR;
1260                 updflag = true;
1261                 break;
1262         }
1263
1264         case LFUN_BREAKPARAGRAPH:
1265                 if (!autoBreakRows) {
1266                         result = DISPATCHED;
1267                         break;
1268                 }
1269                 lt->breakParagraph(paragraphs, 0);
1270                 updwhat = CURSOR | FULL;
1271                 updflag = true;
1272                 break;
1273
1274         case LFUN_BREAKPARAGRAPHKEEPLAYOUT:
1275                 if (!autoBreakRows) {
1276                         result = DISPATCHED;
1277                         break;
1278                 }
1279                 lt->breakParagraph(paragraphs, 1);
1280                 updwhat = CURSOR | FULL;
1281                 updflag = true;
1282                 break;
1283
1284         case LFUN_BREAKLINE: {
1285                 if (!autoBreakRows) {
1286                         result = DISPATCHED;
1287                         break;
1288                 }
1289
1290                 lt->insertInset(new InsetNewline);
1291                 updwhat = CURSOR | CURSOR_PAR;
1292                 updflag = true;
1293                 break;
1294         }
1295
1296         case LFUN_LAYOUT:
1297                 // do not set layouts on non breakable textinsets
1298                 if (autoBreakRows) {
1299                         string cur_layout = cpar(bv)->layout()->name();
1300
1301                         // Derive layout number from given argument (string)
1302                         // and current buffer's textclass (number). */
1303                         LyXTextClass const & tclass =
1304                                 bv->buffer()->params.getLyXTextClass();
1305                         string layout = cmd.argument;
1306                         bool hasLayout = tclass.hasLayout(layout);
1307
1308                         // If the entry is obsolete, use the new one instead.
1309                         if (hasLayout) {
1310                                 string const & obs =
1311                                         tclass[layout]->obsoleted_by();
1312                                 if (!obs.empty())
1313                                         layout = obs;
1314                         }
1315
1316                         // see if we found the layout number:
1317                         if (!hasLayout) {
1318                                 FuncRequest lf(LFUN_MESSAGE, N_("Layout ") + cmd.argument + N_(" not known"));
1319                                 bv->owner()->dispatch(lf);
1320                                 break;
1321                         }
1322
1323                         if (cur_layout != layout) {
1324                                 cur_layout = layout;
1325                                 lt->setLayout(layout);
1326                                 bv->owner()->setLayout(cpar(bv)->layout()->name());
1327                                 updwhat = CURSOR_PAR;
1328                                 updflag = true;
1329                         }
1330                 } else {
1331                         // reset the layout box
1332                         bv->owner()->setLayout(cpar(bv)->layout()->name());
1333                 }
1334                 break;
1335         case LFUN_PARAGRAPH_SPACING:
1336                 // This one is absolutely not working. When fiddling with this
1337                 // it also seems to me that the paragraphs inside the insettext
1338                 // inherit bufferparams/paragraphparams in a strange way. (Lgb)
1339                 // FIXME: how old is this comment ? ...
1340         {
1341                 ParagraphList::iterator pit = lt->cursor.par();
1342                 Spacing::Space cur_spacing = pit->params().spacing().getSpace();
1343                 float cur_value = 1.0;
1344                 if (cur_spacing == Spacing::Other) {
1345                         cur_value = pit->params().spacing().getValue();
1346                 }
1347
1348                 istringstream istr(STRCONV(cmd.argument));
1349                 string tmp;
1350                 istr >> tmp;
1351                 Spacing::Space new_spacing = cur_spacing;
1352                 float new_value = cur_value;
1353                 if (tmp.empty()) {
1354                         lyxerr << "Missing argument to `paragraph-spacing'"
1355                                    << endl;
1356                 } else if (tmp == "single") {
1357                         new_spacing = Spacing::Single;
1358                 } else if (tmp == "onehalf") {
1359                         new_spacing = Spacing::Onehalf;
1360                 } else if (tmp == "double") {
1361                         new_spacing = Spacing::Double;
1362                 } else if (tmp == "other") {
1363                         new_spacing = Spacing::Other;
1364                         float tmpval = 0.0;
1365                         istr >> tmpval;
1366                         lyxerr << "new_value = " << tmpval << endl;
1367                         if (tmpval != 0.0)
1368                                 new_value = tmpval;
1369                 } else if (tmp == "default") {
1370                         new_spacing = Spacing::Default;
1371                 } else {
1372                         lyxerr << _("Unknown spacing argument: ")
1373                                    << cmd.argument << endl;
1374                 }
1375                 if (cur_spacing != new_spacing || cur_value != new_value) {
1376                         pit->params().spacing(Spacing(new_spacing, new_value));
1377                         updwhat = CURSOR_PAR;
1378                         updflag = true;
1379                 }
1380         }
1381         break;
1382
1383         // These need to do repaints but don't require
1384         // special handling otherwise. A *lot* of the
1385         // above could probably be done similarly ...
1386
1387         case LFUN_HOME:
1388         case LFUN_END:
1389         case LFUN_WORDLEFT:
1390         case LFUN_WORDRIGHT:
1391         // these two are really unhandled ...
1392         case LFUN_ENDBUF:
1393         case LFUN_BEGINNINGBUF:
1394                 updwhat = cursor_update;
1395                 if (!bv->dispatch(cmd))
1396                         result = UNDISPATCHED;
1397                 break;
1398
1399         case LFUN_RIGHTSEL:
1400         case LFUN_UPSEL:
1401         case LFUN_DOWNSEL:
1402         case LFUN_LEFTSEL:
1403         case LFUN_HOMESEL:
1404         case LFUN_ENDSEL:
1405         case LFUN_WORDLEFTSEL:
1406         case LFUN_WORDRIGHTSEL:
1407                 updwhat = SELECTION;
1408
1409                 // fallthrough
1410
1411         default:
1412                 if (!bv->dispatch(cmd))
1413                         result = UNDISPATCHED;
1414                 break;
1415         }
1416
1417         if (clear)
1418                 lt = 0;
1419         if (updwhat > 0)
1420                 updateLocal(bv, updwhat, updflag);
1421         /// If the action has deleted all text in the inset, we need to change the
1422         // language to the language of the surronding text.
1423         if (!was_empty && paragraphs.begin()->empty() &&
1424             paragraphs.size() == 1) {
1425                 LyXFont font(LyXFont::ALL_IGNORE);
1426                 font.setLanguage(bv->getParentLanguage(this));
1427                 setFont(bv, font, false);
1428         }
1429
1430         if (result >= FINISHED)
1431                 bv->unlockInset(this);
1432
1433         if (result == DISPATCHED_NOUPDATE && (need_update & FULL))
1434                 result = DISPATCHED;
1435         return result;
1436 }
1437
1438
1439 int InsetText::latex(Buffer const * buf, ostream & os,
1440                      LatexRunParams const & runparams) const
1441 {
1442         TexRow texrow;
1443         latexParagraphs(buf, paragraphs, os, texrow, runparams);
1444         return texrow.rows();
1445 }
1446
1447
1448 int InsetText::ascii(Buffer const * buf, ostream & os, int linelen) const
1449 {
1450         unsigned int lines = 0;
1451
1452         ParagraphList::const_iterator beg = paragraphs.begin();
1453         ParagraphList::const_iterator end = paragraphs.end();
1454         ParagraphList::const_iterator it = beg;
1455         for (; it != end; ++it) {
1456                 string const tmp = buf->asciiParagraph(*it, linelen, it == beg);
1457                 lines += lyx::count(tmp.begin(), tmp.end(), '\n');
1458                 os << tmp;
1459         }
1460         return lines;
1461 }
1462
1463
1464 int InsetText::docbook(Buffer const * buf, ostream & os, bool mixcont) const
1465 {
1466         unsigned int lines = 0;
1467
1468         vector<string> environment_stack(10);
1469         vector<string> environment_inner(10);
1470
1471         int const command_depth = 0;
1472         string item_name;
1473
1474         Paragraph::depth_type depth = 0; // paragraph depth
1475
1476         ParagraphList::iterator pit = const_cast<ParagraphList&>(paragraphs).begin();
1477         ParagraphList::iterator pend = const_cast<ParagraphList&>(paragraphs).end();
1478
1479         for (; pit != pend; ++pit) {
1480                 string sgmlparam;
1481                 int desc_on = 0; // description mode
1482
1483                 LyXLayout_ptr const & style = pit->layout();
1484
1485                 // environment tag closing
1486                 for (; depth > pit->params().depth(); --depth) {
1487                         if (environment_inner[depth] != "!-- --") {
1488                                 item_name = "listitem";
1489                                 lines += sgml::closeTag(os, command_depth + depth, mixcont, item_name);
1490                                 if (environment_inner[depth] == "varlistentry")
1491                                         lines += sgml::closeTag(os, depth+command_depth, mixcont, environment_inner[depth]);
1492                         }
1493                         lines += sgml::closeTag(os, depth + command_depth, mixcont, environment_stack[depth]);
1494                         environment_stack[depth].erase();
1495                         environment_inner[depth].erase();
1496                 }
1497
1498                 if (depth == pit->params().depth()
1499                    && environment_stack[depth] != style->latexname()
1500                    && !environment_stack[depth].empty()) {
1501                         if (environment_inner[depth] != "!-- --") {
1502                                 item_name= "listitem";
1503                                 lines += sgml::closeTag(os, command_depth+depth, mixcont, item_name);
1504                                 if (environment_inner[depth] == "varlistentry")
1505                                         lines += sgml::closeTag(os, depth + command_depth, mixcont, environment_inner[depth]);
1506                         }
1507
1508                         lines += sgml::closeTag(os, depth + command_depth, mixcont, environment_stack[depth]);
1509
1510                         environment_stack[depth].erase();
1511                         environment_inner[depth].erase();
1512                 }
1513
1514                 // Write opening SGML tags.
1515                 switch (style->latextype) {
1516                 case LATEX_PARAGRAPH:
1517                         lines += sgml::openTag(os, depth + command_depth, mixcont, style->latexname());
1518                         break;
1519
1520                 case LATEX_COMMAND:
1521                         buf->parseError(ErrorItem(_("Error"), _("LatexType Command not allowed here.\n"), pit->id(), 0, pit->size()));
1522                         return -1;
1523                         break;
1524
1525                 case LATEX_ENVIRONMENT:
1526                 case LATEX_ITEM_ENVIRONMENT:
1527                         if (depth < pit->params().depth()) {
1528                                 depth = pit->params().depth();
1529                                 environment_stack[depth].erase();
1530                         }
1531
1532                         if (environment_stack[depth] != style->latexname()) {
1533                                 if (environment_stack.size() == depth + 1) {
1534                                         environment_stack.push_back("!-- --");
1535                                         environment_inner.push_back("!-- --");
1536                                 }
1537                                 environment_stack[depth] = style->latexname();
1538                                 environment_inner[depth] = "!-- --";
1539                                 lines += sgml::openTag(os, depth + command_depth, mixcont, environment_stack[depth]);
1540                         } else {
1541                                 if (environment_inner[depth] != "!-- --") {
1542                                         item_name= "listitem";
1543                                         lines += sgml::closeTag(os, command_depth + depth, mixcont, item_name);
1544                                         if (environment_inner[depth] == "varlistentry")
1545                                                 lines += sgml::closeTag(os, depth + command_depth, mixcont, environment_inner[depth]);
1546                                 }
1547                         }
1548
1549                         if (style->latextype == LATEX_ENVIRONMENT) {
1550                                 if (!style->latexparam().empty()) {
1551                                         if (style->latexparam() == "CDATA")
1552                                                 os << "<![CDATA[";
1553                                         else
1554                                           lines += sgml::openTag(os, depth + command_depth, mixcont, style->latexparam());
1555                                 }
1556                                 break;
1557                         }
1558
1559                         desc_on = (style->labeltype == LABEL_MANUAL);
1560
1561                         environment_inner[depth] = desc_on ? "varlistentry" : "listitem";
1562                         lines += sgml::openTag(os, depth + 1 + command_depth, mixcont, environment_inner[depth]);
1563
1564                         item_name = desc_on ? "term" : "para";
1565                         lines += sgml::openTag(os, depth + 1 + command_depth, mixcont, item_name);
1566
1567                         break;
1568                 default:
1569                         lines += sgml::openTag(os, depth + command_depth, mixcont, style->latexname());
1570                         break;
1571                 }
1572
1573                 buf->simpleDocBookOnePar(os, pit, desc_on, depth + 1 + command_depth);
1574
1575                 string end_tag;
1576                 // write closing SGML tags
1577                 switch (style->latextype) {
1578                 case LATEX_ENVIRONMENT:
1579                         if (!style->latexparam().empty()) {
1580                                 if (style->latexparam() == "CDATA")
1581                                         os << "]]>";
1582                                 else
1583                                         lines += sgml::closeTag(os, depth + command_depth, mixcont, style->latexparam());
1584                         }
1585                         break;
1586                 case LATEX_ITEM_ENVIRONMENT:
1587                         if (desc_on == 1) break;
1588                         end_tag= "para";
1589                         lines += sgml::closeTag(os, depth + 1 + command_depth, mixcont, end_tag);
1590                         break;
1591                 case LATEX_PARAGRAPH:
1592                         lines += sgml::closeTag(os, depth + command_depth, mixcont, style->latexname());
1593                         break;
1594                 default:
1595                         lines += sgml::closeTag(os, depth + command_depth, mixcont, style->latexname());
1596                         break;
1597                 }
1598         }
1599
1600         // Close open tags
1601         for (int d = depth; d >= 0; --d) {
1602                 if (!environment_stack[depth].empty()) {
1603                         if (environment_inner[depth] != "!-- --") {
1604                                 item_name = "listitem";
1605                                 lines += sgml::closeTag(os, command_depth + depth, mixcont, item_name);
1606                                if (environment_inner[depth] == "varlistentry")
1607                                        lines += sgml::closeTag(os, depth + command_depth, mixcont, environment_inner[depth]);
1608                         }
1609
1610                         lines += sgml::closeTag(os, depth + command_depth, mixcont, environment_stack[depth]);
1611                 }
1612         }
1613
1614         return lines;
1615 }
1616
1617
1618 void InsetText::validate(LaTeXFeatures & features) const
1619 {
1620         for_each(paragraphs.begin(), paragraphs.end(),
1621                  boost::bind(&Paragraph::validate, _1, boost::ref(features)));
1622 }
1623
1624
1625 void InsetText::getCursor(BufferView & bv, int & x, int & y) const
1626 {
1627         if (the_locking_inset) {
1628                 the_locking_inset->getCursor(bv, x, y);
1629                 return;
1630         }
1631         x = cx(&bv);
1632         y = cy(&bv) + InsetText::y();
1633 }
1634
1635
1636 void InsetText::getCursorPos(BufferView * bv, int & x, int & y) const
1637 {
1638         if (the_locking_inset) {
1639                 the_locking_inset->getCursorPos(bv, x, y);
1640                 return;
1641         }
1642         x = cx(bv) - top_x - TEXT_TO_INSET_OFFSET;
1643         y = cy(bv) - TEXT_TO_INSET_OFFSET;
1644 }
1645
1646
1647 int InsetText::insetInInsetY() const
1648 {
1649         if (!the_locking_inset)
1650                 return 0;
1651
1652         return (inset_y + the_locking_inset->insetInInsetY());
1653 }
1654
1655
1656 void InsetText::fitInsetCursor(BufferView * bv) const
1657 {
1658         if (the_locking_inset) {
1659                 the_locking_inset->fitInsetCursor(bv);
1660                 return;
1661         }
1662         LyXFont const font =
1663                 getLyXText(bv)->getFont(bv->buffer(), cpar(bv), cpos(bv));
1664
1665         int const asc = font_metrics::maxAscent(font);
1666         int const desc = font_metrics::maxDescent(font);
1667
1668         if (bv->fitLockedInsetCursor(cx(bv), cy(bv), asc, desc))
1669                 need_update |= FULL;
1670 }
1671
1672
1673 Inset::RESULT
1674 InsetText::moveRight(BufferView * bv, bool activate_inset, bool selecting)
1675 {
1676         if (getLyXText(bv)->cursor.par()->isRightToLeftPar(bv->buffer()->params))
1677                 return moveLeftIntern(bv, false, activate_inset, selecting);
1678         else
1679                 return moveRightIntern(bv, true, activate_inset, selecting);
1680 }
1681
1682
1683 Inset::RESULT
1684 InsetText::moveLeft(BufferView * bv, bool activate_inset, bool selecting)
1685 {
1686         if (getLyXText(bv)->cursor.par()->isRightToLeftPar(bv->buffer()->params))
1687                 return moveRightIntern(bv, true, activate_inset, selecting);
1688         else
1689                 return moveLeftIntern(bv, false, activate_inset, selecting);
1690 }
1691
1692
1693 Inset::RESULT
1694 InsetText::moveRightIntern(BufferView * bv, bool front,
1695                            bool activate_inset, bool selecting)
1696 {
1697         LyXText * text = getLyXText(bv);
1698
1699         ParagraphList::iterator c_par = cpar(bv);
1700
1701         if (boost::next(c_par) == paragraphs.end() &&
1702             (cpos(bv) >= c_par->size()))
1703                 return FINISHED_RIGHT;
1704         if (activate_inset && checkAndActivateInset(bv, front))
1705                 return DISPATCHED;
1706         text->cursorRight(bv);
1707         if (!selecting)
1708                 text->clearSelection();
1709         return DISPATCHED_NOUPDATE;
1710 }
1711
1712
1713 Inset::RESULT
1714 InsetText::moveLeftIntern(BufferView * bv, bool front,
1715                           bool activate_inset, bool selecting)
1716 {
1717         LyXText * text = getLyXText(bv);
1718
1719         if (cpar(bv) == paragraphs.begin() && (cpos(bv) <= 0))
1720                 return FINISHED;
1721         text->cursorLeft(bv);
1722         if (!selecting)
1723                 text->clearSelection();
1724         if (activate_inset && checkAndActivateInset(bv, front))
1725                 return DISPATCHED;
1726         return DISPATCHED_NOUPDATE;
1727 }
1728
1729
1730 Inset::RESULT InsetText::moveUp(BufferView * bv)
1731 {
1732         if (crow(bv) == getLyXText(bv)->rows().begin())
1733                 return FINISHED_UP;
1734         getLyXText(bv)->cursorUp(bv);
1735         getLyXText(bv)->clearSelection();
1736         return DISPATCHED_NOUPDATE;
1737 }
1738
1739
1740 Inset::RESULT InsetText::moveDown(BufferView * bv)
1741 {
1742         if (boost::next(crow(bv)) == getLyXText(bv)->rows().end())
1743                 return FINISHED_DOWN;
1744         getLyXText(bv)->cursorDown(bv);
1745         getLyXText(bv)->clearSelection();
1746         return DISPATCHED_NOUPDATE;
1747 }
1748
1749
1750 bool InsetText::insertInset(BufferView * bv, Inset * inset)
1751 {
1752         if (the_locking_inset) {
1753                 if (the_locking_inset->insetAllowed(inset))
1754                         return the_locking_inset->insertInset(bv, inset);
1755                 return false;
1756         }
1757         inset->setOwner(this);
1758         getLyXText(bv)->insertInset(inset);
1759         bv->fitCursor();
1760         updateLocal(bv, CURSOR_PAR|CURSOR, true);
1761         return true;
1762 }
1763
1764
1765 bool InsetText::insetAllowed(Inset::Code code) const
1766 {
1767         // in_insetAllowed is a really gross hack,
1768         // to allow us to call the owner's insetAllowed
1769         // without stack overflow, which can happen
1770         // when the owner uses InsetCollapsable::insetAllowed()
1771         bool ret = true;
1772         if (in_insetAllowed)
1773                 return ret;
1774         in_insetAllowed = true;
1775         if (the_locking_inset)
1776                 ret = the_locking_inset->insetAllowed(code);
1777         else if (owner())
1778                 ret = owner()->insetAllowed(code);
1779         in_insetAllowed = false;
1780         return ret;
1781 }
1782
1783
1784 UpdatableInset * InsetText::getLockingInset() const
1785 {
1786         return the_locking_inset ? the_locking_inset->getLockingInset() :
1787                 const_cast<InsetText *>(this);
1788 }
1789
1790
1791 UpdatableInset * InsetText::getFirstLockingInsetOfType(Inset::Code c)
1792 {
1793         if (c == lyxCode())
1794                 return this;
1795         if (the_locking_inset)
1796                 return the_locking_inset->getFirstLockingInsetOfType(c);
1797         return 0;
1798 }
1799
1800
1801 bool InsetText::showInsetDialog(BufferView * bv) const
1802 {
1803         if (the_locking_inset)
1804                 return the_locking_inset->showInsetDialog(bv);
1805         return false;
1806 }
1807
1808
1809 void InsetText::getLabelList(std::vector<string> & list) const
1810 {
1811         ParagraphList::const_iterator pit = paragraphs.begin();
1812         ParagraphList::const_iterator pend = paragraphs.end();
1813         for (; pit != pend; ++pit) {
1814                 InsetList::const_iterator beg = pit->insetlist.begin();
1815                 InsetList::const_iterator end = pit->insetlist.end();
1816                 for (; beg != end; ++beg)
1817                         beg->inset->getLabelList(list);
1818         }
1819 }
1820
1821
1822 void InsetText::setFont(BufferView * bv, LyXFont const & font, bool toggleall,
1823                         bool selectall)
1824 {
1825         if (the_locking_inset) {
1826                 the_locking_inset->setFont(bv, font, toggleall, selectall);
1827                 return;
1828         }
1829
1830         if ((paragraphs.size() == 1 && paragraphs.begin()->empty())
1831             || cpar(bv)->empty()) {
1832                 getLyXText(bv)->setFont(font, toggleall);
1833                 return;
1834         }
1835
1836         bool clear = false;
1837         if (!lt) {
1838                 lt = getLyXText(bv);
1839                 clear = true;
1840         }
1841
1842         if (lt->selection.set()) {
1843                 setUndo(bv, Undo::EDIT, lt->cursor.par());
1844         }
1845
1846         if (selectall)
1847                 selectAll(bv);
1848
1849         lt->toggleFree(font, toggleall);
1850
1851         if (selectall)
1852                 lt->clearSelection();
1853
1854         bv->fitCursor();
1855
1856         bool flag = (selectall || lt->selection.set());
1857
1858         if (clear)
1859                 lt = 0;
1860
1861         if (flag)
1862                 updateLocal(bv, FULL, true);
1863         else
1864                 updateLocal(bv, CURSOR_PAR, true);
1865 }
1866
1867
1868 bool InsetText::checkAndActivateInset(BufferView * bv, bool front)
1869 {
1870         if (cpar(bv)->isInset(cpos(bv))) {
1871                 Inset * inset =
1872                         static_cast<UpdatableInset*>(cpar(bv)->getInset(cpos(bv)));
1873                 if (!isHighlyEditableInset(inset))
1874                         return false;
1875                 FuncRequest cmd(bv, LFUN_INSET_EDIT, front ? "left" : "right");
1876                 inset->localDispatch(cmd);
1877                 if (!the_locking_inset)
1878                         return false;
1879                 updateLocal(bv, CURSOR, false);
1880                 return true;
1881         }
1882         return false;
1883 }
1884
1885
1886 bool InsetText::checkAndActivateInset(BufferView * bv, int x, int y,
1887                                       mouse_button::state button)
1888 {
1889         x -= drawTextXOffset;
1890         int dummyx = x;
1891         int dummyy = y + dim_.asc;
1892         Inset * inset = getLyXText(bv)->checkInsetHit(dummyx, dummyy);
1893         // we only do the edit() call if the inset was hit by the mouse
1894         // or if it is a highly editable inset. So we should call this
1895         // function from our own edit with button < 0.
1896         // FIXME: GUII jbl. I've changed this to ::none for now which is probably
1897         // WRONG
1898         if (button == mouse_button::none && !isHighlyEditableInset(inset))
1899                 return false;
1900
1901         if (inset) {
1902                 if (x < 0)
1903                         x = dim_.wid;
1904                 if (y < 0)
1905                         y = dim_.des;
1906                 inset_x = cix(bv) - top_x + drawTextXOffset;
1907                 inset_y = ciy(bv) + drawTextYOffset;
1908                 FuncRequest cmd(bv, LFUN_INSET_EDIT, x - inset_x, y - inset_y, button);
1909                 inset->localDispatch(cmd);
1910                 if (!the_locking_inset)
1911                         return false;
1912                 updateLocal(bv, CURSOR, false);
1913                 return true;
1914         }
1915         return false;
1916 }
1917
1918
1919 int InsetText::getMaxWidth(BufferView * bv, UpdatableInset const * inset) const
1920 {
1921 #if 0
1922         int w = UpdatableInset::getMaxWidth(bv, inset);
1923         if (w < 0) {
1924                 return -1;
1925         }
1926         if (owner()) {
1927                 w = w - top_x + owner()->x();
1928                 return w;
1929         }
1930         w -= (2 * TEXT_TO_INSET_OFFSET);
1931         return w - top_x;
1932 #else
1933         return UpdatableInset::getMaxWidth(bv, inset);
1934 #endif
1935 }
1936
1937
1938 void InsetText::setParagraphData(ParagraphList const & plist)
1939 {
1940         // we have to unlock any locked inset otherwise we're in troubles
1941         the_locking_inset = 0;
1942
1943         // But it it makes no difference that is a lot better.
1944 #warning FIXME.
1945         // See if this can be simplified when std::list is in effect.
1946         paragraphs.clear();
1947
1948         ParagraphList::const_iterator it = plist.begin();
1949         ParagraphList::const_iterator end = plist.end();
1950         for (; it != end; ++it) {
1951                 paragraphs.push_back(*it);
1952                 Paragraph & tmp = paragraphs.back();
1953                 tmp.setInsetOwner(this);
1954         }
1955
1956         reinitLyXText();
1957         need_update = INIT;
1958 }
1959
1960
1961 void InsetText::markNew(bool track_changes)
1962 {
1963         ParagraphList::iterator pit = paragraphs.begin();
1964         ParagraphList::iterator pend = paragraphs.end();
1965         for (; pit != pend; ++pit) {
1966                 if (track_changes) {
1967                         pit->trackChanges();
1968                 } else {
1969                         // no-op when not tracking
1970                         pit->cleanChanges();
1971                 }
1972         }
1973 }
1974
1975
1976 void InsetText::setText(string const & data, LyXFont const & font)
1977 {
1978         clear(false);
1979         for (unsigned int i = 0; i < data.length(); ++i)
1980                 paragraphs.begin()->insertChar(i, data[i], font);
1981         reinitLyXText();
1982 }
1983
1984
1985 void InsetText::setAutoBreakRows(bool flag)
1986 {
1987         if (flag != autoBreakRows) {
1988                 autoBreakRows = flag;
1989                 if (!flag)
1990                         removeNewlines();
1991                 need_update = INIT;
1992         }
1993 }
1994
1995
1996 void InsetText::setDrawFrame(BufferView * bv, DrawFrame how)
1997 {
1998         if (how != drawFrame_) {
1999                 drawFrame_ = how;
2000                 if (bv)
2001                         updateLocal(bv, DRAW_FRAME, false);
2002         }
2003 }
2004
2005
2006 void InsetText::setFrameColor(BufferView * bv, LColor::color col)
2007 {
2008         if (frame_color != col) {
2009                 frame_color = col;
2010                 if (bv)
2011                         updateLocal(bv, DRAW_FRAME, false);
2012         }
2013 }
2014
2015
2016 int InsetText::cx(BufferView * bv) const
2017 {
2018         // we do nothing dangerous so we use a local cache
2019         LyXText * llt = getLyXText(bv);
2020         int x = llt->cursor.x() + top_x + TEXT_TO_INSET_OFFSET;
2021         if (the_locking_inset) {
2022                 LyXFont font = llt->getFont(bv->buffer(), llt->cursor.par(),
2023                                             llt->cursor.pos());
2024                 if (font.isVisibleRightToLeft())
2025                         x -= the_locking_inset->width(bv, font);
2026         }
2027         return x;
2028 }
2029
2030
2031 int InsetText::cix(BufferView * bv) const
2032 {
2033         // we do nothing dangerous so we use a local cache
2034         LyXText * llt = getLyXText(bv);
2035         int x = llt->cursor.ix() + top_x + TEXT_TO_INSET_OFFSET;
2036         if (the_locking_inset) {
2037                 LyXFont font = llt->getFont(bv->buffer(), llt->cursor.par(),
2038                                             llt->cursor.pos());
2039                 if (font.isVisibleRightToLeft())
2040                         x -= the_locking_inset->width(bv, font);
2041         }
2042         return x;
2043 }
2044
2045
2046 int InsetText::cy(BufferView * bv) const
2047 {
2048         LyXFont font;
2049         return getLyXText(bv)->cursor.y() - ascent(bv, font) + TEXT_TO_INSET_OFFSET;
2050 }
2051
2052
2053 int InsetText::ciy(BufferView * bv) const
2054 {
2055         LyXFont font;
2056         return getLyXText(bv)->cursor.iy() - ascent(bv, font) + TEXT_TO_INSET_OFFSET;
2057 }
2058
2059
2060 pos_type InsetText::cpos(BufferView * bv) const
2061 {
2062         return getLyXText(bv)->cursor.pos();
2063 }
2064
2065
2066 ParagraphList::iterator InsetText::cpar(BufferView * bv) const
2067 {
2068         return getLyXText(bv)->cursor.par();
2069 }
2070
2071
2072 bool InsetText::cboundary(BufferView * bv) const
2073 {
2074         return getLyXText(bv)->cursor.boundary();
2075 }
2076
2077
2078 RowList::iterator InsetText::crow(BufferView * bv) const
2079 {
2080         return getLyXText(bv)->cursorRow();
2081 }
2082
2083
2084 LyXText * InsetText::getLyXText(BufferView const * lbv,
2085                                 bool const recursive) const
2086 {
2087         if (cached_bview == lbv) {
2088                 if (recursive && the_locking_inset)
2089                         return the_locking_inset->getLyXText(lbv, true);
2090                 LyXText * lt = cached_text.get();
2091                 lyx::Assert(lt && lt->rows().begin()->par() == const_cast<ParagraphList&>(paragraphs).begin());
2092                 return lt;
2093         }
2094         // Super UGLY! (Lgb)
2095         BufferView * bv = const_cast<BufferView *>(lbv);
2096
2097         cached_bview = bv;
2098         Cache::iterator it = cache.find(bv);
2099
2100         if (it != cache.end()) {
2101                 if (do_reinit) {
2102                         reinitLyXText();
2103                 } else if (do_resize) {
2104                         resizeLyXText(do_resize);
2105                 } else {
2106                         if (lt || !it->second.remove) {
2107                                 lyx::Assert(it->second.text.get());
2108                                 cached_text = it->second.text;
2109                                 if (recursive && the_locking_inset) {
2110                                         return the_locking_inset->getLyXText(bv, true);
2111                                 }
2112                                 return cached_text.get();
2113                         } else if (it->second.remove) {
2114                                 if (locked) {
2115                                         saveLyXTextState(it->second.text.get());
2116                                 } else {
2117                                         sstate.lpar = const_cast<ParagraphList&>(paragraphs).end();
2118                                 }
2119                         }
2120                         //
2121                         // when we have to reinit the existing LyXText!
2122                         //
2123                         it->second.text->init(bv);
2124                         restoreLyXTextState(it->second.text.get());
2125                         it->second.remove = false;
2126                 }
2127                 cached_text = it->second.text;
2128                 if (the_locking_inset && recursive) {
2129                         return the_locking_inset->getLyXText(bv);
2130                 }
2131                 return cached_text.get();
2132         }
2133         ///
2134         // we are here only if we don't have a BufferView * in the cache!!!
2135         ///
2136         cached_text.reset(new LyXText(bv, const_cast<InsetText *>(this)));
2137         cached_text->init(bv);
2138         restoreLyXTextState(cached_text.get());
2139
2140         cache.insert(make_pair(bv, cached_text));
2141
2142         if (the_locking_inset && recursive) {
2143                 return the_locking_inset->getLyXText(bv);
2144         }
2145         return cached_text.get();
2146 }
2147
2148
2149 void InsetText::deleteLyXText(BufferView * bv, bool recursive) const
2150 {
2151         cached_bview = 0;
2152
2153         Cache::iterator it = cache.find(bv);
2154
2155         if (it == cache.end()) {
2156                 return;
2157         }
2158
2159         lyx::Assert(it->second.text.get());
2160
2161         it->second.remove = true;
2162         if (recursive) {
2163                 /// then remove all LyXText in text-insets
2164                 for_each(const_cast<ParagraphList&>(paragraphs).begin(),
2165                          const_cast<ParagraphList&>(paragraphs).end(),
2166                          boost::bind(&Paragraph::deleteInsetsLyXText, _1, bv));
2167         }
2168 }
2169
2170
2171 void InsetText::resizeLyXText(BufferView * bv, bool force) const
2172 {
2173         if (lt) {
2174                 // we cannot resize this because we are in use!
2175                 // so do this on the next possible getLyXText()
2176                 do_resize = bv;
2177                 return;
2178         }
2179         do_resize = 0;
2180         if (paragraphs.size() == 1 && paragraphs.begin()->empty()) {
2181                 // no data, resize not neccessary!
2182                 // we have to do this as a fixed width may have changed!
2183                 LyXText * t = getLyXText(bv);
2184                 saveLyXTextState(t);
2185                 t->init(bv, true);
2186                 restoreLyXTextState(t);
2187                 return;
2188         }
2189
2190         // one endless line, resize normally not necessary
2191         if (!force && getMaxWidth(bv, this) < 0)
2192                 return;
2193
2194         Cache::iterator it = cache.find(bv);
2195         if (it == cache.end()) {
2196                 return;
2197         }
2198
2199         lyx::Assert(it->second.text.get());
2200
2201         LyXText * t = it->second.text.get();
2202         saveLyXTextState(t);
2203
2204         for_each(const_cast<ParagraphList&>(paragraphs).begin(),
2205                  const_cast<ParagraphList&>(paragraphs).end(),
2206                  boost::bind(&Paragraph::resizeInsetsLyXText, _1, bv));
2207
2208         t->init(bv, true);
2209         restoreLyXTextState(t);
2210
2211         if (the_locking_inset) {
2212                 inset_x = cix(bv) - top_x + drawTextXOffset;
2213                 inset_y = ciy(bv) + drawTextYOffset;
2214         }
2215
2216         t->top_y(bv->screen().topCursorVisible(t));
2217         if (!owner()) {
2218                 const_cast<InsetText*>(this)->updateLocal(bv, FULL, false);
2219                 // this will scroll the screen such that the cursor becomes visible
2220                 bv->updateScrollbar();
2221         } else {
2222                 need_update |= FULL;
2223         }
2224 }
2225
2226
2227 void InsetText::reinitLyXText() const
2228 {
2229         if (lt) {
2230                 // we cannot resize this because we are in use!
2231                 // so do this on the next possible getLyXText()
2232                 do_reinit = true;
2233                 return;
2234         }
2235         do_reinit = false;
2236         do_resize = 0;
2237         for (Cache::iterator it = cache.begin(); it != cache.end(); ++it) {
2238                 lyx::Assert(it->second.text.get());
2239
2240                 LyXText * t = it->second.text.get();
2241                 BufferView * bv = it->first;
2242
2243                 saveLyXTextState(t);
2244
2245                 for_each(const_cast<ParagraphList&>(paragraphs).begin(),
2246                          const_cast<ParagraphList&>(paragraphs).end(),
2247                          boost::bind(&Paragraph::resizeInsetsLyXText, _1, bv));
2248
2249                 t->init(bv, true);
2250                 restoreLyXTextState(t);
2251                 if (the_locking_inset) {
2252                         inset_x = cix(bv) - top_x + drawTextXOffset;
2253                         inset_y = ciy(bv) + drawTextYOffset;
2254                 }
2255                 t->top_y(bv->screen().topCursorVisible(t));
2256                 if (!owner()) {
2257                         const_cast<InsetText*>(this)->updateLocal(bv, FULL, false);
2258                         // this will scroll the screen such that the cursor becomes visible
2259                         bv->updateScrollbar();
2260                 } else {
2261                         need_update = FULL;
2262                 }
2263         }
2264 }
2265
2266
2267 void InsetText::removeNewlines()
2268 {
2269         bool changed = false;
2270
2271         ParagraphList::iterator it = paragraphs.begin();
2272         ParagraphList::iterator end = paragraphs.end();
2273         for (; it != end; ++it) {
2274                 for (int i = 0; i < it->size(); ++i) {
2275                         if (it->isNewline(i)) {
2276                                 changed = true;
2277                                 it->erase(i);
2278                         }
2279                 }
2280         }
2281         if (changed)
2282                 reinitLyXText();
2283 }
2284
2285
2286 bool InsetText::nodraw() const
2287 {
2288         if (the_locking_inset)
2289                 return the_locking_inset->nodraw();
2290         return UpdatableInset::nodraw();
2291 }
2292
2293
2294 int InsetText::scroll(bool recursive) const
2295 {
2296         int sx = UpdatableInset::scroll(false);
2297
2298         if (recursive && the_locking_inset)
2299                 sx += the_locking_inset->scroll(recursive);
2300
2301         return sx;
2302 }
2303
2304
2305 void InsetText::selectAll(BufferView * bv)
2306 {
2307         getLyXText(bv)->cursorTop();
2308         getLyXText(bv)->selection.cursor = getLyXText(bv)->cursor;
2309         getLyXText(bv)->cursorBottom();
2310         getLyXText(bv)->setSelection();
2311 }
2312
2313
2314 void InsetText::clearSelection(BufferView * bv)
2315 {
2316         getLyXText(bv)->clearSelection();
2317 }
2318
2319
2320 void InsetText::clearInset(BufferView * bv, int start_x, int baseline) const
2321 {
2322         Painter & pain = bv->painter();
2323         int w = dim_.wid;
2324         int h = dim_.asc + dim_.des;
2325         int ty = baseline - dim_.asc;
2326
2327         if (ty < 0) {
2328                 h += ty;
2329                 ty = 0;
2330         }
2331         if ((ty + h) > pain.paperHeight())
2332                 h = pain.paperHeight();
2333         if ((top_x + drawTextXOffset + w) > pain.paperWidth())
2334                 w = pain.paperWidth();
2335         pain.fillRectangle(start_x + 1, ty + 1, w - 3, h - 1, backgroundColor());
2336         need_update = FULL;
2337 }
2338
2339
2340 ParagraphList * InsetText::getParagraphs(int i) const
2341 {
2342         return (i == 0) ? const_cast<ParagraphList*>(&paragraphs) : 0;
2343 }
2344
2345
2346 LyXCursor const & InsetText::cursor(BufferView * bv) const
2347 {
2348         if (the_locking_inset)
2349                 return the_locking_inset->cursor(bv);
2350         return getLyXText(bv)->cursor;
2351 }
2352
2353
2354 Inset * InsetText::getInsetFromID(int id_arg) const
2355 {
2356         if (id_arg == id())
2357                 return const_cast<InsetText *>(this);
2358
2359         ParagraphList::const_iterator pit = paragraphs.begin();
2360         ParagraphList::const_iterator pend = paragraphs.end();
2361         for (; pit != pend; ++pit) {
2362                 InsetList::const_iterator it = pit->insetlist.begin();
2363                 InsetList::const_iterator end = pit->insetlist.end();
2364                 for (; it != end; ++it) {
2365                         if (it->inset->id() == id_arg)
2366                                 return it->inset;
2367                         Inset * in = it->inset->getInsetFromID(id_arg);
2368                         if (in)
2369                                 return in;
2370                 }
2371         }
2372         return 0;
2373 }
2374
2375
2376 WordLangTuple const
2377 InsetText::selectNextWordToSpellcheck(BufferView * bv,
2378                                       float & value) const
2379 {
2380         bool clear = false;
2381         WordLangTuple word;
2382
2383         if (!lt) {
2384                 lt = getLyXText(bv);
2385                 clear = true;
2386         }
2387         if (the_locking_inset) {
2388                 word = the_locking_inset->selectNextWordToSpellcheck(bv, value);
2389                 if (!word.word().empty()) {
2390                         value += cy(bv);
2391                         if (clear)
2392                                 lt = 0;
2393                         return word;
2394                 }
2395                 // we have to go on checking so move cursor to the next char
2396                 lt->cursor.pos(lt->cursor.pos() + 1);
2397         }
2398         word = lt->selectNextWordToSpellcheck(value);
2399         if (word.word().empty())
2400                 bv->unlockInset(const_cast<InsetText *>(this));
2401         else
2402                 value = cy(bv);
2403         if (clear)
2404                 lt = 0;
2405         return word;
2406 }
2407
2408
2409 void InsetText::selectSelectedWord(BufferView * bv)
2410 {
2411         if (the_locking_inset) {
2412                 the_locking_inset->selectSelectedWord(bv);
2413                 return;
2414         }
2415         getLyXText(bv)->selectSelectedWord();
2416         updateLocal(bv, SELECTION, false);
2417 }
2418
2419
2420 void InsetText::toggleSelection(BufferView * bv, bool kill_selection)
2421 {
2422         if (the_locking_inset) {
2423                 the_locking_inset->toggleSelection(bv, kill_selection);
2424         }
2425         bool clear = false;
2426         if (!lt) {
2427                 lt = getLyXText(bv);
2428                 clear = true;
2429         }
2430
2431         int x = top_x + TEXT_TO_INSET_OFFSET;
2432
2433         RowList::iterator rowit = lt->rows().begin();
2434         RowList::iterator end = lt->rows().end();
2435         int y_offset = top_baseline - rowit->ascent_of_text();
2436         int y = y_offset;
2437         while ((rowit != end) && ((y + rowit->height()) <= 0)) {
2438                 y += rowit->height();
2439                 ++rowit;
2440         }
2441         if (y_offset < 0)
2442                 y_offset = y;
2443
2444         if (need_update & SELECTION)
2445                 need_update = NONE;
2446         bv->screen().toggleSelection(lt, bv, kill_selection, y_offset, x);
2447         if (clear)
2448                 lt = 0;
2449 }
2450
2451
2452 bool InsetText::nextChange(BufferView * bv, lyx::pos_type & length)
2453 {
2454         bool clear = false;
2455         if (!lt) {
2456                 lt = getLyXText(bv);
2457                 clear = true;
2458         }
2459         if (the_locking_inset) {
2460                 if (the_locking_inset->nextChange(bv, length))
2461                         return true;
2462                 lt->cursorRight(true);
2463         }
2464         lyxfind::SearchResult result =
2465                 lyxfind::findNextChange(bv, lt, length);
2466
2467         if (result == lyxfind::SR_FOUND) {
2468                 LyXCursor cur = lt->cursor;
2469                 bv->unlockInset(bv->theLockingInset());
2470                 if (bv->lockInset(this))
2471                         locked = true;
2472                 lt->cursor = cur;
2473                 lt->setSelectionRange(length);
2474                 updateLocal(bv, SELECTION, false);
2475         }
2476         if (clear)
2477                 lt = 0;
2478         return result != lyxfind::SR_NOT_FOUND;
2479 }
2480
2481
2482 bool InsetText::searchForward(BufferView * bv, string const & str,
2483                               bool cs, bool mw)
2484 {
2485         bool clear = false;
2486         if (!lt) {
2487                 lt = getLyXText(bv);
2488                 clear = true;
2489         }
2490         if (the_locking_inset) {
2491                 if (the_locking_inset->searchForward(bv, str, cs, mw))
2492                         return true;
2493                 lt->cursorRight(true);
2494         }
2495         lyxfind::SearchResult result =
2496                 lyxfind::LyXFind(bv, lt, str, true, cs, mw);
2497
2498         if (result == lyxfind::SR_FOUND) {
2499                 LyXCursor cur = lt->cursor;
2500                 bv->unlockInset(bv->theLockingInset());
2501                 if (bv->lockInset(this))
2502                         locked = true;
2503                 lt->cursor = cur;
2504                 lt->setSelectionRange(str.length());
2505                 updateLocal(bv, SELECTION, false);
2506         }
2507         if (clear)
2508                 lt = 0;
2509         return (result != lyxfind::SR_NOT_FOUND);
2510 }
2511
2512 bool InsetText::searchBackward(BufferView * bv, string const & str,
2513                                bool cs, bool mw)
2514 {
2515         if (the_locking_inset) {
2516                 if (the_locking_inset->searchBackward(bv, str, cs, mw))
2517                         return true;
2518         }
2519         bool clear = false;
2520         if (!lt) {
2521                 lt = getLyXText(bv);
2522                 clear = true;
2523         }
2524         if (!locked) {
2525                 ParagraphList::iterator pit = paragraphs.begin();
2526                 ParagraphList::iterator pend = paragraphs.end();
2527
2528                 while (boost::next(pit) != pend)
2529                         ++pit;
2530
2531                 lt->setCursor(pit, pit->size());
2532         }
2533         lyxfind::SearchResult result =
2534                 lyxfind::LyXFind(bv, lt, str, false, cs, mw);
2535
2536         if (result == lyxfind::SR_FOUND) {
2537                 LyXCursor cur = lt->cursor;
2538                 bv->unlockInset(bv->theLockingInset());
2539                 if (bv->lockInset(this))
2540                         locked = true;
2541                 lt->cursor = cur;
2542                 lt->setSelectionRange(str.length());
2543                 updateLocal(bv, SELECTION, false);
2544         }
2545         if (clear)
2546                 lt = 0;
2547         return (result != lyxfind::SR_NOT_FOUND);
2548 }
2549
2550
2551 bool InsetText::checkInsertChar(LyXFont & font)
2552 {
2553         if (owner())
2554                 return owner()->checkInsertChar(font);
2555         return true;
2556 }
2557
2558
2559 void InsetText::collapseParagraphs(BufferView * bv)
2560 {
2561         LyXText * llt = getLyXText(bv);
2562
2563         while (paragraphs.size() > 1) {
2564                 ParagraphList::iterator first_par = paragraphs.begin();
2565                 ParagraphList::iterator next_par = boost::next(first_par);
2566                 size_t const first_par_size = first_par->size();
2567
2568                 if (!first_par->empty() &&
2569                     !next_par->empty() &&
2570                     !first_par->isSeparator(first_par_size - 1)) {
2571                         first_par->insertChar(first_par_size, ' ');
2572                 }
2573
2574                 if (llt->selection.set()) {
2575                         if (llt->selection.start.par() == next_par) {
2576                                 llt->selection.start.par(first_par);
2577                                 llt->selection.start.pos(
2578                                         llt->selection.start.pos() + first_par_size);
2579                         }
2580                         if (llt->selection.end.par() == next_par) {
2581                                 llt->selection.end.par(first_par);
2582                                 llt->selection.end.pos(
2583                                         llt->selection.end.pos() + first_par_size);
2584                         }
2585                 }
2586
2587                 mergeParagraph(bv->buffer()->params, paragraphs, first_par);
2588         }
2589         reinitLyXText();
2590 }
2591
2592
2593 void InsetText::getDrawFont(LyXFont & font) const
2594 {
2595         if (!owner())
2596                 return;
2597         owner()->getDrawFont(font);
2598 }
2599
2600
2601 void InsetText::appendParagraphs(Buffer * buffer, ParagraphList & plist)
2602 {
2603 #warning FIXME Check if Changes stuff needs changing here. (Lgb)
2604 // And it probably does. You have to take a look at this John. (Lgb)
2605 #warning John, have a look here. (Lgb)
2606         ParagraphList::iterator pit = plist.begin();
2607         ParagraphList::iterator ins = paragraphs.insert(paragraphs.end(), *pit);
2608         ++pit;
2609         mergeParagraph(buffer->params, paragraphs, boost::prior(ins));
2610
2611         ParagraphList::iterator pend = plist.end();
2612         for (; pit != pend; ++pit) {
2613                 paragraphs.push_back(*pit);
2614         }
2615
2616         reinitLyXText();
2617 }
2618
2619
2620 void InsetText::addPreview(grfx::PreviewLoader & loader) const
2621 {
2622         ParagraphList::const_iterator pit = paragraphs.begin();
2623         ParagraphList::const_iterator pend = paragraphs.end();
2624
2625         for (; pit != pend; ++pit) {
2626                 InsetList::const_iterator it  = pit->insetlist.begin();
2627                 InsetList::const_iterator end = pit->insetlist.end();
2628                 for (; it != end; ++it) {
2629                         it->inset->addPreview(loader);
2630                 }
2631         }
2632 }