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