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