2 * \file BufferView_pimpl.C
3 * Copyright 2002 the LyX Team
4 * Read the file COPYING
12 #pragma implementation
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"
25 #include "paragraph.h"
26 #include "frontends/LyXView.h"
27 #include "commandtags.h"
30 #include "bufferview_funcs.h"
31 #include "TextCache.h"
32 #include "bufferlist.h"
35 // added for Dispatch functions
38 #include "FloatList.h"
40 #include "ParagraphParameters.h"
41 #include "undo_funcs.h"
42 #include "funcrequest.h"
45 #include "insets/insetbib.h"
46 #include "insets/insettext.h"
47 #include "insets/inseturl.h"
48 #include "insets/insetlatexaccent.h"
49 #include "insets/insettoc.h"
50 #include "insets/insetref.h"
51 #include "insets/insetparent.h"
52 #include "insets/insetindex.h"
53 #include "insets/insetinclude.h"
54 #include "insets/insetcite.h"
55 #include "insets/insetgraphics.h"
56 #include "insets/insetmarginal.h"
57 #include "insets/insetcaption.h"
58 #include "insets/insetfloatlist.h"
60 #include "mathed/formulabase.h"
62 #include "graphics/Previews.h"
64 #include "support/LAssert.h"
65 #include "support/lstrings.h"
66 #include "support/filetools.h"
68 #include <boost/bind.hpp>
69 #include <boost/signals/connection.hpp>
85 /* the selection possible is needed, that only motion events are
86 * used, where the bottom press event was on the drawing area too */
87 bool selection_possible = false;
89 extern BufferList bufferlist;
90 extern int bibitemMaxWidth(BufferView *, LyXFont const &);
95 unsigned int const saved_positions_num = 20;
97 // All the below connection objects are needed because of a bug in some
98 // versions of GCC (<=2.96 are on the suspects list.) By having and assigning
99 // to these connections we avoid a segfault upon startup, and also at exit.
102 boost::signals::connection timecon;
103 boost::signals::connection doccon;
104 boost::signals::connection resizecon;
105 boost::signals::connection bpresscon;
106 boost::signals::connection breleasecon;
107 boost::signals::connection motioncon;
108 boost::signals::connection doublecon;
109 boost::signals::connection triplecon;
110 boost::signals::connection kpresscon;
111 boost::signals::connection selectioncon;
112 boost::signals::connection lostcon;
116 * Return the on-screen dimensions of the inset at the cursor.
117 * Pre-condition: the cursor must be at an inset.
119 Box insetDimensions(BufferView * bv, LyXText const & text,
120 LyXCursor const & cursor)
122 Paragraph /*const*/ & par = *cursor.par();
123 pos_type const pos = cursor.pos();
125 lyx::Assert(par.getInset(pos));
127 Inset const & inset(*par.getInset(pos));
129 LyXFont const & font = text.getFont(bv->buffer(), &par, pos);
131 int const width = inset.width(bv, font);
132 int const inset_x = font.isVisibleRightToLeft()
133 ? (cursor.ix() - width) : cursor.ix();
136 inset_x + inset.scroll(),
138 cursor.iy() - inset.ascent(bv, font),
139 cursor.iy() + inset.descent(bv, font));
144 * check if the given co-ordinates are inside an inset at the
145 * given cursor, if one exists. If so, the inset is returned,
146 * and the co-ordinates are made relative. Otherwise, 0 is returned.
148 Inset * checkInset(BufferView * bv, LyXText const & text,
149 LyXCursor const & cursor, int & x, int & y)
151 pos_type const pos = cursor.pos();
152 Paragraph /*const*/ & par(*cursor.par());
154 if (pos >= par.size() || !par.isInset(pos)) {
158 Inset /*const*/ * inset = par.getInset(pos);
160 if (!isEditableInset(inset))
163 Box b = insetDimensions(bv, text, cursor);
165 if (!b.contained(x, y)) {
166 lyxerr[Debug::GUI] << "Missed inset at x,y " << x << "," << y
167 << " box " << b << endl;
171 text.setCursor(bv, &par, pos, true);
174 // The origin of an inset is on the baseline
175 y -= text.cursor.iy();
183 BufferView::Pimpl::Pimpl(BufferView * bv, LyXView * owner,
184 int xpos, int ypos, int width, int height)
185 : bv_(bv), owner_(owner), buffer_(0), cursor_timeout(400),
186 using_xterm_cursor(false)
188 workarea_.reset(WorkAreaFactory::create(xpos, ypos, width, height));
189 screen_.reset(LyXScreenFactory::create(workarea()));
192 doccon = workarea().scrollDocView
193 .connect(boost::bind(&BufferView::Pimpl::scrollDocView, this, _1));
194 resizecon = workarea().workAreaResize
195 .connect(boost::bind(&BufferView::Pimpl::workAreaResize, this));
196 bpresscon = workarea().workAreaButtonPress
197 .connect(boost::bind(&BufferView::Pimpl::workAreaButtonPress, this, _1, _2, _3));
198 breleasecon = workarea().workAreaButtonRelease
199 .connect(boost::bind(&BufferView::Pimpl::workAreaButtonRelease, this, _1, _2, _3));
200 motioncon = workarea().workAreaMotionNotify
201 .connect(boost::bind(&BufferView::Pimpl::workAreaMotionNotify, this, _1, _2, _3));
202 doublecon = workarea().workAreaDoubleClick
203 .connect(boost::bind(&BufferView::Pimpl::doubleClick, this, _1, _2, _3));
204 triplecon = workarea().workAreaTripleClick
205 .connect(boost::bind(&BufferView::Pimpl::tripleClick, this, _1, _2, _3));
206 kpresscon = workarea().workAreaKeyPress
207 .connect(boost::bind(&BufferView::Pimpl::workAreaKeyPress, this, _1, _2));
208 selectioncon = workarea().selectionRequested
209 .connect(boost::bind(&BufferView::Pimpl::selectionRequested, this));
210 lostcon = workarea().selectionLost
211 .connect(boost::bind(&BufferView::Pimpl::selectionLost, this));
213 timecon = cursor_timeout.timeout
214 .connect(boost::bind(&BufferView::Pimpl::cursorToggle, this));
215 cursor_timeout.start();
216 saved_positions.resize(saved_positions_num);
220 WorkArea & BufferView::Pimpl::workarea() const
222 return *workarea_.get();
226 LyXScreen & BufferView::Pimpl::screen() const
228 return *screen_.get();
232 Painter & BufferView::Pimpl::painter() const
234 return workarea().getPainter();
238 void BufferView::Pimpl::buffer(Buffer * b)
240 lyxerr[Debug::INFO] << "Setting buffer in BufferView ("
243 buffer_->delUser(bv_);
245 // Put the old text into the TextCache, but
246 // only if the buffer is still loaded.
247 // Also set the owner of the test to 0
248 // bv_->text->owner(0);
249 textcache.add(buffer_, workarea().workWidth(), bv_->text);
250 if (lyxerr.debugging())
251 textcache.show(lyxerr, "BufferView::buffer");
256 // set current buffer
259 if (bufferlist.getState() == BufferList::CLOSING) return;
261 // if we are closing the buffer, use the first buffer as current
263 buffer_ = bufferlist.first();
267 lyxerr[Debug::INFO] << "Buffer addr: " << buffer_ << endl;
268 buffer_->addUser(bv_);
270 // If we don't have a text object for this, we make one
271 if (bv_->text == 0) {
272 resizeCurrentBuffer();
275 // FIXME: needed when ?
277 screen().topCursorVisible(bv_->text->cursor, bv_->text->first_y);
279 // Similarly, buffer-dependent dialogs should be updated or
280 // hidden. This should go here because some dialogs (eg ToC)
281 // require bv_->text.
282 owner_->getDialogs().updateBufferDependent(true);
284 lyxerr[Debug::INFO] << " No Buffer!" << endl;
285 owner_->getDialogs().hideBufferDependent();
287 // Also remove all remaining text's from the testcache.
288 // (there should not be any!) (if there is any it is a
290 if (lyxerr.debugging())
291 textcache.show(lyxerr, "buffer delete all");
297 owner_->updateMenubar();
298 owner_->updateToolbar();
299 owner_->updateLayoutChoice();
300 owner_->updateWindowTitle();
302 if (grfx::Previews::activated() && buffer_)
303 grfx::Previews::get().generateBufferPreviews(*buffer_);
307 bool BufferView::Pimpl::fitCursor()
311 if (bv_->theLockingInset()) {
312 bv_->theLockingInset()->fitInsetCursor(bv_);
315 ret = screen().fitCursor(bv_->text, bv_);
318 bv_->owner()->getDialogs().updateParagraph();
325 void BufferView::Pimpl::redoCurrentBuffer()
327 lyxerr[Debug::INFO] << "BufferView::redoCurrentBuffer" << endl;
328 if (buffer_ && bv_->text) {
329 resizeCurrentBuffer();
331 owner_->updateLayoutChoice();
337 int BufferView::Pimpl::resizeCurrentBuffer()
339 lyxerr[Debug::INFO] << "resizeCurrentBuffer" << endl;
342 Paragraph * selstartpar = 0;
343 Paragraph * selendpar = 0;
344 UpdatableInset * the_locking_inset = 0;
347 pos_type selstartpos = 0;
348 pos_type selendpos = 0;
349 bool selection = false;
350 bool mark_set = false;
352 owner_->prohibitInput();
354 owner_->message(_("Formatting document..."));
357 par = bv_->text->cursor.par();
358 pos = bv_->text->cursor.pos();
359 selstartpar = bv_->text->selection.start.par();
360 selstartpos = bv_->text->selection.start.pos();
361 selendpar = bv_->text->selection.end.par();
362 selendpos = bv_->text->selection.end.pos();
363 selection = bv_->text->selection.set();
364 mark_set = bv_->text->selection.mark();
365 the_locking_inset = bv_->theLockingInset();
366 buffer_->resizeInsets(bv_);
367 // I don't think the delete and new are necessary here we just could
368 // call only init! (Jug 20020419)
370 bv_->text = new LyXText(bv_);
371 bv_->text->init(bv_);
373 // See if we have a text in TextCache that fits
374 // the new buffer_ with the correct width.
375 bv_->text = textcache.findFit(buffer_, workarea().workWidth());
377 if (lyxerr.debugging()) {
378 lyxerr << "Found a LyXText that fits:\n";
379 textcache.show(lyxerr, make_pair(buffer_, make_pair(workarea().workWidth(), bv_->text)));
381 // Set the owner of the newly found text
382 // bv_->text->owner(bv_);
383 if (lyxerr.debugging())
384 textcache.show(lyxerr, "resizeCurrentBuffer");
386 bv_->text = new LyXText(bv_);
387 bv_->text->init(bv_);
388 //buffer_->resizeInsets(bv_);
393 bv_->text->selection.set(true);
394 // At this point just to avoid the Delete-Empty-Paragraph-
395 // Mechanism when setting the cursor.
396 bv_->text->selection.mark(mark_set);
398 bv_->text->setCursor(bv_, selstartpar, selstartpos);
399 bv_->text->selection.cursor = bv_->text->cursor;
400 bv_->text->setCursor(bv_, selendpar, selendpos);
401 bv_->text->setSelection(bv_);
402 bv_->text->setCursor(bv_, par, pos);
404 bv_->text->setCursor(bv_, par, pos);
405 bv_->text->selection.cursor = bv_->text->cursor;
406 bv_->text->selection.set(false);
408 // remake the inset locking
409 bv_->theLockingInset(the_locking_inset);
412 bv_->text->first_y = screen().topCursorVisible(bv_->text->cursor, bv_->text->first_y);
415 owner_->allowInput();
423 void BufferView::Pimpl::repaint()
425 // Regenerate the screen.
426 screen().redraw(bv_->text, bv_);
430 void BufferView::Pimpl::updateScrollbar()
433 lyxerr[Debug::GUI] << "no text in updateScrollbar" << endl;
434 workarea().setScrollbarParams(0, 0, 0);
438 LyXText const & t = *bv_->text;
440 lyxerr[Debug::GUI] << "Updating scrollbar: h " << t.height << ", first_y "
441 << t.first_y << ", default height " << t.defaultHeight() << endl;
443 workarea().setScrollbarParams(t.height, t.first_y, t.defaultHeight());
447 void BufferView::Pimpl::scrollDocView(int value)
449 lyxerr[Debug::GUI] << "scrollDocView of " << value << endl;
451 if (!buffer_) return;
453 screen().draw(bv_->text, bv_, value);
455 if (!lyxrc.cursor_follows_scrollbar) {
459 LyXText * vbt = bv_->text;
461 int const height = vbt->defaultHeight();
462 int const first = static_cast<int>((bv_->text->first_y + height));
463 int const last = static_cast<int>((bv_->text->first_y + workarea().workHeight() - height));
465 if (vbt->cursor.y() < first)
466 vbt->setCursorFromCoordinates(bv_, 0, first);
467 else if (vbt->cursor.y() > last)
468 vbt->setCursorFromCoordinates(bv_, 0, last);
472 int BufferView::Pimpl::scroll(long time)
477 LyXText const * t = bv_->text;
479 double const diff = t->defaultHeight()
480 + double(time) * double(time) * 0.125;
482 scrollDocView(int(diff));
483 workarea().setScrollbarParams(t->height, t->first_y, t->defaultHeight());
488 void BufferView::Pimpl::workAreaKeyPress(LyXKeySymPtr key,
489 key_modifier::state state)
491 bv_->owner()->getLyXFunc().processKeySym(key, state);
495 void BufferView::Pimpl::workAreaMotionNotify(int x, int y, mouse_button::state state)
497 // Only use motion with button 1
498 if (!(state & mouse_button::button1))
504 // Check for inset locking
505 if (bv_->theLockingInset()) {
506 LyXCursor cursor = bv_->text->cursor;
507 LyXFont font = bv_->text->getFont(buffer_,
508 cursor.par(), cursor.pos());
509 int width = bv_->theLockingInset()->width(bv_, font);
510 int inset_x = font.isVisibleRightToLeft()
511 ? cursor.ix() - width : cursor.ix();
512 int start_x = inset_x + bv_->theLockingInset()->scroll();
514 FuncRequest cmd(bv_, LFUN_MOUSE_MOTION,
515 x - start_x, y - cursor.iy() + bv_->text->first_y, state);
516 bv_->theLockingInset()->localDispatch(cmd);
520 // The test for not selection possible is needed, that only motion
521 // events are used, where the bottom press event was on
522 // the drawing area too
523 if (!selection_possible)
526 screen().hideCursor();
528 Row * cursorrow = bv_->text->cursor.row();
529 bv_->text->setCursorFromCoordinates(bv_, x, y + bv_->text->first_y);
531 // sorry for this but I have a strange error that the y value jumps at
532 // a certain point. This seems like an error in my xforms library or
533 // in some other local environment, but I would like to leave this here
534 // for the moment until I can remove this (Jug 20020418)
535 if (y_before < bv_->text->cursor.y())
536 lyxerr << y_before << ":" << bv_->text->cursor.y() << endl;
538 // This is to allow jumping over large insets
539 if (cursorrow == bv_->text->cursor.row()) {
540 if (y >= int(workarea().workHeight())) {
541 bv_->text->cursorDown(bv_, false);
543 bv_->text->cursorUp(bv_, false);
547 if (!bv_->text->selection.set())
548 update(bv_->text, BufferView::UPDATE); // Maybe an empty line was deleted
550 bv_->text->setSelection(bv_);
551 screen().toggleToggle(bv_->text, bv_);
557 // Single-click on work area
558 void BufferView::Pimpl::workAreaButtonPress(int xpos, int ypos,
559 mouse_button::state button)
564 // ok ok, this is a hack (for xforms)
565 if (button == mouse_button::button4) {
566 scroll(-lyxrc.wheel_jump);
567 // We shouldn't go further down as we really should only do the
568 // scrolling and be done with this. Otherwise we may open some
569 // dialogs (Jug 20020424).
571 } else if (button == mouse_button::button5) {
572 scroll(lyxrc.wheel_jump);
573 // We shouldn't go further down as we really should only do the
574 // scrolling and be done with this. Otherwise we may open some
575 // dialogs (Jug 20020424).
579 Inset * inset_hit = checkInsetHit(bv_->text, xpos, ypos);
581 // Middle button press pastes if we have a selection
582 // We do this here as if the selection was inside an inset
583 // it could get cleared on the unlocking of the inset so
584 // we have to check this first
585 bool paste_internally = false;
586 if (button == mouse_button::button2 && bv_->getLyXText()->selection.set()) {
587 owner_->dispatch(FuncRequest(LFUN_COPY));
588 paste_internally = true;
591 int const screen_first = bv_->text->first_y;
593 if (bv_->theLockingInset()) {
594 // We are in inset locking mode
596 // Check whether the inset was hit. If not reset mode,
597 // otherwise give the event to the inset
598 if (inset_hit == bv_->theLockingInset()) {
599 FuncRequest cmd(bv_, LFUN_MOUSE_PRESS, xpos, ypos, button);
600 bv_->theLockingInset()->localDispatch(cmd);
603 bv_->unlockInset(bv_->theLockingInset());
607 selection_possible = true;
608 screen().hideCursor();
610 // Clear the selection
611 screen().toggleSelection(bv_->text, bv_);
612 bv_->text->clearSelection();
613 bv_->text->fullRebreak(bv_);
617 // Single left click in math inset?
618 if (isHighlyEditableInset(inset_hit)) {
619 // Highly editable inset, like math
620 UpdatableInset * inset = static_cast<UpdatableInset *>(inset_hit);
621 selection_possible = false;
622 owner_->updateLayoutChoice();
623 owner_->message(inset->editMessage());
624 //inset->edit(bv_, xpos, ypos, button);
625 // We just have to lock the inset before calling a PressEvent on it!
626 // we don't need the edit() call here! (Jug20020329)
627 if (!bv_->lockInset(inset)) {
628 lyxerr[Debug::INSETS] << "Cannot lock inset" << endl;
630 FuncRequest cmd(bv_, LFUN_MOUSE_PRESS, xpos, ypos, button);
631 inset->localDispatch(cmd);
634 // I'm not sure we should continue here if we hit an inset (Jug20020403)
636 // Right click on a footnote flag opens float menu
637 if (button == mouse_button::button3) {
638 selection_possible = false;
642 if (!inset_hit) // otherwise it was already set in checkInsetHit(...)
643 bv_->text->setCursorFromCoordinates(bv_, xpos, ypos + screen_first);
645 bv_->text->selection.cursor = bv_->text->cursor;
646 bv_->text->cursor.x_fix(bv_->text->cursor.x());
648 owner_->updateLayoutChoice();
650 selection_possible = false;
653 // Insert primary selection with middle mouse
654 // if there is a local selection in the current buffer,
656 if (button == mouse_button::button2) {
657 if (paste_internally)
658 owner_->dispatch(FuncRequest(LFUN_PASTE));
660 owner_->dispatch(FuncRequest(LFUN_PASTESELECTION, "paragraph"));
661 selection_possible = false;
667 void BufferView::Pimpl::doubleClick(int /*x*/, int /*y*/, mouse_button::state button)
672 LyXText * text = bv_->getLyXText();
674 if (text->bv_owner && bv_->theLockingInset())
677 if (button == mouse_button::button1) {
678 if (text->bv_owner) {
679 screen().hideCursor();
680 screen().toggleSelection(text, bv_);
681 text->selectWord(bv_, LyXText::WHOLE_WORD_STRICT);
682 screen().toggleSelection(text, bv_, false);
684 text->selectWord(bv_, LyXText::WHOLE_WORD_STRICT);
686 // This will fit the cursor on the screen if necessary
687 update(text, BufferView::SELECT|BufferView::FITCUR);
688 workarea().haveSelection(bv_->getLyXText()->selection.set());
693 void BufferView::Pimpl::tripleClick(int /*x*/, int /*y*/, mouse_button::state button)
698 LyXText * text = bv_->getLyXText();
700 if (text->bv_owner && bv_->theLockingInset())
703 if (button == mouse_button::button1) {
704 if (text->bv_owner) {
705 screen().hideCursor();
706 screen().toggleSelection(text, bv_);
708 text->cursorHome(bv_);
709 text->selection.cursor = text->cursor;
710 text->cursorEnd(bv_);
711 text->setSelection(bv_);
712 if (text->bv_owner) {
713 screen().toggleSelection(text, bv_, false);
715 // This will fit the cursor on the screen if necessary
716 update(text, BufferView::SELECT|BufferView::FITCUR);
717 workarea().haveSelection(bv_->getLyXText()->selection.set());
722 void BufferView::Pimpl::selectionRequested()
729 LyXText * text = bv_->getLyXText();
731 if (text->selection.set() &&
732 (!bv_->text->xsel_cache.set() ||
733 text->selection.start != bv_->text->xsel_cache.start ||
734 text->selection.end != bv_->text->xsel_cache.end))
736 bv_->text->xsel_cache = text->selection;
737 sel = text->selectionAsString(bv_->buffer(), false);
738 } else if (!text->selection.set()) {
740 bv_->text->xsel_cache.set(false);
743 workarea().putClipboard(sel);
748 void BufferView::Pimpl::selectionLost()
753 bv_->getLyXText()->clearSelection();
755 bv_->text->xsel_cache.set(false);
760 void BufferView::Pimpl::workAreaButtonRelease(int x, int y,
761 mouse_button::state button)
763 // do nothing if we used the mouse wheel
764 if (!buffer_ || button == mouse_button::button4 || button == mouse_button::button5)
767 // If we hit an inset, we have the inset coordinates in these
768 // and inset_hit points to the inset. If we do not hit an
769 // inset, inset_hit is 0, and inset_x == x, inset_y == y.
770 Inset * inset_hit = checkInsetHit(bv_->text, x, y);
772 if (bv_->theLockingInset()) {
773 // We are in inset locking mode.
775 // LyX does a kind of work-area grabbing for insets.
776 // Only a ButtonPress FuncRequest outside the inset will
777 // force a insetUnlock.
778 FuncRequest cmd(bv_, LFUN_MOUSE_RELEASE, x, y, button);
779 bv_->theLockingInset()->localDispatch(cmd);
783 selection_possible = false;
785 if (button == mouse_button::button2)
789 if (button == mouse_button::button1) {
790 workarea().haveSelection(bv_->getLyXText()->selection.set());
794 owner_->view_state_changed();
795 owner_->updateMenubar();
796 owner_->updateToolbar();
798 // Did we hit an editable inset?
800 selection_possible = false;
802 // if we reach this point with a selection, it
803 // must mean we are currently selecting.
804 // But we don't want to open the inset
805 // because that is annoying for the user.
806 // So just pretend we didn't hit it.
807 // this is OK because a "kosher" ButtonRelease
808 // will follow a ButtonPress that clears
810 // Note this also fixes selection drawing
811 // problems if we end up opening an inset
812 if (bv_->getLyXText()->selection.set())
815 // CHECK fix this proper in 0.13
816 // well, maybe 13.0 !!!!!!!!!
818 // Following a ref shouldn't issue
819 // a push on the undo-stack
820 // anylonger, now that we have
821 // keybindings for following
822 // references and returning from
823 // references. IMHO though, it
824 // should be the inset's own business
825 // to push or not push on the undo
826 // stack. They don't *have* to
827 // alter the document...
829 // ...or maybe the SetCursorParUndo()
830 // below isn't necessary at all anylonger?
831 if (inset_hit->lyxCode() == Inset::REF_CODE) {
832 setCursorParUndo(bv_);
835 owner_->message(inset_hit->editMessage());
837 if (isHighlyEditableInset(inset_hit)) {
838 // Highly editable inset, like math
839 UpdatableInset *inset = (UpdatableInset *)inset_hit;
840 FuncRequest cmd(bv_, LFUN_MOUSE_RELEASE, x, y, button);
841 inset->localDispatch(cmd);
843 FuncRequest cmd(bv_, LFUN_MOUSE_RELEASE, x, y, button);
844 inset_hit->localDispatch(cmd);
845 // IMO this is a grosshack! Inset's should be changed so that
846 // they call the actions they have to do with the insetButtonRel.
847 // function and not in the edit(). This should be changed
850 #warning Please remove donot call inset->edit() here (Jug 20020812)
852 inset_hit->edit(bv_, x, y, button);
857 // Maybe we want to edit a bibitem ale970302
858 if (bv_->text->cursor.par()->bibkey) {
859 bool const is_rtl = bv_->text->cursor.par()->isRightToLeftPar(buffer_->params);
860 int const width = bibitemMaxWidth(bv_, buffer_->params.getLyXTextClass().defaultfont());
861 if ((is_rtl && x > bv_->text->workWidth(bv_)-20-width) ||
862 (!is_rtl && x < 20+width)) {
863 bv_->text->cursor.par()->bibkey->edit(bv_, 0, 0, mouse_button::none);
873 Inset * BufferView::Pimpl::checkInsetHit(LyXText * text, int & x, int & y)
875 int y_tmp = y + text->first_y;
878 text->setCursorFromCoordinates(bv_, cursor, x, y_tmp);
880 Inset * inset = checkInset(bv_, *text, cursor, x, y_tmp);
887 // look at previous position
888 if (cursor.pos() == 0) {
893 text->setCursor(bv_, cursor, cursor.par(), cursor.pos() - 1, true);
895 inset = checkInset(bv_, *text, cursor, x, y_tmp);
903 void BufferView::Pimpl::workAreaResize()
905 static int work_area_width;
906 static int work_area_height;
908 bool const widthChange = workarea().workWidth() != work_area_width;
909 bool const heightChange = workarea().workHeight() != work_area_height;
911 // update from work area
912 work_area_width = workarea().workWidth();
913 work_area_height = workarea().workHeight();
917 // The visible LyXView need a resize
918 resizeCurrentBuffer();
920 // Remove all texts from the textcache
921 // This is not _really_ what we want to do. What
922 // we really want to do is to delete in textcache
923 // that does not have a BufferView with matching
924 // width, but as long as we have only one BufferView
925 // deleting all gives the same result.
926 if (lyxerr.debugging())
927 textcache.show(lyxerr, "Expose delete all");
929 // FIXME: this is already done in resizeCurrentBuffer() ??
930 buffer_->resizeInsets(bv_);
931 } else if (heightChange) {
932 // fitCursor() ensures we don't jump back
933 // to the start of the document on vertical
939 if (widthChange || heightChange) {
943 // always make sure that the scrollbar is sane.
945 owner_->updateLayoutChoice();
950 void BufferView::Pimpl::update()
952 if (!bv_->theLockingInset() || !bv_->theLockingInset()->nodraw()) {
953 LyXText::text_status st = bv_->text->status();
954 screen().update(bv_->text, bv_);
956 while (bv_->text->status() == LyXText::CHANGED_IN_DRAW) {
957 bv_->text->fullRebreak(bv_);
958 st = LyXText::NEED_MORE_REFRESH;
959 bv_->text->setCursor(bv_, bv_->text->cursor.par(),
960 bv_->text->cursor.pos());
961 if (bv_->text->selection.set()) {
962 bv_->text->setCursor(bv_, bv_->text->selection.start,
963 bv_->text->selection.start.par(),
964 bv_->text->selection.start.pos());
965 bv_->text->setCursor(bv_, bv_->text->selection.end,
966 bv_->text->selection.end.par(),
967 bv_->text->selection.end.pos());
970 bv_->text->status(bv_, st);
971 screen().update(bv_->text, bv_);
973 // do this here instead of in the screen::update because of
975 bv_->text->status(bv_, LyXText::UNCHANGED);
981 // Values used when calling update:
983 // -2 - update, move sel_cursor if selection, fitcursor
984 // -1 - update, move sel_cursor if selection, fitcursor, mark dirty
985 // 0 - update, move sel_cursor if selection, fitcursor
986 // 1 - update, move sel_cursor if selection, fitcursor, mark dirty
987 // 3 - update, move sel_cursor if selection
990 // a simple redraw of the parts that need refresh
992 // move sel_cursor if selection -
993 // the text's sel_cursor is moved if there is selection is progress
996 // fitCursor() is called and the scrollbar updated
999 // the buffer is marked dirty.
1008 // UPDATE_ONLY = UPDATE;
1009 // UPDATE_SELECT = UPDATE | SELECT;
1010 // UPDATE_SELECT_MOVE = UPDATE | SELECT | FITCUR;
1011 // UPDATE_SELECT_MOVE_AFTER_CHANGE = UPDATE | SELECT | FITCUR | CHANGE;
1013 // update(-3) -> update(0) -> update(0) -> update(UPDATE)
1014 // update(-2) -> update(1 + 2) -> update(3) -> update(SELECT|FITCUR)
1015 // update(-1) -> update(1 + 2 + 4) -> update(7) -> update(SELECT|FITCUR|CHANGE)
1016 // update(1) -> update(1 + 2 + 4) -> update(7) -> update(SELECT|FITCUR|CHANGE)
1017 // update(3) -> update(1) -> update(1) -> update(SELECT)
1019 void BufferView::Pimpl::update(LyXText * text, BufferView::UpdateCodes f)
1021 owner_->updateLayoutChoice();
1023 if (!text->selection.set() && (f & SELECT)) {
1024 text->selection.cursor = text->cursor;
1027 text->fullRebreak(bv_);
1029 if (text->inset_owner) {
1030 text->inset_owner->setUpdateStatus(bv_, InsetText::NONE);
1031 updateInset(text->inset_owner, false);
1041 buffer_->markDirty();
1046 // Callback for cursor timer
1047 void BufferView::Pimpl::cursorToggle()
1050 cursor_timeout.restart();
1055 extern void reapSpellchecker(void);
1058 if (!bv_->theLockingInset()) {
1059 screen().cursorToggle(bv_);
1061 bv_->theLockingInset()->toggleInsetCursor(bv_);
1064 cursor_timeout.restart();
1068 bool BufferView::Pimpl::available() const
1070 if (buffer_ && bv_->text)
1076 void BufferView::Pimpl::beforeChange(LyXText * text)
1079 text->clearSelection();
1083 void BufferView::Pimpl::savePosition(unsigned int i)
1085 if (i >= saved_positions_num)
1087 saved_positions[i] = Position(buffer_->fileName(),
1088 bv_->text->cursor.par()->id(),
1089 bv_->text->cursor.pos());
1092 str << _("Saved bookmark") << ' ' << i;
1093 owner_->message(str.str().c_str());
1098 void BufferView::Pimpl::restorePosition(unsigned int i)
1100 if (i >= saved_positions_num)
1103 string const fname = saved_positions[i].filename;
1105 beforeChange(bv_->text);
1107 if (fname != buffer_->fileName()) {
1108 Buffer * b = bufferlist.exists(fname) ?
1109 bufferlist.getBuffer(fname) :
1110 bufferlist.loadLyXFile(fname); // don't ask, just load it
1111 if (b != 0) buffer(b);
1114 Paragraph * par = buffer_->getParFromID(saved_positions[i].par_id);
1118 bv_->text->setCursor(bv_, par,
1119 min(par->size(), saved_positions[i].par_pos));
1121 update(bv_->text, BufferView::SELECT | BufferView::FITCUR);
1124 str << _("Moved to bookmark") << ' ' << i;
1125 owner_->message(str.str().c_str());
1130 bool BufferView::Pimpl::isSavedPosition(unsigned int i)
1132 if (i >= saved_positions_num)
1135 return !saved_positions[i].filename.empty();
1139 void BufferView::Pimpl::switchKeyMap()
1141 if (!lyxrc.rtl_support)
1144 LyXText * text = bv_->getLyXText();
1145 if (text->real_current_font.isRightToLeft()
1146 && !(bv_->theLockingInset()
1147 && bv_->theLockingInset()->lyxCode() == Inset::ERT_CODE))
1149 if (owner_->getIntl().keymap == Intl::PRIMARY)
1150 owner_->getIntl().KeyMapSec();
1152 if (owner_->getIntl().keymap == Intl::SECONDARY)
1153 owner_->getIntl().KeyMapPrim();
1158 void BufferView::Pimpl::insetUnlock()
1160 if (bv_->theLockingInset()) {
1161 bv_->theLockingInset()->insetUnlock(bv_);
1162 bv_->theLockingInset(0);
1168 void BufferView::Pimpl::showCursor()
1170 if (bv_->theLockingInset())
1171 bv_->theLockingInset()->showInsetCursor(bv_);
1173 screen().showCursor(bv_->text, bv_);
1177 void BufferView::Pimpl::hideCursor()
1179 if (!bv_->theLockingInset())
1180 screen().hideCursor();
1184 void BufferView::Pimpl::toggleSelection(bool b)
1186 if (bv_->theLockingInset())
1187 bv_->theLockingInset()->toggleSelection(bv_, b);
1188 screen().toggleSelection(bv_->text, bv_, b);
1192 void BufferView::Pimpl::toggleToggle()
1194 screen().toggleToggle(bv_->text, bv_);
1198 void BufferView::Pimpl::center()
1200 LyXText * t = bv_->text;
1203 int const half_height = workarea().workHeight() / 2;
1206 if (t->cursor.y() > half_height) {
1207 new_y = t->cursor.y() - half_height;
1210 // FIXME: can we do this w/o calling screen directly ?
1211 // This updates first_y but means the fitCursor() call
1212 // from the update(FITCUR) doesn't realise that we might
1213 // have moved (e.g. from GOTOPARAGRAPH), so doesn't cause
1214 // the scrollbar to be updated as it should, so we have
1215 // to do it manually. Any operation that does a center()
1216 // and also might have moved first_y must make sure to call
1217 // updateScrollbar() currently. Never mind that this is a
1218 // pretty obfuscated way of updating t->first_y
1219 screen().draw(t, bv_, new_y);
1221 update(t, BufferView::SELECT | BufferView::FITCUR);
1225 void BufferView::Pimpl::stuffClipboard(string const & stuff) const
1227 workarea().putClipboard(stuff);
1232 * Dispatch functions for actions which can be valid for BufferView->text
1233 * and/or InsetText->text!!!
1237 Inset * BufferView::Pimpl::getInsetByCode(Inset::Code code)
1240 LyXCursor cursor = bv_->getLyXText()->cursor;
1241 Buffer::inset_iterator it =
1242 find_if(Buffer::inset_iterator(
1243 cursor.par(), cursor.pos()),
1244 buffer_->inset_iterator_end(),
1245 lyx::compare_memfun(&Inset::lyxCode, code));
1246 return it != buffer_->inset_iterator_end() ? (*it) : 0;
1248 // Ok, this is a little bit too brute force but it
1249 // should work for now. Better infrastructure is comming. (Lgb)
1251 Buffer * b = bv_->buffer();
1252 LyXCursor cursor = bv_->getLyXText()->cursor;
1254 Buffer::inset_iterator beg = b->inset_iterator_begin();
1255 Buffer::inset_iterator end = b->inset_iterator_end();
1257 bool cursor_par_seen = false;
1259 for (; beg != end; ++beg) {
1260 if (beg.getPar() == cursor.par()) {
1261 cursor_par_seen = true;
1263 if (cursor_par_seen) {
1264 if (beg.getPar() == cursor.par()
1265 && beg.getPos() >= cursor.pos()) {
1267 } else if (beg.getPar() != cursor.par()) {
1274 // Now find the first inset that matches code.
1275 for (; beg != end; ++beg) {
1276 if (beg->lyxCode() == code) {
1286 void BufferView::Pimpl::MenuInsertLyXFile(string const & filen)
1288 string filename = filen;
1290 if (filename.empty()) {
1291 // Launch a file browser
1292 string initpath = lyxrc.document_path;
1295 string const trypath = owner_->buffer()->filePath();
1296 // If directory is writeable, use this as default.
1297 if (IsDirWriteable(trypath))
1301 FileDialog fileDlg(bv_->owner(),
1302 _("Select LyX document to insert"),
1304 make_pair(string(_("Documents|#o#O")),
1305 string(lyxrc.document_path)),
1306 make_pair(string(_("Examples|#E#e")),
1307 string(AddPath(system_lyxdir, "examples"))));
1309 FileDialog::Result result =
1310 fileDlg.Select(initpath,
1311 _("*.lyx| LyX Documents (*.lyx)"));
1313 if (result.first == FileDialog::Later)
1316 filename = result.second;
1318 // check selected filename
1319 if (filename.empty()) {
1320 owner_->message(_("Canceled."));
1325 // get absolute path of file and add ".lyx" to the filename if
1327 filename = FileSearch(string(), filename, "lyx");
1329 string const disp_fn(MakeDisplayPath(filename));
1332 s1 << _("Inserting document") << ' '
1333 << disp_fn << " ...";
1334 owner_->message(s1.str().c_str());
1335 bool const res = bv_->insertLyXFile(filename);
1338 str << _("Document") << ' ' << disp_fn
1339 << ' ' << _("inserted.");
1340 owner_->message(str.str().c_str());
1343 str << _("Could not insert document") << ' '
1345 owner_->message(str.str().c_str());
1350 bool BufferView::Pimpl::dispatch(FuncRequest const & ev)
1352 lyxerr[Debug::ACTION] << "BufferView::Pimpl::Dispatch: action["
1353 << ev.action <<"] arg[" << ev.argument << "]" << endl;
1355 LyXTextClass const & tclass = buffer_->params.getLyXTextClass();
1357 switch (ev.action) {
1359 case LFUN_TOC_INSERT:
1361 InsetCommandParams p;
1362 p.setCmdName("tableofcontents");
1363 Inset * inset = new InsetTOC(p);
1364 if (!insertInset(inset, tclass.defaultLayoutName()))
1369 case LFUN_SCROLL_INSET:
1370 // this is not handled here as this function is only active
1371 // if we have a locking_inset and that one is (or contains)
1375 case LFUN_INSET_GRAPHICS:
1377 Inset * new_inset = new InsetGraphics;
1378 if (!insertInset(new_inset)) {
1381 // this is need because you don't use a inset->Edit()
1382 updateInset(new_inset, true);
1383 new_inset->edit(bv_);
1388 case LFUN_LAYOUT_COPY:
1389 bv_->copyEnvironment();
1392 case LFUN_LAYOUT_PASTE:
1393 bv_->pasteEnvironment();
1397 case LFUN_DEPTH_MIN:
1398 changeDepth(bv_, bv_->getLyXText(), -1);
1401 case LFUN_DEPTH_PLUS:
1402 changeDepth(bv_, bv_->getLyXText(), 1);
1406 owner_->getDialogs().setUserFreeFont();
1409 case LFUN_FILE_INSERT:
1410 MenuInsertLyXFile(ev.argument);
1413 case LFUN_FILE_INSERT_ASCII_PARA:
1414 InsertAsciiFile(bv_, ev.argument, true);
1417 case LFUN_FILE_INSERT_ASCII:
1418 InsertAsciiFile(bv_, ev.argument, false);
1422 lang(bv_, ev.argument);
1424 owner_->view_state_changed();
1429 owner_->view_state_changed();
1434 owner_->view_state_changed();
1439 owner_->view_state_changed();
1444 owner_->view_state_changed();
1449 owner_->view_state_changed();
1454 owner_->view_state_changed();
1459 owner_->view_state_changed();
1462 case LFUN_UNDERLINE:
1464 owner_->view_state_changed();
1467 case LFUN_FONT_SIZE:
1468 fontSize(bv_, ev.argument);
1469 owner_->view_state_changed();
1472 case LFUN_FONT_STATE:
1473 owner_->getLyXFunc().setMessage(currentState(bv_));
1476 case LFUN_INSERT_LABEL:
1477 MenuInsertLabel(bv_, ev.argument);
1480 case LFUN_REF_INSERT:
1481 if (ev.argument.empty()) {
1482 InsetCommandParams p("ref");
1483 owner_->getDialogs().createRef(p.getAsString());
1485 InsetCommandParams p;
1486 p.setFromString(ev.argument);
1488 InsetRef * inset = new InsetRef(p, *buffer_);
1489 if (!insertInset(inset))
1492 updateInset(inset, true);
1496 case LFUN_BOOKMARK_SAVE:
1497 savePosition(strToUnsignedInt(ev.argument));
1500 case LFUN_BOOKMARK_GOTO:
1501 restorePosition(strToUnsignedInt(ev.argument));
1506 string label = ev.argument;
1507 if (label.empty()) {
1509 static_cast<InsetRef*>(getInsetByCode(Inset::REF_CODE));
1511 label = inset->getContents();
1516 if (!label.empty()) {
1517 //bv_->savePosition(0);
1518 if (!bv_->gotoLabel(label))
1519 Alert::alert(_("Error"),
1520 _("Couldn't find this label"),
1521 _("in current document."));
1529 InsetCommandParams p;
1530 if (ev.action == LFUN_HTMLURL)
1531 p.setCmdName("htmlurl");
1533 p.setCmdName("url");
1534 owner_->getDialogs().createUrl(p.getAsString());
1538 case LFUN_INSERT_URL:
1540 InsetCommandParams p;
1541 p.setFromString(ev.argument);
1543 InsetUrl * inset = new InsetUrl(p);
1544 if (!insertInset(inset))
1547 updateInset(inset, true);
1551 case LFUN_INSET_CAPTION:
1553 // Do we have a locking inset...
1554 if (bv_->theLockingInset()) {
1555 lyxerr << "Locking inset code: "
1556 << static_cast<int>(bv_->theLockingInset()->lyxCode());
1557 InsetCaption * new_inset =
1558 new InsetCaption(buffer_->params);
1559 new_inset->setOwner(bv_->theLockingInset());
1560 new_inset->setAutoBreakRows(true);
1561 new_inset->setDrawFrame(0, InsetText::LOCKED);
1562 new_inset->setFrameColor(0, LColor::captionframe);
1563 if (insertInset(new_inset))
1564 new_inset->edit(bv_);
1572 // --- accented characters ---------------------------
1575 case LFUN_CIRCUMFLEX:
1585 case LFUN_SPECIAL_CARON:
1588 case LFUN_HUNG_UMLAUT:
1591 if (ev.argument.empty()) {
1593 owner_->getLyXFunc().handleKeyFunc(ev.action);
1595 owner_->getLyXFunc().handleKeyFunc(ev.action);
1596 owner_->getIntl().getTransManager()
1597 .TranslateAndInsert(ev.argument[0], bv_->getLyXText());
1598 update(bv_->getLyXText(),
1600 | BufferView::FITCUR
1601 | BufferView::CHANGE);
1605 case LFUN_MATH_MACRO:
1606 case LFUN_MATH_DELIM:
1607 case LFUN_INSERT_MATRIX:
1608 case LFUN_INSERT_MATH:
1609 case LFUN_MATH_IMPORT_SELECTION: // Imports LaTeX from the X selection
1610 case LFUN_MATH_DISPLAY: // Open or create a displayed math inset
1611 case LFUN_MATH_MODE: // Open or create an inlined math inset
1612 case LFUN_GREEK: // Insert a single greek letter
1613 mathDispatch(FuncRequest(bv_, ev.action, ev.argument));
1616 case LFUN_CITATION_INSERT:
1618 InsetCommandParams p;
1619 p.setFromString(ev.argument);
1621 InsetCitation * inset = new InsetCitation(p);
1622 if (!insertInset(inset))
1625 updateInset(inset, true);
1629 case LFUN_INSERT_BIBTEX:
1631 // ale970405+lasgoutt970425
1632 // The argument can be up to two tokens separated
1633 // by a space. The first one is the bibstyle.
1634 string const db = token(ev.argument, ' ', 0);
1635 string bibstyle = token(ev.argument, ' ', 1);
1636 if (bibstyle.empty())
1639 InsetCommandParams p("BibTeX", db, bibstyle);
1640 InsetBibtex * inset = new InsetBibtex(p);
1642 if (insertInset(inset)) {
1643 if (ev.argument.empty())
1650 // BibTeX data bases
1651 case LFUN_BIBDB_ADD:
1653 InsetBibtex * inset =
1654 static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
1656 inset->addDatabase(ev.argument);
1661 case LFUN_BIBDB_DEL:
1663 InsetBibtex * inset =
1664 static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
1666 inset->delDatabase(ev.argument);
1671 case LFUN_BIBTEX_STYLE:
1673 InsetBibtex * inset =
1674 static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
1676 inset->setOptions(ev.argument);
1681 case LFUN_INDEX_INSERT:
1683 string entry = ev.argument;
1685 entry = bv_->getLyXText()->getStringToIndex(bv_);
1687 if (entry.empty()) {
1688 owner_->getDialogs().createIndex();
1692 InsetIndex * inset = new InsetIndex(InsetCommandParams("index", entry));
1694 if (!insertInset(inset)) {
1697 updateInset(inset, true);
1702 case LFUN_INDEX_PRINT:
1704 InsetCommandParams p("printindex");
1705 Inset * inset = new InsetPrintIndex(p);
1706 if (!insertInset(inset, tclass.defaultLayoutName()))
1711 case LFUN_PARENTINSERT:
1713 InsetCommandParams p("lyxparent", ev.argument);
1714 Inset * inset = new InsetParent(p, *buffer_);
1715 if (!insertInset(inset, tclass.defaultLayoutName()))
1721 case LFUN_CHILD_INSERT:
1723 InsetInclude::Params p;
1724 p.cparams.setFromString(ev.argument);
1725 p.masterFilename_ = buffer_->fileName();
1727 InsetInclude * inset = new InsetInclude(p);
1728 if (!insertInset(inset))
1731 updateInset(inset, true);
1732 bv_->owner()->getDialogs().showInclude(inset);
1737 case LFUN_FLOAT_LIST:
1738 if (tclass.floats().typeExist(ev.argument)) {
1739 Inset * inset = new InsetFloatList(ev.argument);
1740 if (!insertInset(inset, tclass.defaultLayoutName()))
1743 lyxerr << "Non-existent float type: "
1744 << ev.argument << endl;
1748 case LFUN_THESAURUS_ENTRY:
1750 string arg = ev.argument;
1753 arg = bv_->getLyXText()->selectionAsString(buffer_,
1757 if (arg.size() > 100 || arg.empty()) {
1758 // Get word or selection
1759 bv_->getLyXText()->selectWordWhenUnderCursor(bv_, LyXText::WHOLE_WORD);
1760 arg = bv_->getLyXText()->selectionAsString(buffer_, false);
1761 // FIXME: where is getLyXText()->unselect(bv_) ?
1765 bv_->owner()->getDialogs().showThesaurus(arg);
1769 case LFUN_UNKNOWN_ACTION:
1770 ev.errorMessage(N_("Unknown function!"));
1774 FuncRequest cmd = ev;
1776 return bv_->getLyXText()->dispatch(cmd);
1783 bool BufferView::Pimpl::insertInset(Inset * inset, string const & lout)
1785 // if we are in a locking inset we should try to insert the
1786 // inset there otherwise this is a illegal function now
1787 if (bv_->theLockingInset()) {
1788 if (bv_->theLockingInset()->insetAllowed(inset))
1789 return bv_->theLockingInset()->insertInset(bv_, inset);
1793 // not quite sure if we want this...
1794 setCursorParUndo(bv_);
1797 beforeChange(bv_->text);
1798 if (!lout.empty()) {
1799 update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
1800 bv_->text->breakParagraph(bv_);
1801 update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
1803 if (!bv_->text->cursor.par()->empty()) {
1804 bv_->text->cursorLeft(bv_);
1806 bv_->text->breakParagraph(bv_);
1807 update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
1811 LyXTextClass const & tclass =
1812 buffer_->params.getLyXTextClass();
1813 bool hasLayout = tclass.hasLayout(lres);
1814 string lay = tclass.defaultLayoutName();
1816 if (hasLayout != false) {
1820 // layout not fount using default
1821 lay = tclass.defaultLayoutName();
1824 bv_->text->setLayout(bv_, lay);
1826 bv_->text->setParagraph(bv_, 0, 0,
1828 VSpace(VSpace::NONE), VSpace(VSpace::NONE),
1833 update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
1836 bv_->text->insertInset(bv_, inset);
1837 update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
1844 void BufferView::Pimpl::updateInset(Inset * inset, bool mark_dirty)
1846 if (!inset || !available())
1849 // first check for locking insets
1850 if (bv_->theLockingInset()) {
1851 if (bv_->theLockingInset() == inset) {
1852 if (bv_->text->updateInset(bv_, inset)) {
1855 buffer_->markDirty();
1860 } else if (bv_->theLockingInset()->updateInsetInInset(bv_, inset)) {
1861 if (bv_->text->updateInset(bv_, bv_->theLockingInset())) {
1864 buffer_->markDirty();
1872 // then check if the inset is a top_level inset (has no owner)
1873 // if yes do the update as always otherwise we have to update the
1874 // toplevel inset where this inset is inside
1875 Inset * tl_inset = inset;
1876 while (tl_inset->owner())
1877 tl_inset = tl_inset->owner();
1879 if (tl_inset == inset) {
1880 update(bv_->text, BufferView::UPDATE);
1881 if (bv_->text->updateInset(bv_, inset)) {
1885 | BufferView::FITCUR
1886 | BufferView::CHANGE);
1888 update(bv_->text, SELECT);
1892 } else if (static_cast<UpdatableInset *>(tl_inset)
1893 ->updateInsetInInset(bv_, inset))
1895 if (bv_->text->updateInset(bv_, tl_inset)) {