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