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