]> git.lyx.org Git - lyx.git/blob - src/BufferView_pimpl.C
move mouse click handling to LyXText::dispatch
[lyx.git] / src / BufferView_pimpl.C
1 /**
2  * \file BufferView_pimpl.C
3  * Copyright 2002 the LyX Team
4  * Read the file COPYING
5  *
6  * \author various
7  */
8
9 #include <config.h>
10
11 #ifdef __GNUG__
12 #pragma implementation
13 #endif
14
15 #include "BufferView_pimpl.h"
16 #include "frontends/WorkArea.h"
17 #include "frontends/screen.h"
18 #include "frontends/LyXScreenFactory.h"
19 #include "frontends/WorkAreaFactory.h"
20 #include "frontends/Dialogs.h"
21 #include "frontends/Alert.h"
22 #include "frontends/FileDialog.h"
23 #include "frontends/mouse_state.h"
24 #include "lyxtext.h"
25 #include "lyxrow.h"
26 #include "paragraph.h"
27 #include "frontends/LyXView.h"
28 #include "commandtags.h"
29 #include "lyxfunc.h"
30 #include "debug.h"
31 #include "bufferview_funcs.h"
32 #include "TextCache.h"
33 #include "bufferlist.h"
34 #include "lyxrc.h"
35 #include "intl.h"
36 // added for Dispatch functions
37 #include "lyx_cb.h"
38 #include "lyx_main.h"
39 #include "FloatList.h"
40 #include "gettext.h"
41 #include "ParagraphParameters.h"
42 #include "undo_funcs.h"
43 #include "funcrequest.h"
44 #include "box.h"
45
46 #include "insets/insetbib.h"
47 #include "insets/insettext.h"
48 #include "insets/inseturl.h"
49 #include "insets/insetlatexaccent.h"
50 #include "insets/insettoc.h"
51 #include "insets/insetref.h"
52 #include "insets/insetparent.h"
53 #include "insets/insetindex.h"
54 #include "insets/insetinclude.h"
55 #include "insets/insetcite.h"
56 #include "insets/insetgraphics.h"
57 #include "insets/insetmarginal.h"
58 #include "insets/insetcaption.h"
59 #include "insets/insetfloatlist.h"
60
61 #include "mathed/formulabase.h"
62
63 #include "graphics/Previews.h"
64
65 #include "support/LAssert.h"
66 #include "support/lstrings.h"
67 #include "support/filetools.h"
68
69 #include <boost/bind.hpp>
70 #include <boost/signals/connection.hpp>
71
72 #include <unistd.h>
73 #include <sys/wait.h>
74
75
76 using std::vector;
77 using std::find_if;
78 using std::find;
79 using std::pair;
80 using std::endl;
81 using std::make_pair;
82 using std::min;
83
84 using lyx::pos_type;
85
86 extern BufferList bufferlist;
87
88
89 namespace {
90
91 unsigned int const saved_positions_num = 20;
92
93 // All the below connection objects are needed because of a bug in some
94 // versions of GCC (<=2.96 are on the suspects list.) By having and assigning
95 // to these connections we avoid a segfault upon startup, and also at exit.
96 // (Lgb)
97
98 boost::signals::connection dispatchcon;
99 boost::signals::connection timecon;
100 boost::signals::connection doccon;
101 boost::signals::connection resizecon;
102 boost::signals::connection kpresscon;
103 boost::signals::connection selectioncon;
104 boost::signals::connection lostcon;
105
106
107         /**
108          * Return the on-screen dimensions of the inset at the cursor.
109          * Pre-condition: the cursor must be at an inset.
110          */
111 Box insetDimensions(BufferView * bv, LyXText const & text,
112                                        LyXCursor const & cursor)
113 {
114         Paragraph /*const*/ & par = *cursor.par();
115         pos_type const pos = cursor.pos();
116
117         lyx::Assert(par.getInset(pos));
118
119         Inset const & inset(*par.getInset(pos));
120
121         LyXFont const & font = text.getFont(bv->buffer(), &par, pos);
122
123         int const width = inset.width(bv, font);
124         int const inset_x = font.isVisibleRightToLeft()
125                 ? (cursor.ix() - width) : cursor.ix();
126
127         return Box(
128                 inset_x + inset.scroll(),
129                 inset_x + width,
130                 cursor.iy() - inset.ascent(bv, font),
131                 cursor.iy() + inset.descent(bv, font));
132 }
133
134
135 /**
136  * check if the given co-ordinates are inside an inset at the
137  * given cursor, if one exists. If so, the inset is returned,
138  * and the co-ordinates are made relative. Otherwise, 0 is returned.
139  */
140 Inset * checkInset(BufferView * bv, LyXText const & text,
141                                       LyXCursor const & cursor, int & x, int & y)
142 {
143         pos_type const pos = cursor.pos();
144         Paragraph /*const*/ & par(*cursor.par());
145
146         if (pos >= par.size() || !par.isInset(pos)) {
147                 return 0;
148         }
149
150         Inset /*const*/ * inset = par.getInset(pos);
151
152         if (!isEditableInset(inset)) 
153                 return 0;
154
155         Box b = insetDimensions(bv, text, cursor);
156
157         if (!b.contained(x, y)) {
158                 lyxerr[Debug::GUI] << "Missed inset at x,y " << x << "," << y
159                         << " box " << b << endl;
160                 return 0;
161         }
162
163         text.setCursor(bv, &par, pos, true);
164
165         x -= b.x1;
166         // The origin of an inset is on the baseline
167         y -= text.cursor.iy();
168
169         return inset;
170 }
171
172 } // anon namespace
173
174
175 BufferView::Pimpl::Pimpl(BufferView * bv, LyXView * owner,
176              int xpos, int ypos, int width, int height)
177         : bv_(bv), owner_(owner), buffer_(0), cursor_timeout(400),
178           using_xterm_cursor(false)
179 {
180         workarea_.reset(WorkAreaFactory::create(xpos, ypos, width, height));
181         screen_.reset(LyXScreenFactory::create(workarea()));
182
183         // Setup the signals
184         doccon = workarea().scrollDocView
185                 .connect(boost::bind(&BufferView::Pimpl::scrollDocView, this, _1));
186         resizecon = workarea().workAreaResize
187                 .connect(boost::bind(&BufferView::Pimpl::workAreaResize, this));
188         dispatchcon = workarea().dispatch
189                 .connect(boost::bind(&BufferView::Pimpl::dispatch, this, _1));
190         kpresscon = workarea().workAreaKeyPress
191                 .connect(boost::bind(&BufferView::Pimpl::workAreaKeyPress, this, _1, _2));
192         selectioncon = workarea().selectionRequested
193                 .connect(boost::bind(&BufferView::Pimpl::selectionRequested, this));
194         lostcon = workarea().selectionLost
195                 .connect(boost::bind(&BufferView::Pimpl::selectionLost, this));
196
197         timecon = cursor_timeout.timeout
198                 .connect(boost::bind(&BufferView::Pimpl::cursorToggle, this));
199         cursor_timeout.start();
200         saved_positions.resize(saved_positions_num);
201 }
202
203
204 WorkArea & BufferView::Pimpl::workarea() const
205 {
206         return *workarea_.get();
207 }
208
209
210 LyXScreen & BufferView::Pimpl::screen() const
211 {
212         return *screen_.get();
213 }
214
215
216 Painter & BufferView::Pimpl::painter() const
217 {
218         return workarea().getPainter();
219 }
220
221
222 void BufferView::Pimpl::buffer(Buffer * b)
223 {
224         lyxerr[Debug::INFO] << "Setting buffer in BufferView ("
225                             << b << ")" << endl;
226         if (buffer_) {
227                 buffer_->delUser(bv_);
228
229                 // Put the old text into the TextCache, but
230                 // only if the buffer is still loaded.
231                 // Also set the owner of the test to 0
232                 //              bv_->text->owner(0);
233                 textcache.add(buffer_, workarea().workWidth(), bv_->text);
234                 if (lyxerr.debugging())
235                         textcache.show(lyxerr, "BufferView::buffer");
236
237                 bv_->text = 0;
238         }
239
240         // set current buffer
241         buffer_ = b;
242
243         if (bufferlist.getState() == BufferList::CLOSING) return;
244
245         // if we are closing the buffer, use the first buffer as current
246         if (!buffer_) {
247                 buffer_ = bufferlist.first();
248         }
249
250         if (buffer_) {
251                 lyxerr[Debug::INFO] << "Buffer addr: " << buffer_ << endl;
252                 buffer_->addUser(bv_);
253
254                 // If we don't have a text object for this, we make one
255                 if (bv_->text == 0) {
256                         resizeCurrentBuffer();
257                 }
258
259                 // FIXME: needed when ?
260                 bv_->text->first_y =
261                         screen().topCursorVisible(bv_->text->cursor, bv_->text->first_y);
262
263                 // Similarly, buffer-dependent dialogs should be updated or
264                 // hidden. This should go here because some dialogs (eg ToC)
265                 // require bv_->text.
266                 owner_->getDialogs().updateBufferDependent(true);
267         } else {
268                 lyxerr[Debug::INFO] << "  No Buffer!" << endl;
269                 owner_->getDialogs().hideBufferDependent();
270
271                 // Also remove all remaining text's from the testcache.
272                 // (there should not be any!) (if there is any it is a
273                 // bug!)
274                 if (lyxerr.debugging())
275                         textcache.show(lyxerr, "buffer delete all");
276                 textcache.clear();
277         }
278
279         repaint();
280         updateScrollbar();
281         owner_->updateMenubar();
282         owner_->updateToolbar();
283         owner_->updateLayoutChoice();
284         owner_->updateWindowTitle();
285
286         if (grfx::Previews::activated() && buffer_)
287                 grfx::Previews::get().generateBufferPreviews(*buffer_);
288 }
289
290
291 bool BufferView::Pimpl::fitCursor()
292 {
293         bool ret;
294
295         if (bv_->theLockingInset()) {
296                 bv_->theLockingInset()->fitInsetCursor(bv_);
297                 ret = true;
298         } else {
299                 ret = screen().fitCursor(bv_->text, bv_);
300         }
301
302         bv_->owner()->getDialogs().updateParagraph();
303         if (ret)
304                 updateScrollbar();
305         return ret;
306 }
307
308
309 void BufferView::Pimpl::redoCurrentBuffer()
310 {
311         lyxerr[Debug::INFO] << "BufferView::redoCurrentBuffer" << endl;
312         if (buffer_ && bv_->text) {
313                 resizeCurrentBuffer();
314                 updateScrollbar();
315                 owner_->updateLayoutChoice();
316                 repaint();
317         }
318 }
319
320
321 int BufferView::Pimpl::resizeCurrentBuffer()
322 {
323         lyxerr[Debug::INFO] << "resizeCurrentBuffer" << endl;
324
325         Paragraph * par = 0;
326         Paragraph * selstartpar = 0;
327         Paragraph * selendpar = 0;
328         UpdatableInset * the_locking_inset = 0;
329
330         pos_type pos = 0;
331         pos_type selstartpos = 0;
332         pos_type selendpos = 0;
333         bool selection = false;
334         bool mark_set  = false;
335
336         owner_->prohibitInput();
337
338         owner_->message(_("Formatting document..."));
339
340         if (bv_->text) {
341                 par = bv_->text->cursor.par();
342                 pos = bv_->text->cursor.pos();
343                 selstartpar = bv_->text->selection.start.par();
344                 selstartpos = bv_->text->selection.start.pos();
345                 selendpar = bv_->text->selection.end.par();
346                 selendpos = bv_->text->selection.end.pos();
347                 selection = bv_->text->selection.set();
348                 mark_set = bv_->text->selection.mark();
349                 the_locking_inset = bv_->theLockingInset();
350                 buffer_->resizeInsets(bv_);
351                 // I don't think the delete and new are necessary here we just could
352                 // call only init! (Jug 20020419)
353                 delete bv_->text;
354                 bv_->text = new LyXText(bv_);
355                 bv_->text->init(bv_);
356         } else {
357                 // See if we have a text in TextCache that fits
358                 // the new buffer_ with the correct width.
359                 bv_->text = textcache.findFit(buffer_, workarea().workWidth());
360                 if (bv_->text) {
361                         if (lyxerr.debugging()) {
362                                 lyxerr << "Found a LyXText that fits:\n";
363                                 textcache.show(lyxerr, make_pair(buffer_, make_pair(workarea().workWidth(), bv_->text)));
364                         }
365                         // Set the owner of the newly found text
366                         //      bv_->text->owner(bv_);
367                         if (lyxerr.debugging())
368                                 textcache.show(lyxerr, "resizeCurrentBuffer");
369                 } else {
370                         bv_->text = new LyXText(bv_);
371                         bv_->text->init(bv_);
372                         //buffer_->resizeInsets(bv_);
373                 }
374         }
375
376         if (par) {
377                 bv_->text->selection.set(true);
378                 // At this point just to avoid the Delete-Empty-Paragraph-
379                 // Mechanism when setting the cursor.
380                 bv_->text->selection.mark(mark_set);
381                 if (selection) {
382                         bv_->text->setCursor(bv_, selstartpar, selstartpos);
383                         bv_->text->selection.cursor = bv_->text->cursor;
384                         bv_->text->setCursor(bv_, selendpar, selendpos);
385                         bv_->text->setSelection(bv_);
386                         bv_->text->setCursor(bv_, par, pos);
387                 } else {
388                         bv_->text->setCursor(bv_, par, pos);
389                         bv_->text->selection.cursor = bv_->text->cursor;
390                         bv_->text->selection.set(false);
391                 }
392                 // remake the inset locking
393                 bv_->theLockingInset(the_locking_inset);
394         }
395
396         bv_->text->first_y = screen().topCursorVisible(bv_->text->cursor, bv_->text->first_y);
397
398         switchKeyMap();
399         owner_->allowInput();
400
401         updateScrollbar();
402
403         return 0;
404 }
405
406
407 void BufferView::Pimpl::repaint()
408 {
409         // Regenerate the screen.
410         screen().redraw(bv_->text, bv_);
411 }
412
413
414 void BufferView::Pimpl::updateScrollbar()
415 {
416         if (!bv_->text) {
417                 lyxerr[Debug::GUI] << "no text in updateScrollbar" << endl;
418                 workarea().setScrollbarParams(0, 0, 0);
419                 return;
420         }
421
422         LyXText const & t = *bv_->text;
423
424         lyxerr[Debug::GUI] << "Updating scrollbar: h " << t.height << ", first_y "
425                 << t.first_y << ", default height " << t.defaultHeight() << endl;
426
427         workarea().setScrollbarParams(t.height, t.first_y, t.defaultHeight());
428 }
429
430
431 void BufferView::Pimpl::scrollDocView(int value)
432 {
433         lyxerr[Debug::GUI] << "scrollDocView of " << value << endl;
434
435         if (!buffer_) return;
436
437         screen().draw(bv_->text, bv_, value);
438
439         if (!lyxrc.cursor_follows_scrollbar) {
440                 return;
441         }
442
443         LyXText * vbt = bv_->text;
444
445         int const height = vbt->defaultHeight();
446         int const first = static_cast<int>((bv_->text->first_y + height));
447         int const last = static_cast<int>((bv_->text->first_y + workarea().workHeight() - height));
448
449         if (vbt->cursor.y() < first)
450                 vbt->setCursorFromCoordinates(bv_, 0, first);
451         else if (vbt->cursor.y() > last)
452                 vbt->setCursorFromCoordinates(bv_, 0, last);
453 }
454
455
456 int BufferView::Pimpl::scroll(long time)
457 {
458         if (!buffer_)
459                 return 0;
460
461         LyXText const * t = bv_->text;
462
463         double const diff = t->defaultHeight()
464                 + double(time) * double(time) * 0.125;
465
466         scrollDocView(int(diff));
467         workarea().setScrollbarParams(t->height, t->first_y, t->defaultHeight());
468         return 0;
469 }
470
471
472 void BufferView::Pimpl::workAreaKeyPress(LyXKeySymPtr key,
473                                          key_modifier::state state)
474 {
475         bv_->owner()->getLyXFunc().processKeySym(key, state);
476 }
477
478
479 void BufferView::Pimpl::selectionRequested()
480 {
481         static string sel;
482
483         if (!available())
484                 return;
485
486         LyXText * text = bv_->getLyXText();
487
488         if (text->selection.set() &&
489                 (!bv_->text->xsel_cache.set() ||
490                  text->selection.start != bv_->text->xsel_cache.start ||
491                  text->selection.end != bv_->text->xsel_cache.end))
492         {
493                 bv_->text->xsel_cache = text->selection;
494                 sel = text->selectionAsString(bv_->buffer(), false);
495         } else if (!text->selection.set()) {
496                 sel = string();
497                 bv_->text->xsel_cache.set(false);
498         }
499         if (!sel.empty()) {
500                 workarea().putClipboard(sel);
501         }
502 }
503
504
505 void BufferView::Pimpl::selectionLost()
506 {
507         if (available()) {
508                 hideCursor();
509                 toggleSelection();
510                 bv_->getLyXText()->clearSelection();
511                 showCursor();
512                 bv_->text->xsel_cache.set(false);
513         }
514 }
515
516
517
518 Inset * BufferView::Pimpl::checkInsetHit(LyXText * text, int & x, int & y)
519 {
520         int y_tmp = y + text->first_y;
521
522         LyXCursor cursor;
523         text->setCursorFromCoordinates(bv_, cursor, x, y_tmp);
524
525         Inset * inset = checkInset(bv_, *text, cursor, x, y_tmp);
526
527         if (inset) {
528                 y = y_tmp;
529                 return inset;
530         }
531
532         // look at previous position
533         if (cursor.pos() == 0) {
534                 return 0;
535         }
536
537         // move back one
538         text->setCursor(bv_, cursor, cursor.par(), cursor.pos() - 1, true);
539
540         inset = checkInset(bv_, *text, cursor, x, y_tmp);
541         if (inset) {
542                 y = y_tmp;
543         }
544         return inset;
545 }
546
547
548 void BufferView::Pimpl::workAreaResize()
549 {
550         static int work_area_width;
551         static int work_area_height;
552
553         bool const widthChange = workarea().workWidth() != work_area_width;
554         bool const heightChange = workarea().workHeight() != work_area_height;
555
556         // update from work area
557         work_area_width = workarea().workWidth();
558         work_area_height = workarea().workHeight();
559
560         if (buffer_ != 0) {
561                 if (widthChange) {
562                         // The visible LyXView need a resize
563                         resizeCurrentBuffer();
564
565                         // Remove all texts from the textcache
566                         // This is not _really_ what we want to do. What
567                         // we really want to do is to delete in textcache
568                         // that does not have a BufferView with matching
569                         // width, but as long as we have only one BufferView
570                         // deleting all gives the same result.
571                         if (lyxerr.debugging())
572                                 textcache.show(lyxerr, "Expose delete all");
573                         textcache.clear();
574                         // FIXME: this is already done in resizeCurrentBuffer() ??
575                         buffer_->resizeInsets(bv_);
576                 } else if (heightChange) {
577                         // fitCursor() ensures we don't jump back
578                         // to the start of the document on vertical
579                         // resize
580                         fitCursor();
581                 }
582         }
583
584         if (widthChange || heightChange) {
585                 repaint();
586         }
587
588         // always make sure that the scrollbar is sane.
589         updateScrollbar();
590         owner_->updateLayoutChoice();
591         return;
592 }
593
594
595 void BufferView::Pimpl::update()
596 {
597         if (!bv_->theLockingInset() || !bv_->theLockingInset()->nodraw()) {
598                 LyXText::text_status st = bv_->text->status();
599                 screen().update(bv_->text, bv_);
600                 bool fitc = false;
601                 while (bv_->text->status() == LyXText::CHANGED_IN_DRAW) {
602                         bv_->text->fullRebreak(bv_);
603                         st = LyXText::NEED_MORE_REFRESH;
604                         bv_->text->setCursor(bv_, bv_->text->cursor.par(),
605                                              bv_->text->cursor.pos());
606                         if (bv_->text->selection.set()) {
607                                 bv_->text->setCursor(bv_, bv_->text->selection.start,
608                                                      bv_->text->selection.start.par(),
609                                                      bv_->text->selection.start.pos());
610                                 bv_->text->setCursor(bv_, bv_->text->selection.end,
611                                                      bv_->text->selection.end.par(),
612                                                      bv_->text->selection.end.pos());
613                         }
614                         fitc = true;
615                         bv_->text->status(bv_, st);
616                         screen().update(bv_->text, bv_);
617                 }
618                 // do this here instead of in the screen::update because of
619                 // the above loop!
620                 bv_->text->status(bv_, LyXText::UNCHANGED);
621                 if (fitc)
622                         fitCursor();
623         }
624 }
625
626 // Values used when calling update:
627 // -3 - update
628 // -2 - update, move sel_cursor if selection, fitcursor
629 // -1 - update, move sel_cursor if selection, fitcursor, mark dirty
630 //  0 - update, move sel_cursor if selection, fitcursor
631 //  1 - update, move sel_cursor if selection, fitcursor, mark dirty
632 //  3 - update, move sel_cursor if selection
633 //
634 // update -
635 // a simple redraw of the parts that need refresh
636 //
637 // move sel_cursor if selection -
638 // the text's sel_cursor is moved if there is selection is progress
639 //
640 // fitcursor -
641 // fitCursor() is called and the scrollbar updated
642 //
643 // mark dirty -
644 // the buffer is marked dirty.
645 //
646 // enum {
647 //       UPDATE = 0,
648 //       SELECT = 1,
649 //       FITCUR = 2,
650 //       CHANGE = 4
651 // };
652 //
653 // UPDATE_ONLY = UPDATE;
654 // UPDATE_SELECT = UPDATE | SELECT;
655 // UPDATE_SELECT_MOVE = UPDATE | SELECT | FITCUR;
656 // UPDATE_SELECT_MOVE_AFTER_CHANGE = UPDATE | SELECT | FITCUR | CHANGE;
657 //
658 // update(-3) -> update(0)         -> update(0) -> update(UPDATE)
659 // update(-2) -> update(1 + 2)     -> update(3) -> update(SELECT|FITCUR)
660 // update(-1) -> update(1 + 2 + 4) -> update(7) -> update(SELECT|FITCUR|CHANGE)
661 // update(1)  -> update(1 + 2 + 4) -> update(7) -> update(SELECT|FITCUR|CHANGE)
662 // update(3)  -> update(1)         -> update(1) -> update(SELECT)
663
664 void BufferView::Pimpl::update(LyXText * text, BufferView::UpdateCodes f)
665 {
666         owner_->updateLayoutChoice();
667
668         if (!text->selection.set() && (f & SELECT)) {
669                 text->selection.cursor = text->cursor;
670         }
671
672         text->fullRebreak(bv_);
673
674         if (text->inset_owner) {
675                 text->inset_owner->setUpdateStatus(bv_, InsetText::NONE);
676                 updateInset(text->inset_owner, false);
677         } else {
678                 update();
679         }
680
681         if ((f & FITCUR)) {
682                 fitCursor();
683         }
684
685         if ((f & CHANGE)) {
686                 buffer_->markDirty();
687         }
688 }
689
690
691 // Callback for cursor timer
692 void BufferView::Pimpl::cursorToggle()
693 {
694         if (!buffer_) {
695                 cursor_timeout.restart();
696                 return;
697         }
698
699         /* FIXME */
700         extern void reapSpellchecker(void);
701         reapSpellchecker();
702
703         if (!bv_->theLockingInset()) {
704                 screen().cursorToggle(bv_);
705         } else {
706                 bv_->theLockingInset()->toggleInsetCursor(bv_);
707         }
708
709         cursor_timeout.restart();
710 }
711
712
713 bool BufferView::Pimpl::available() const
714 {
715         if (buffer_ && bv_->text)
716                 return true;
717         return false;
718 }
719
720
721 void BufferView::Pimpl::beforeChange(LyXText * text)
722 {
723         toggleSelection();
724         text->clearSelection();
725 }
726
727
728 void BufferView::Pimpl::savePosition(unsigned int i)
729 {
730         if (i >= saved_positions_num)
731                 return;
732         saved_positions[i] = Position(buffer_->fileName(),
733                                       bv_->text->cursor.par()->id(),
734                                       bv_->text->cursor.pos());
735         if (i > 0) {
736                 ostringstream str;
737                 str << _("Saved bookmark") << ' ' << i;
738                 owner_->message(str.str().c_str());
739         }
740 }
741
742
743 void BufferView::Pimpl::restorePosition(unsigned int i)
744 {
745         if (i >= saved_positions_num)
746                 return;
747
748         string const fname = saved_positions[i].filename;
749
750         beforeChange(bv_->text);
751
752         if (fname != buffer_->fileName()) {
753                 Buffer * b = bufferlist.exists(fname) ?
754                         bufferlist.getBuffer(fname) :
755                         bufferlist.loadLyXFile(fname); // don't ask, just load it
756                 if (b != 0) buffer(b);
757         }
758
759         Paragraph * par = buffer_->getParFromID(saved_positions[i].par_id);
760         if (!par)
761                 return;
762
763         bv_->text->setCursor(bv_, par,
764                              min(par->size(), saved_positions[i].par_pos));
765
766         update(bv_->text, BufferView::SELECT | BufferView::FITCUR);
767         if (i > 0) {
768                 ostringstream str;
769                 str << _("Moved to bookmark") << ' ' << i;
770                 owner_->message(str.str().c_str());
771         }
772 }
773
774
775 bool BufferView::Pimpl::isSavedPosition(unsigned int i)
776 {
777         if (i >= saved_positions_num)
778                 return false;
779
780         return !saved_positions[i].filename.empty();
781 }
782
783
784 void BufferView::Pimpl::switchKeyMap()
785 {
786         if (!lyxrc.rtl_support)
787                 return;
788
789         LyXText * text = bv_->getLyXText();
790         if (text->real_current_font.isRightToLeft()
791             && !(bv_->theLockingInset()
792                  && bv_->theLockingInset()->lyxCode() == Inset::ERT_CODE))
793         {
794                 if (owner_->getIntl().keymap == Intl::PRIMARY)
795                         owner_->getIntl().KeyMapSec();
796         } else {
797                 if (owner_->getIntl().keymap == Intl::SECONDARY)
798                         owner_->getIntl().KeyMapPrim();
799         }
800 }
801
802
803 void BufferView::Pimpl::insetUnlock()
804 {
805         if (bv_->theLockingInset()) {
806                 bv_->theLockingInset()->insetUnlock(bv_);
807                 bv_->theLockingInset(0);
808                 finishUndo();
809         }
810 }
811
812
813 void BufferView::Pimpl::showCursor()
814 {
815         if (bv_->theLockingInset())
816                 bv_->theLockingInset()->showInsetCursor(bv_);
817         else
818                 screen().showCursor(bv_->text, bv_);
819 }
820
821
822 void BufferView::Pimpl::hideCursor()
823 {
824         if (!bv_->theLockingInset())
825                 screen().hideCursor();
826 }
827
828
829 void BufferView::Pimpl::toggleSelection(bool b)
830 {
831         if (bv_->theLockingInset())
832                 bv_->theLockingInset()->toggleSelection(bv_, b);
833         screen().toggleSelection(bv_->text, bv_, b);
834 }
835
836
837 void BufferView::Pimpl::toggleToggle()
838 {
839         screen().toggleToggle(bv_->text, bv_);
840 }
841
842
843 void BufferView::Pimpl::center()
844 {
845         LyXText * t = bv_->text;
846
847         beforeChange(t);
848         int const half_height = workarea().workHeight() / 2;
849         int new_y = 0;
850
851         if (t->cursor.y() > half_height) {
852                 new_y = t->cursor.y() - half_height;
853         }
854
855         // FIXME: can we do this w/o calling screen directly ?
856         // This updates first_y but means the fitCursor() call
857         // from the update(FITCUR) doesn't realise that we might
858         // have moved (e.g. from GOTOPARAGRAPH), so doesn't cause
859         // the scrollbar to be updated as it should, so we have
860         // to do it manually. Any operation that does a center()
861         // and also might have moved first_y must make sure to call
862         // updateScrollbar() currently. Never mind that this is a
863         // pretty obfuscated way of updating t->first_y
864         screen().draw(t, bv_, new_y);
865
866         update(t, BufferView::SELECT | BufferView::FITCUR);
867 }
868
869
870 void BufferView::Pimpl::stuffClipboard(string const & stuff) const
871 {
872         workarea().putClipboard(stuff);
873 }
874
875
876 /*
877  * Dispatch functions for actions which can be valid for BufferView->text
878  * and/or InsetText->text!!!
879  */
880
881
882 Inset * BufferView::Pimpl::getInsetByCode(Inset::Code code)
883 {
884 #if 0
885         LyXCursor cursor = bv_->getLyXText()->cursor;
886         Buffer::inset_iterator it =
887                 find_if(Buffer::inset_iterator(
888                         cursor.par(), cursor.pos()),
889                         buffer_->inset_iterator_end(),
890                         lyx::compare_memfun(&Inset::lyxCode, code));
891         return it != buffer_->inset_iterator_end() ? (*it) : 0;
892 #else
893         // Ok, this is a little bit too brute force but it
894         // should work for now. Better infrastructure is comming. (Lgb)
895
896         Buffer * b = bv_->buffer();
897         LyXCursor cursor = bv_->getLyXText()->cursor;
898
899         Buffer::inset_iterator beg = b->inset_iterator_begin();
900         Buffer::inset_iterator end = b->inset_iterator_end();
901
902         bool cursor_par_seen = false;
903
904         for (; beg != end; ++beg) {
905                 if (beg.getPar() == cursor.par()) {
906                         cursor_par_seen = true;
907                 }
908                 if (cursor_par_seen) {
909                         if (beg.getPar() == cursor.par()
910                             && beg.getPos() >= cursor.pos()) {
911                                 break;
912                         } else if (beg.getPar() != cursor.par()) {
913                                 break;
914                         }
915                 }
916
917         }
918         if (beg != end) {
919                 // Now find the first inset that matches code.
920                 for (; beg != end; ++beg) {
921                         if (beg->lyxCode() == code) {
922                                 return &(*beg);
923                         }
924                 }
925         }
926         return 0;
927 #endif
928 }
929
930
931 void BufferView::Pimpl::MenuInsertLyXFile(string const & filen)
932 {
933         string filename = filen;
934
935         if (filename.empty()) {
936                 // Launch a file browser
937                 string initpath = lyxrc.document_path;
938
939                 if (available()) {
940                         string const trypath = owner_->buffer()->filePath();
941                         // If directory is writeable, use this as default.
942                         if (IsDirWriteable(trypath))
943                                 initpath = trypath;
944                 }
945
946                 FileDialog fileDlg(bv_->owner(),
947                                    _("Select LyX document to insert"),
948                         LFUN_FILE_INSERT,
949                         make_pair(string(_("Documents|#o#O")),
950                                   string(lyxrc.document_path)),
951                         make_pair(string(_("Examples|#E#e")),
952                                   string(AddPath(system_lyxdir, "examples"))));
953
954                 FileDialog::Result result =
955                         fileDlg.Select(initpath,
956                                        _("*.lyx| LyX Documents (*.lyx)"));
957
958                 if (result.first == FileDialog::Later)
959                         return;
960
961                 filename = result.second;
962
963                 // check selected filename
964                 if (filename.empty()) {
965                         owner_->message(_("Canceled."));
966                         return;
967                 }
968         }
969
970         // get absolute path of file and add ".lyx" to the filename if
971         // necessary
972         filename = FileSearch(string(), filename, "lyx");
973
974         string const disp_fn(MakeDisplayPath(filename));
975
976         ostringstream s1;
977         s1 << _("Inserting document") << ' '
978            << disp_fn << " ...";
979         owner_->message(s1.str().c_str());
980         bool const res = bv_->insertLyXFile(filename);
981         if (res) {
982                 ostringstream str;
983                 str << _("Document") << ' ' << disp_fn
984                     << ' ' << _("inserted.");
985                 owner_->message(str.str().c_str());
986         } else {
987                 ostringstream str;
988                 str << _("Could not insert document") << ' '
989                     << disp_fn;
990                 owner_->message(str.str().c_str());
991         }
992 }
993
994
995 bool BufferView::Pimpl::dispatch(FuncRequest const & ev)
996 {
997         lyxerr[Debug::ACTION] << "BufferView::Pimpl::Dispatch:"
998                 << " action[" << ev.action <<"]"
999                 << " arg[" << ev.argument << "]"
1000                 << " x[" << ev.x << "]"
1001                 << " y[" << ev.y << "]"
1002                 << " button[" << ev.button() << "]"
1003                 << endl;
1004
1005         LyXTextClass const & tclass = buffer_->params.getLyXTextClass();
1006
1007         switch (ev.action) {
1008
1009         case LFUN_TOC_INSERT:
1010         {
1011                 InsetCommandParams p;
1012                 p.setCmdName("tableofcontents");
1013                 Inset * inset = new InsetTOC(p);
1014                 if (!insertInset(inset, tclass.defaultLayoutName()))
1015                         delete inset;
1016                 break;
1017         }
1018
1019         case LFUN_SCROLL_INSET:
1020                 // this is not handled here as this function is only active
1021                 // if we have a locking_inset and that one is (or contains)
1022                 // a tabular-inset
1023                 break;
1024
1025         case LFUN_INSET_GRAPHICS:
1026         {
1027                 Inset * new_inset = new InsetGraphics;
1028                 if (!insertInset(new_inset)) {
1029                         delete new_inset;
1030                 } else {
1031                         // this is need because you don't use a inset->Edit()
1032                         updateInset(new_inset, true);
1033                         new_inset->edit(bv_);
1034                 }
1035                 break;
1036         }
1037
1038         case LFUN_LAYOUT_COPY:
1039                 bv_->copyEnvironment();
1040                 break;
1041
1042         case LFUN_LAYOUT_PASTE:
1043                 bv_->pasteEnvironment();
1044                 switchKeyMap();
1045                 break;
1046
1047         case LFUN_DEPTH_MIN:
1048                 changeDepth(bv_, bv_->getLyXText(), -1);
1049                 break;
1050
1051         case LFUN_DEPTH_PLUS:
1052                 changeDepth(bv_, bv_->getLyXText(), 1);
1053                 break;
1054
1055         case LFUN_FREE:
1056                 owner_->getDialogs().setUserFreeFont();
1057                 break;
1058
1059         case LFUN_FILE_INSERT:
1060                 MenuInsertLyXFile(ev.argument);
1061                 break;
1062
1063         case LFUN_FILE_INSERT_ASCII_PARA:
1064                 InsertAsciiFile(bv_, ev.argument, true);
1065                 break;
1066
1067         case LFUN_FILE_INSERT_ASCII:
1068                 InsertAsciiFile(bv_, ev.argument, false);
1069                 break;
1070
1071         case LFUN_LANGUAGE:
1072                 lang(bv_, ev.argument);
1073                 switchKeyMap();
1074                 owner_->view_state_changed();
1075                 break;
1076
1077         case LFUN_EMPH:
1078                 emph(bv_);
1079                 owner_->view_state_changed();
1080                 break;
1081
1082         case LFUN_BOLD:
1083                 bold(bv_);
1084                 owner_->view_state_changed();
1085                 break;
1086
1087         case LFUN_NOUN:
1088                 noun(bv_);
1089                 owner_->view_state_changed();
1090                 break;
1091
1092         case LFUN_CODE:
1093                 code(bv_);
1094                 owner_->view_state_changed();
1095                 break;
1096
1097         case LFUN_SANS:
1098                 sans(bv_);
1099                 owner_->view_state_changed();
1100                 break;
1101
1102         case LFUN_ROMAN:
1103                 roman(bv_);
1104                 owner_->view_state_changed();
1105                 break;
1106
1107         case LFUN_DEFAULT:
1108                 styleReset(bv_);
1109                 owner_->view_state_changed();
1110                 break;
1111
1112         case LFUN_UNDERLINE:
1113                 underline(bv_);
1114                 owner_->view_state_changed();
1115                 break;
1116
1117         case LFUN_FONT_SIZE:
1118                 fontSize(bv_, ev.argument);
1119                 owner_->view_state_changed();
1120                 break;
1121
1122         case LFUN_FONT_STATE:
1123                 owner_->getLyXFunc().setMessage(currentState(bv_));
1124                 break;
1125
1126         case LFUN_INSERT_LABEL:
1127                 MenuInsertLabel(bv_, ev.argument);
1128                 break;
1129
1130         case LFUN_REF_INSERT:
1131                 if (ev.argument.empty()) {
1132                         InsetCommandParams p("ref");
1133                         owner_->getDialogs().createRef(p.getAsString());
1134                 } else {
1135                         InsetCommandParams p;
1136                         p.setFromString(ev.argument);
1137
1138                         InsetRef * inset = new InsetRef(p, *buffer_);
1139                         if (!insertInset(inset))
1140                                 delete inset;
1141                         else
1142                                 updateInset(inset, true);
1143                 }
1144                 break;
1145
1146         case LFUN_BOOKMARK_SAVE:
1147                 savePosition(strToUnsignedInt(ev.argument));
1148                 break;
1149
1150         case LFUN_BOOKMARK_GOTO:
1151                 restorePosition(strToUnsignedInt(ev.argument));
1152                 break;
1153
1154         case LFUN_REF_GOTO:
1155         {
1156                 string label = ev.argument;
1157                 if (label.empty()) {
1158                         InsetRef * inset =
1159                                 static_cast<InsetRef*>(getInsetByCode(Inset::REF_CODE));
1160                         if (inset) {
1161                                 label = inset->getContents();
1162                                 savePosition(0);
1163                         }
1164                 }
1165
1166                 if (!label.empty()) {
1167                         //bv_->savePosition(0);
1168                         if (!bv_->gotoLabel(label))
1169                                 Alert::alert(_("Error"),
1170                                            _("Couldn't find this label"),
1171                                            _("in current document."));
1172                 }
1173         }
1174         break;
1175
1176         case LFUN_HTMLURL:
1177         case LFUN_URL:
1178         {
1179                 InsetCommandParams p;
1180                 if (ev.action == LFUN_HTMLURL)
1181                         p.setCmdName("htmlurl");
1182                 else
1183                         p.setCmdName("url");
1184                 owner_->getDialogs().createUrl(p.getAsString());
1185         }
1186         break;
1187
1188         case LFUN_INSERT_URL:
1189         {
1190                 InsetCommandParams p;
1191                 p.setFromString(ev.argument);
1192
1193                 InsetUrl * inset = new InsetUrl(p);
1194                 if (!insertInset(inset))
1195                         delete inset;
1196                 else
1197                         updateInset(inset, true);
1198         }
1199         break;
1200
1201         case LFUN_INSET_CAPTION:
1202         {
1203                 // Do we have a locking inset...
1204                 if (bv_->theLockingInset()) {
1205                         lyxerr << "Locking inset code: "
1206                                << static_cast<int>(bv_->theLockingInset()->lyxCode());
1207                         InsetCaption * new_inset =
1208                                 new InsetCaption(buffer_->params);
1209                         new_inset->setOwner(bv_->theLockingInset());
1210                         new_inset->setAutoBreakRows(true);
1211                         new_inset->setDrawFrame(0, InsetText::LOCKED);
1212                         new_inset->setFrameColor(0, LColor::captionframe);
1213                         if (insertInset(new_inset))
1214                                 new_inset->edit(bv_);
1215                         else
1216                                 delete new_inset;
1217                 }
1218         }
1219         break;
1220
1221
1222         // --- accented characters ---------------------------
1223
1224         case LFUN_UMLAUT:
1225         case LFUN_CIRCUMFLEX:
1226         case LFUN_GRAVE:
1227         case LFUN_ACUTE:
1228         case LFUN_TILDE:
1229         case LFUN_CEDILLA:
1230         case LFUN_MACRON:
1231         case LFUN_DOT:
1232         case LFUN_UNDERDOT:
1233         case LFUN_UNDERBAR:
1234         case LFUN_CARON:
1235         case LFUN_SPECIAL_CARON:
1236         case LFUN_BREVE:
1237         case LFUN_TIE:
1238         case LFUN_HUNG_UMLAUT:
1239         case LFUN_CIRCLE:
1240         case LFUN_OGONEK:
1241                 if (ev.argument.empty()) {
1242                         // As always...
1243                         owner_->getLyXFunc().handleKeyFunc(ev.action);
1244                 } else {
1245                         owner_->getLyXFunc().handleKeyFunc(ev.action);
1246                         owner_->getIntl().getTransManager()
1247                                 .TranslateAndInsert(ev.argument[0], bv_->getLyXText());
1248                         update(bv_->getLyXText(),
1249                                BufferView::SELECT
1250                                | BufferView::FITCUR
1251                                | BufferView::CHANGE);
1252                 }
1253                 break;
1254
1255         case LFUN_MATH_MACRO:
1256         case LFUN_MATH_DELIM:
1257         case LFUN_INSERT_MATRIX:
1258         case LFUN_INSERT_MATH:
1259         case LFUN_MATH_IMPORT_SELECTION: // Imports LaTeX from the X selection
1260         case LFUN_MATH_DISPLAY:          // Open or create a displayed math inset
1261         case LFUN_MATH_MODE:             // Open or create an inlined math inset
1262         case LFUN_GREEK:                 // Insert a single greek letter
1263                 mathDispatch(FuncRequest(bv_, ev.action, ev.argument));
1264                 break;
1265
1266         case LFUN_CITATION_INSERT:
1267         {
1268                 InsetCommandParams p;
1269                 p.setFromString(ev.argument);
1270
1271                 InsetCitation * inset = new InsetCitation(p);
1272                 if (!insertInset(inset))
1273                         delete inset;
1274                 else
1275                         updateInset(inset, true);
1276         }
1277         break;
1278
1279         case LFUN_INSERT_BIBTEX:
1280         {
1281                 // ale970405+lasgoutt970425
1282                 // The argument can be up to two tokens separated
1283                 // by a space. The first one is the bibstyle.
1284                 string const db = token(ev.argument, ' ', 0);
1285                 string bibstyle = token(ev.argument, ' ', 1);
1286                 if (bibstyle.empty())
1287                         bibstyle = "plain";
1288
1289                 InsetCommandParams p("BibTeX", db, bibstyle);
1290                 InsetBibtex * inset = new InsetBibtex(p);
1291
1292                 if (insertInset(inset)) {
1293                         if (ev.argument.empty())
1294                                 inset->edit(bv_);
1295                 } else
1296                         delete inset;
1297         }
1298         break;
1299
1300         // BibTeX data bases
1301         case LFUN_BIBDB_ADD:
1302         {
1303                 InsetBibtex * inset =
1304                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
1305                 if (inset) {
1306                         inset->addDatabase(ev.argument);
1307                 }
1308         }
1309         break;
1310
1311         case LFUN_BIBDB_DEL:
1312         {
1313                 InsetBibtex * inset =
1314                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
1315                 if (inset) {
1316                         inset->delDatabase(ev.argument);
1317                 }
1318         }
1319         break;
1320
1321         case LFUN_BIBTEX_STYLE:
1322         {
1323                 InsetBibtex * inset =
1324                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
1325                 if (inset) {
1326                         inset->setOptions(ev.argument);
1327                 }
1328         }
1329         break;
1330
1331         case LFUN_INDEX_INSERT:
1332         {
1333                 string entry = ev.argument;
1334                 if (entry.empty())
1335                         entry = bv_->getLyXText()->getStringToIndex(bv_);
1336
1337                 if (entry.empty()) {
1338                         owner_->getDialogs().createIndex();
1339                         break;
1340                 }
1341
1342                 InsetIndex * inset = new InsetIndex(InsetCommandParams("index", entry));
1343
1344                 if (!insertInset(inset)) {
1345                         delete inset;
1346                 } else {
1347                         updateInset(inset, true);
1348                 }
1349         }
1350         break;
1351
1352         case LFUN_INDEX_PRINT:
1353         {
1354                 InsetCommandParams p("printindex");
1355                 Inset * inset = new InsetPrintIndex(p);
1356                 if (!insertInset(inset, tclass.defaultLayoutName()))
1357                         delete inset;
1358         }
1359         break;
1360
1361         case LFUN_PARENTINSERT:
1362         {
1363                 InsetCommandParams p("lyxparent", ev.argument);
1364                 Inset * inset = new InsetParent(p, *buffer_);
1365                 if (!insertInset(inset, tclass.defaultLayoutName()))
1366                         delete inset;
1367         }
1368
1369         break;
1370
1371         case LFUN_CHILD_INSERT:
1372         {
1373                 InsetInclude::Params p;
1374                 p.cparams.setFromString(ev.argument);
1375                 p.masterFilename_ = buffer_->fileName();
1376
1377                 InsetInclude * inset = new InsetInclude(p);
1378                 if (!insertInset(inset))
1379                         delete inset;
1380                 else {
1381                         updateInset(inset, true);
1382                         bv_->owner()->getDialogs().showInclude(inset);
1383                 }
1384         }
1385         break;
1386
1387         case LFUN_FLOAT_LIST:
1388                 if (tclass.floats().typeExist(ev.argument)) {
1389                         Inset * inset = new InsetFloatList(ev.argument);
1390                         if (!insertInset(inset, tclass.defaultLayoutName()))
1391                                 delete inset;
1392                 } else {
1393                         lyxerr << "Non-existent float type: "
1394                                << ev.argument << endl;
1395                 }
1396                 break;
1397
1398         case LFUN_THESAURUS_ENTRY:
1399         {
1400                 string arg = ev.argument;
1401
1402                 if (arg.empty()) {
1403                         arg = bv_->getLyXText()->selectionAsString(buffer_,
1404                                                                    false);
1405
1406                         // FIXME
1407                         if (arg.size() > 100 || arg.empty()) {
1408                                 // Get word or selection
1409                                 bv_->getLyXText()->selectWordWhenUnderCursor(bv_, LyXText::WHOLE_WORD);
1410                                 arg = bv_->getLyXText()->selectionAsString(buffer_, false);
1411                                 // FIXME: where is getLyXText()->unselect(bv_) ?
1412                         }
1413                 }
1414
1415                 bv_->owner()->getDialogs().showThesaurus(arg);
1416         }
1417                 break;
1418
1419         case LFUN_UNKNOWN_ACTION:
1420                 ev.errorMessage(N_("Unknown function!"));
1421                 break;
1422
1423         default:
1424                 return bv_->getLyXText()->dispatch(FuncRequest(ev, bv_));
1425         } // end of switch
1426
1427         return true;
1428 }
1429
1430
1431 bool BufferView::Pimpl::insertInset(Inset * inset, string const & lout)
1432 {
1433         // if we are in a locking inset we should try to insert the
1434         // inset there otherwise this is a illegal function now
1435         if (bv_->theLockingInset()) {
1436                 if (bv_->theLockingInset()->insetAllowed(inset))
1437                     return bv_->theLockingInset()->insertInset(bv_, inset);
1438                 return false;
1439         }
1440
1441         // not quite sure if we want this...
1442         setCursorParUndo(bv_);
1443         freezeUndo();
1444
1445         beforeChange(bv_->text);
1446         if (!lout.empty()) {
1447                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
1448                 bv_->text->breakParagraph(bv_);
1449                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
1450
1451                 if (!bv_->text->cursor.par()->empty()) {
1452                         bv_->text->cursorLeft(bv_);
1453
1454                         bv_->text->breakParagraph(bv_);
1455                         update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
1456                 }
1457
1458                 string lres = lout;
1459                 LyXTextClass const & tclass =
1460                         buffer_->params.getLyXTextClass();
1461                 bool hasLayout = tclass.hasLayout(lres);
1462                 string lay = tclass.defaultLayoutName();
1463
1464                 if (hasLayout != false) {
1465                         // layout found
1466                         lay = lres;
1467                 } else {
1468                         // layout not fount using default
1469                         lay = tclass.defaultLayoutName();
1470                 }
1471
1472                 bv_->text->setLayout(bv_, lay);
1473
1474                 bv_->text->setParagraph(bv_, 0, 0,
1475                                    0, 0,
1476                                    VSpace(VSpace::NONE), VSpace(VSpace::NONE),
1477                                    Spacing(),
1478                                    LYX_ALIGN_LAYOUT,
1479                                    string(),
1480                                    0);
1481                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
1482         }
1483
1484         bv_->text->insertInset(bv_, inset);
1485         update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
1486
1487         unFreezeUndo();
1488         return true;
1489 }
1490
1491
1492 void BufferView::Pimpl::updateInset(Inset * inset, bool mark_dirty)
1493 {
1494         if (!inset || !available())
1495                 return;
1496
1497         // first check for locking insets
1498         if (bv_->theLockingInset()) {
1499                 if (bv_->theLockingInset() == inset) {
1500                         if (bv_->text->updateInset(bv_, inset)) {
1501                                 update();
1502                                 if (mark_dirty) {
1503                                         buffer_->markDirty();
1504                                 }
1505                                 updateScrollbar();
1506                                 return;
1507                         }
1508                 } else if (bv_->theLockingInset()->updateInsetInInset(bv_, inset)) {
1509                         if (bv_->text->updateInset(bv_,  bv_->theLockingInset())) {
1510                                 update();
1511                                 if (mark_dirty) {
1512                                         buffer_->markDirty();
1513                                 }
1514                                 updateScrollbar();
1515                                 return;
1516                         }
1517                 }
1518         }
1519
1520         // then check if the inset is a top_level inset (has no owner)
1521         // if yes do the update as always otherwise we have to update the
1522         // toplevel inset where this inset is inside
1523         Inset * tl_inset = inset;
1524         while (tl_inset->owner())
1525                 tl_inset = tl_inset->owner();
1526         hideCursor();
1527         if (tl_inset == inset) {
1528                 update(bv_->text, BufferView::UPDATE);
1529                 if (bv_->text->updateInset(bv_, inset)) {
1530                         if (mark_dirty) {
1531                                 update(bv_->text,
1532                                        BufferView::SELECT
1533                                        | BufferView::FITCUR
1534                                        | BufferView::CHANGE);
1535                         } else {
1536                                 update(bv_->text, SELECT);
1537                         }
1538                         return;
1539                 }
1540         } else if (static_cast<UpdatableInset *>(tl_inset)
1541                            ->updateInsetInInset(bv_, inset))
1542         {
1543                 if (bv_->text->updateInset(bv_, tl_inset)) {
1544                         update();
1545                         updateScrollbar();
1546                 }
1547         }
1548 }