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