]> git.lyx.org Git - lyx.git/blob - src/BufferView_pimpl.C
move some stuff from BufferView_pimpl.C to text3.C and lose a few lines...
[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 "lyxtext.h"
24 #include "lyxrow.h"
25 #include "paragraph.h"
26 #include "frontends/LyXView.h"
27 #include "commandtags.h"
28 #include "lyxfunc.h"
29 #include "debug.h"
30 #include "bufferview_funcs.h"
31 #include "TextCache.h"
32 #include "bufferlist.h"
33 #include "lyxrc.h"
34 #include "intl.h"
35 // added for Dispatch functions
36 #include "lyx_cb.h"
37 #include "lyx_main.h"
38 #include "FloatList.h"
39 #include "gettext.h"
40 #include "ParagraphParameters.h"
41 #include "undo_funcs.h"
42 #include "funcrequest.h"
43 #include "language.h"
44
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/insetnote.h"
54 #include "insets/insetinclude.h"
55 #include "insets/insetcite.h"
56 #include "insets/insetert.h"
57 #include "insets/insetexternal.h"
58 #include "insets/insetgraphics.h"
59 #include "insets/insetfoot.h"
60 #include "insets/insetmarginal.h"
61 #include "insets/insetminipage.h"
62 #include "insets/insetfloat.h"
63 #include "insets/insettabular.h"
64 #if 0
65 #include "insets/insettheorem.h"
66 #include "insets/insetlist.h"
67 #endif
68 #include "insets/insetcaption.h"
69 #include "insets/insetfloatlist.h"
70
71 #include "mathed/formulabase.h"
72
73 #include "graphics/Previews.h"
74
75 #include "support/LAssert.h"
76 #include "support/lstrings.h"
77 #include "support/filetools.h"
78 #include "support/lyxfunctional.h"
79
80 #include <boost/bind.hpp>
81 #include <boost/signals/connection.hpp>
82
83 #include <cstdio>
84 #include <ctime>
85 #include <unistd.h>
86 #include <sys/wait.h>
87 #include <clocale>
88
89
90 #ifndef CXX_GLOBAL_CSTD
91 using std::tm;
92 using std::localtime;
93 using std::time;
94 using std::setlocale;
95 using std::strftime;
96 #endif
97
98 using std::vector;
99 using std::find_if;
100 using std::find;
101 using std::pair;
102 using std::endl;
103 using std::make_pair;
104 using std::min;
105
106 using lyx::pos_type;
107 using lyx::textclass_type;
108
109 /* the selection possible is needed, that only motion events are
110  * used, where the bottom press event was on the drawing area too */
111 bool selection_possible = false;
112
113 extern BufferList bufferlist;
114 extern char ascii_type;
115
116 extern int bibitemMaxWidth(BufferView *, LyXFont const &);
117
118
119 namespace {
120
121 unsigned int const saved_positions_num = 20;
122
123 // All the below connection objects are needed because of a bug in some
124 // versions of GCC (<=2.96 are on the suspects list.) By having and assigning
125 // to these connections we avoid a segfault upon startup, and also at exit.
126 // (Lgb)
127
128 boost::signals::connection timecon;
129 boost::signals::connection doccon;
130 boost::signals::connection resizecon;
131 boost::signals::connection bpresscon;
132 boost::signals::connection breleasecon;
133 boost::signals::connection motioncon;
134 boost::signals::connection doublecon;
135 boost::signals::connection triplecon;
136 boost::signals::connection kpresscon;
137 boost::signals::connection selectioncon;
138 boost::signals::connection lostcon;
139
140 } // anon namespace
141
142
143 BufferView::Pimpl::Pimpl(BufferView * bv, LyXView * owner,
144              int xpos, int ypos, int width, int height)
145         : bv_(bv), owner_(owner), buffer_(0), cursor_timeout(400),
146           using_xterm_cursor(false)
147 {
148         workarea_.reset(WorkAreaFactory::create(xpos, ypos, width, height));
149         screen_.reset(LyXScreenFactory::create(workarea()));
150
151         // Setup the signals
152         doccon = workarea().scrollDocView
153                 .connect(boost::bind(&BufferView::Pimpl::scrollDocView, this, _1));
154         resizecon = workarea().workAreaResize
155                 .connect(boost::bind(&BufferView::Pimpl::workAreaResize, this));
156         bpresscon = workarea().workAreaButtonPress
157                 .connect(boost::bind(&BufferView::Pimpl::workAreaButtonPress, this, _1, _2, _3));
158         breleasecon = workarea().workAreaButtonRelease
159                 .connect(boost::bind(&BufferView::Pimpl::workAreaButtonRelease, this, _1, _2, _3));
160         motioncon = workarea().workAreaMotionNotify
161                 .connect(boost::bind(&BufferView::Pimpl::workAreaMotionNotify, this, _1, _2, _3));
162         doublecon = workarea().workAreaDoubleClick
163                 .connect(boost::bind(&BufferView::Pimpl::doubleClick, this, _1, _2, _3));
164         triplecon = workarea().workAreaTripleClick
165                 .connect(boost::bind(&BufferView::Pimpl::tripleClick, this, _1, _2, _3));
166         kpresscon = workarea().workAreaKeyPress
167                 .connect(boost::bind(&BufferView::Pimpl::workAreaKeyPress, this, _1, _2));
168         selectioncon = workarea().selectionRequested
169                 .connect(boost::bind(&BufferView::Pimpl::selectionRequested, this));
170         lostcon = workarea().selectionLost
171                 .connect(boost::bind(&BufferView::Pimpl::selectionLost, this));
172
173         timecon = cursor_timeout.timeout
174                 .connect(boost::bind(&BufferView::Pimpl::cursorToggle, this));
175         cursor_timeout.start();
176         saved_positions.resize(saved_positions_num);
177 }
178
179
180 WorkArea & BufferView::Pimpl::workarea() const
181 {
182         return *workarea_.get();
183 }
184
185
186 LyXScreen & BufferView::Pimpl::screen() const
187 {
188         return *screen_.get();
189 }
190
191
192 Painter & BufferView::Pimpl::painter() const
193 {
194         return workarea().getPainter();
195 }
196
197
198 void BufferView::Pimpl::buffer(Buffer * b)
199 {
200         lyxerr[Debug::INFO] << "Setting buffer in BufferView ("
201                             << b << ")" << endl;
202         if (buffer_) {
203                 buffer_->delUser(bv_);
204
205                 // Put the old text into the TextCache, but
206                 // only if the buffer is still loaded.
207                 // Also set the owner of the test to 0
208                 //              bv_->text->owner(0);
209                 textcache.add(buffer_, workarea().workWidth(), bv_->text);
210                 if (lyxerr.debugging())
211                         textcache.show(lyxerr, "BufferView::buffer");
212
213                 bv_->text = 0;
214         }
215
216         // set current buffer
217         buffer_ = b;
218
219         if (bufferlist.getState() == BufferList::CLOSING) return;
220
221         // if we are closing the buffer, use the first buffer as current
222         if (!buffer_) {
223                 buffer_ = bufferlist.first();
224         }
225
226         if (buffer_) {
227                 lyxerr[Debug::INFO] << "Buffer addr: " << buffer_ << endl;
228                 buffer_->addUser(bv_);
229
230                 // If we don't have a text object for this, we make one
231                 if (bv_->text == 0) {
232                         resizeCurrentBuffer();
233                 }
234
235                 // FIXME: needed when ?
236                 bv_->text->first_y =
237                         screen().topCursorVisible(bv_->text->cursor, bv_->text->first_y);
238
239                 // Similarly, buffer-dependent dialogs should be updated or
240                 // hidden. This should go here because some dialogs (eg ToC)
241                 // require bv_->text.
242                 owner_->getDialogs().updateBufferDependent(true);
243         } else {
244                 lyxerr[Debug::INFO] << "  No Buffer!" << endl;
245                 owner_->getDialogs().hideBufferDependent();
246
247                 // Also remove all remaining text's from the testcache.
248                 // (there should not be any!) (if there is any it is a
249                 // bug!)
250                 if (lyxerr.debugging())
251                         textcache.show(lyxerr, "buffer delete all");
252                 textcache.clear();
253         }
254
255         repaint();
256         updateScrollbar();
257         owner_->updateMenubar();
258         owner_->updateToolbar();
259         owner_->updateLayoutChoice();
260         owner_->updateWindowTitle();
261
262         if (grfx::Previews::activated() && buffer_)
263                 grfx::Previews::get().generateBufferPreviews(*buffer_);
264 }
265
266
267 bool BufferView::Pimpl::fitCursor()
268 {
269         bool ret;
270
271         if (bv_->theLockingInset()) {
272                 bv_->theLockingInset()->fitInsetCursor(bv_);
273                 ret = true;
274         } else {
275                 ret = screen().fitCursor(bv_->text, bv_);
276         }
277
278         bv_->owner()->getDialogs().updateParagraph();
279         if (ret)
280                 updateScrollbar();
281         return ret;
282 }
283
284
285 void BufferView::Pimpl::redoCurrentBuffer()
286 {
287         lyxerr[Debug::INFO] << "BufferView::redoCurrentBuffer" << endl;
288         if (buffer_ && bv_->text) {
289                 resizeCurrentBuffer();
290                 updateScrollbar();
291                 owner_->updateLayoutChoice();
292                 repaint();
293         }
294 }
295
296
297 int BufferView::Pimpl::resizeCurrentBuffer()
298 {
299         lyxerr[Debug::INFO] << "resizeCurrentBuffer" << endl;
300
301         Paragraph * par = 0;
302         Paragraph * selstartpar = 0;
303         Paragraph * selendpar = 0;
304         UpdatableInset * the_locking_inset = 0;
305
306         pos_type pos = 0;
307         pos_type selstartpos = 0;
308         pos_type selendpos = 0;
309         bool selection = false;
310         bool mark_set  = false;
311
312         owner_->prohibitInput();
313
314         owner_->message(_("Formatting document..."));
315
316         if (bv_->text) {
317                 par = bv_->text->cursor.par();
318                 pos = bv_->text->cursor.pos();
319                 selstartpar = bv_->text->selection.start.par();
320                 selstartpos = bv_->text->selection.start.pos();
321                 selendpar = bv_->text->selection.end.par();
322                 selendpos = bv_->text->selection.end.pos();
323                 selection = bv_->text->selection.set();
324                 mark_set = bv_->text->selection.mark();
325                 the_locking_inset = bv_->theLockingInset();
326                 buffer_->resizeInsets(bv_);
327                 // I don't think the delete and new are necessary here we just could
328                 // call only init! (Jug 20020419)
329                 delete bv_->text;
330                 bv_->text = new LyXText(bv_);
331                 bv_->text->init(bv_);
332         } else {
333                 // See if we have a text in TextCache that fits
334                 // the new buffer_ with the correct width.
335                 bv_->text = textcache.findFit(buffer_, workarea().workWidth());
336                 if (bv_->text) {
337                         if (lyxerr.debugging()) {
338                                 lyxerr << "Found a LyXText that fits:\n";
339                                 textcache.show(lyxerr, make_pair(buffer_, make_pair(workarea().workWidth(), bv_->text)));
340                         }
341                         // Set the owner of the newly found text
342                         //      bv_->text->owner(bv_);
343                         if (lyxerr.debugging())
344                                 textcache.show(lyxerr, "resizeCurrentBuffer");
345                 } else {
346                         bv_->text = new LyXText(bv_);
347                         bv_->text->init(bv_);
348                         //buffer_->resizeInsets(bv_);
349                 }
350         }
351
352         if (par) {
353                 bv_->text->selection.set(true);
354                 // At this point just to avoid the Delete-Empty-Paragraph-
355                 // Mechanism when setting the cursor.
356                 bv_->text->selection.mark(mark_set);
357                 if (selection) {
358                         bv_->text->setCursor(bv_, selstartpar, selstartpos);
359                         bv_->text->selection.cursor = bv_->text->cursor;
360                         bv_->text->setCursor(bv_, selendpar, selendpos);
361                         bv_->text->setSelection(bv_);
362                         bv_->text->setCursor(bv_, par, pos);
363                 } else {
364                         bv_->text->setCursor(bv_, par, pos);
365                         bv_->text->selection.cursor = bv_->text->cursor;
366                         bv_->text->selection.set(false);
367                 }
368                 // remake the inset locking
369                 bv_->theLockingInset(the_locking_inset);
370         }
371
372         bv_->text->first_y = screen().topCursorVisible(bv_->text->cursor, bv_->text->first_y);
373
374         switchKeyMap();
375         owner_->allowInput();
376
377         updateScrollbar();
378
379         return 0;
380 }
381
382
383 void BufferView::Pimpl::repaint()
384 {
385         // Regenerate the screen.
386         screen().redraw(bv_->text, bv_);
387 }
388
389
390 void BufferView::Pimpl::updateScrollbar()
391 {
392         if (!bv_->text) {
393                 lyxerr[Debug::GUI] << "no text in updateScrollbar" << endl;
394                 workarea().setScrollbarParams(0, 0, 0);
395                 return;
396         }
397
398         LyXText const & t = *bv_->text;
399
400         lyxerr[Debug::GUI] << "Updating scrollbar: h " << t.height << ", first_y "
401                 << t.first_y << ", default height " << t.defaultHeight() << endl;
402
403         workarea().setScrollbarParams(t.height, t.first_y, t.defaultHeight());
404 }
405
406
407 void BufferView::Pimpl::scrollDocView(int value)
408 {
409         lyxerr[Debug::GUI] << "scrollDocView of " << value << endl;
410
411         if (!buffer_) return;
412
413         screen().draw(bv_->text, bv_, value);
414
415         if (!lyxrc.cursor_follows_scrollbar) {
416                 return;
417         }
418
419         LyXText * vbt = bv_->text;
420
421         int const height = vbt->defaultHeight();
422         int const first = static_cast<int>((bv_->text->first_y + height));
423         int const last = static_cast<int>((bv_->text->first_y + workarea().workHeight() - height));
424
425         if (vbt->cursor.y() < first)
426                 vbt->setCursorFromCoordinates(bv_, 0, first);
427         else if (vbt->cursor.y() > last)
428                 vbt->setCursorFromCoordinates(bv_, 0, last);
429 }
430
431
432 int BufferView::Pimpl::scroll(long time)
433 {
434         if (!buffer_)
435                 return 0;
436
437         LyXText const * t = bv_->text;
438
439         double const diff = t->defaultHeight()
440                 + double(time) * double(time) * 0.125;
441
442         scrollDocView(int(diff));
443         workarea().setScrollbarParams(t->height, t->first_y, t->defaultHeight());
444         return 0;
445 }
446
447
448 void BufferView::Pimpl::workAreaKeyPress(LyXKeySymPtr key,
449                                          key_modifier::state state)
450 {
451         bv_->owner()->getLyXFunc().processKeySym(key, state);
452 }
453
454
455 void BufferView::Pimpl::workAreaMotionNotify(int x, int y, mouse_button::state state)
456 {
457         // Only use motion with button 1
458         if (!(state & mouse_button::button1))
459                 return;
460
461         if (!buffer_)
462                 return;
463
464         // Check for inset locking
465         if (bv_->theLockingInset()) {
466                 LyXCursor cursor = bv_->text->cursor;
467                 LyXFont font = bv_->text->getFont(buffer_,
468                                                   cursor.par(), cursor.pos());
469                 int width = bv_->theLockingInset()->width(bv_, font);
470                 int inset_x = font.isVisibleRightToLeft()
471                         ? cursor.ix() - width : cursor.ix();
472                 int start_x = inset_x + bv_->theLockingInset()->scroll();
473
474                 FuncRequest cmd(bv_, LFUN_MOUSE_MOTION,
475                                   x - start_x, y - cursor.iy() + bv_->text->first_y, state);
476                 bv_->theLockingInset()->localDispatch(cmd);
477                 return;
478         }
479
480         // The test for not selection possible is needed, that only motion
481         // events are used, where the bottom press event was on
482         //  the drawing area too
483         if (!selection_possible)
484                 return;
485
486         screen().hideCursor();
487
488         Row * cursorrow = bv_->text->cursor.row();
489         bv_->text->setCursorFromCoordinates(bv_, x, y + bv_->text->first_y);
490 #if 0
491         // sorry for this but I have a strange error that the y value jumps at
492         // a certain point. This seems like an error in my xforms library or
493         // in some other local environment, but I would like to leave this here
494         // for the moment until I can remove this (Jug 20020418)
495         if (y_before < bv_->text->cursor.y())
496                 lyxerr << y_before << ":" << bv_->text->cursor.y() << endl;
497 #endif
498         // This is to allow jumping over large insets
499         if (cursorrow == bv_->text->cursor.row()) {
500                 if (y >= int(workarea().workHeight())) {
501                         bv_->text->cursorDown(bv_, false);
502                 } else if (y < 0) {
503                         bv_->text->cursorUp(bv_, false);
504                 }
505         }
506
507         if (!bv_->text->selection.set())
508                 update(bv_->text, BufferView::UPDATE); // Maybe an empty line was deleted
509
510         bv_->text->setSelection(bv_);
511         screen().toggleToggle(bv_->text, bv_);
512         fitCursor();
513         showCursor();
514 }
515
516
517 // Single-click on work area
518 void BufferView::Pimpl::workAreaButtonPress(int xpos, int ypos,
519                                             mouse_button::state button)
520 {
521         if (!buffer_)
522                 return;
523
524         // ok ok, this is a hack (for xforms)
525         if (button == mouse_button::button4) {
526                 scroll(-lyxrc.wheel_jump);
527                 // We shouldn't go further down as we really should only do the
528                 // scrolling and be done with this. Otherwise we may open some
529                 // dialogs (Jug 20020424).
530                 return;
531         } else if (button == mouse_button::button5) {
532                 scroll(lyxrc.wheel_jump);
533                 // We shouldn't go further down as we really should only do the
534                 // scrolling and be done with this. Otherwise we may open some
535                 // dialogs (Jug 20020424).
536                 return;
537         }
538
539         Inset * inset_hit = checkInsetHit(bv_->text, xpos, ypos);
540
541         // Middle button press pastes if we have a selection
542         // We do this here as if the selection was inside an inset
543         // it could get cleared on the unlocking of the inset so
544         // we have to check this first
545         bool paste_internally = false;
546         if (button == mouse_button::button2 && bv_->getLyXText()->selection.set()) {
547                 owner_->dispatch(FuncRequest(LFUN_COPY));
548                 paste_internally = true;
549         }
550
551         int const screen_first = bv_->text->first_y;
552
553         if (bv_->theLockingInset()) {
554                 // We are in inset locking mode
555
556                 // Check whether the inset was hit. If not reset mode,
557                 // otherwise give the event to the inset
558                 if (inset_hit == bv_->theLockingInset()) {
559                         FuncRequest cmd(bv_, LFUN_MOUSE_PRESS, xpos, ypos, button);
560                         bv_->theLockingInset()->localDispatch(cmd);
561                         return;
562                 }
563                 bv_->unlockInset(bv_->theLockingInset());
564         }
565
566         if (!inset_hit)
567                 selection_possible = true;
568         screen().hideCursor();
569
570         // Clear the selection
571         screen().toggleSelection(bv_->text, bv_);
572         bv_->text->clearSelection();
573         bv_->text->fullRebreak(bv_);
574         update();
575         updateScrollbar();
576
577         // Single left click in math inset?
578         if (isHighlyEditableInset(inset_hit)) {
579                 // Highly editable inset, like math
580                 UpdatableInset * inset = static_cast<UpdatableInset *>(inset_hit);
581                 selection_possible = false;
582                 owner_->updateLayoutChoice();
583                 owner_->message(inset->editMessage());
584                 //inset->edit(bv_, xpos, ypos, button);
585                 // We just have to lock the inset before calling a PressEvent on it!
586                 // we don't need the edit() call here! (Jug20020329)
587                 if (!bv_->lockInset(inset)) {
588                         lyxerr[Debug::INSETS] << "Cannot lock inset" << endl;
589                 }
590                 FuncRequest cmd(bv_, LFUN_MOUSE_PRESS, xpos, ypos, button);
591                 inset->localDispatch(cmd);
592                 return;
593         }
594         // I'm not sure we should continue here if we hit an inset (Jug20020403)
595
596         // Right click on a footnote flag opens float menu
597         if (button == mouse_button::button3) {
598                 selection_possible = false;
599                 return;
600         }
601
602         if (!inset_hit) // otherwise it was already set in checkInsetHit(...)
603                 bv_->text->setCursorFromCoordinates(bv_, xpos, ypos + screen_first);
604         finishUndo();
605         bv_->text->selection.cursor = bv_->text->cursor;
606         bv_->text->cursor.x_fix(bv_->text->cursor.x());
607
608         owner_->updateLayoutChoice();
609         if (fitCursor()) {
610                 selection_possible = false;
611         }
612
613         // Insert primary selection with middle mouse
614         // if there is a local selection in the current buffer,
615         // insert this
616         if (button == mouse_button::button2) {
617                 if (paste_internally)
618                         owner_->dispatch(FuncRequest(LFUN_PASTE));
619                 else
620                         owner_->dispatch(FuncRequest(LFUN_PASTESELECTION, "paragraph"));
621                 selection_possible = false;
622                 return;
623         }
624 }
625
626
627 void BufferView::Pimpl::doubleClick(int /*x*/, int /*y*/, mouse_button::state button)
628 {
629         if (!buffer_)
630                 return;
631
632         LyXText * text = bv_->getLyXText();
633
634         if (text->bv_owner && bv_->theLockingInset())
635                 return;
636
637         if (button == mouse_button::button1) {
638                 if (text->bv_owner) {
639                         screen().hideCursor();
640                         screen().toggleSelection(text, bv_);
641                         text->selectWord(bv_, LyXText::WHOLE_WORD_STRICT);
642                         screen().toggleSelection(text, bv_, false);
643                 } else {
644                         text->selectWord(bv_, LyXText::WHOLE_WORD_STRICT);
645                 }
646                 // This will fit the cursor on the screen if necessary
647                 update(text, BufferView::SELECT|BufferView::FITCUR);
648                 workarea().haveSelection(bv_->getLyXText()->selection.set());
649         }
650 }
651
652
653 void BufferView::Pimpl::tripleClick(int /*x*/, int /*y*/, mouse_button::state button)
654 {
655         if (!buffer_)
656                 return;
657
658         LyXText * text = bv_->getLyXText();
659
660         if (text->bv_owner && bv_->theLockingInset())
661             return;
662
663         if (button == mouse_button::button1) {
664                 if (text->bv_owner) {
665                         screen().hideCursor();
666                         screen().toggleSelection(text, bv_);
667                 }
668                 text->cursorHome(bv_);
669                 text->selection.cursor = text->cursor;
670                 text->cursorEnd(bv_);
671                 text->setSelection(bv_);
672                 if (text->bv_owner) {
673                         screen().toggleSelection(text, bv_, false);
674                 }
675                 // This will fit the cursor on the screen if necessary
676                 update(text, BufferView::SELECT|BufferView::FITCUR);
677                 workarea().haveSelection(bv_->getLyXText()->selection.set());
678         }
679 }
680
681
682 void BufferView::Pimpl::selectionRequested()
683 {
684         static string sel;
685
686         if (!available())
687                 return;
688
689         LyXText * text = bv_->getLyXText();
690
691         if (text->selection.set() &&
692                 (!bv_->text->xsel_cache.set() ||
693                  text->selection.start != bv_->text->xsel_cache.start ||
694                  text->selection.end != bv_->text->xsel_cache.end))
695         {
696                 bv_->text->xsel_cache = text->selection;
697                 sel = text->selectionAsString(bv_->buffer(), false);
698         } else if (!text->selection.set()) {
699                 sel = string();
700                 bv_->text->xsel_cache.set(false);
701         }
702         if (!sel.empty()) {
703                 workarea().putClipboard(sel);
704         }
705 }
706
707
708 void BufferView::Pimpl::selectionLost()
709 {
710         if (available()) {
711                 hideCursor();
712                 toggleSelection();
713                 bv_->getLyXText()->clearSelection();
714                 showCursor();
715                 bv_->text->xsel_cache.set(false);
716         }
717 }
718
719
720 void BufferView::Pimpl::workAreaButtonRelease(int x, int y,
721                                               mouse_button::state button)
722 {
723         // do nothing if we used the mouse wheel
724         if (!buffer_ || button == mouse_button::button4 || button == mouse_button::button5)
725                 return;
726
727         // If we hit an inset, we have the inset coordinates in these
728         // and inset_hit points to the inset.  If we do not hit an
729         // inset, inset_hit is 0, and inset_x == x, inset_y == y.
730         Inset * inset_hit = checkInsetHit(bv_->text, x, y);
731
732         if (bv_->theLockingInset()) {
733                 // We are in inset locking mode.
734
735                 // LyX does a kind of work-area grabbing for insets.
736                 // Only a ButtonPress FuncRequest outside the inset will
737                 // force a insetUnlock.
738                 FuncRequest cmd(bv_, LFUN_MOUSE_RELEASE, x, y, button);
739                 bv_->theLockingInset()->localDispatch(cmd);
740                 return;
741         }
742
743         selection_possible = false;
744
745         if (button == mouse_button::button2)
746                 return;
747
748         // finish selection
749         if (button == mouse_button::button1) {
750                 workarea().haveSelection(bv_->getLyXText()->selection.set());
751         }
752
753         switchKeyMap();
754         owner_->view_state_changed();
755         owner_->updateMenubar();
756         owner_->updateToolbar();
757
758         // Did we hit an editable inset?
759         if (inset_hit) {
760                 selection_possible = false;
761
762                 // if we reach this point with a selection, it
763                 // must mean we are currently selecting.
764                 // But we don't want to open the inset
765                 // because that is annoying for the user.
766                 // So just pretend we didn't hit it.
767                 // this is OK because a "kosher" ButtonRelease
768                 // will follow a ButtonPress that clears
769                 // the selection.
770                 // Note this also fixes selection drawing
771                 // problems if we end up opening an inset
772                 if (bv_->getLyXText()->selection.set())
773                         return;
774
775                 // CHECK fix this proper in 0.13
776                 // well, maybe 13.0 !!!!!!!!!
777
778                 // Following a ref shouldn't issue
779                 // a push on the undo-stack
780                 // anylonger, now that we have
781                 // keybindings for following
782                 // references and returning from
783                 // references.  IMHO though, it
784                 // should be the inset's own business
785                 // to push or not push on the undo
786                 // stack. They don't *have* to
787                 // alter the document...
788                 // (Joacim)
789                 // ...or maybe the SetCursorParUndo()
790                 // below isn't necessary at all anylonger?
791                 if (inset_hit->lyxCode() == Inset::REF_CODE) {
792                         setCursorParUndo(bv_);
793                 }
794
795                 owner_->message(inset_hit->editMessage());
796
797                 if (isHighlyEditableInset(inset_hit)) {
798                         // Highly editable inset, like math
799                         UpdatableInset *inset = (UpdatableInset *)inset_hit;
800                         FuncRequest cmd(bv_, LFUN_MOUSE_RELEASE, x, y, button);
801                         inset->localDispatch(cmd);
802                 } else {
803                         FuncRequest cmd(bv_, LFUN_MOUSE_RELEASE, x, y, button);
804                         inset_hit->localDispatch(cmd);
805                         // IMO this is a grosshack! Inset's should be changed so that
806                         // they call the actions they have to do with the insetButtonRel.
807                         // function and not in the edit(). This should be changed
808                         // (Jug 20020329)
809 #ifdef WITH_WARNINGS
810 #warning Please remove donot call inset->edit() here (Jug 20020812)
811 #endif
812                         inset_hit->edit(bv_, x, y, button);
813                 }
814                 return;
815         }
816
817         // Maybe we want to edit a bibitem ale970302
818         if (bv_->text->cursor.par()->bibkey) {
819                 bool const is_rtl = bv_->text->cursor.par()->isRightToLeftPar(buffer_->params);
820                 int const width = bibitemMaxWidth(bv_, buffer_->params.getLyXTextClass().defaultfont());
821                 if ((is_rtl && x > bv_->text->workWidth(bv_)-20-width) ||
822                     (!is_rtl && x < 20+width)) {
823                         bv_->text->cursor.par()->bibkey->edit(bv_, 0, 0, mouse_button::none);
824                 }
825         }
826
827         return;
828 }
829
830
831 Box BufferView::Pimpl::insetDimensions(LyXText const & text,
832                                        LyXCursor const & cursor) const
833 {
834         Paragraph /*const*/ & par = *cursor.par();
835         pos_type const pos = cursor.pos();
836
837         lyx::Assert(par.getInset(pos));
838
839         Inset const & inset(*par.getInset(pos));
840
841         LyXFont const & font = text.getFont(buffer_, &par, pos);
842
843         int const width = inset.width(bv_, font);
844         int const inset_x = font.isVisibleRightToLeft()
845                 ? (cursor.ix() - width) : cursor.ix();
846
847         return Box(
848                 inset_x + inset.scroll(),
849                 inset_x + width,
850                 cursor.iy() - inset.ascent(bv_, font),
851                 cursor.iy() + inset.descent(bv_, font));
852 }
853
854
855 Inset * BufferView::Pimpl::checkInset(LyXText const & text,
856                                       LyXCursor const & cursor,
857                                       int & x, int & y) const
858 {
859         pos_type const pos(cursor.pos());
860         Paragraph /*const*/ & par(*cursor.par());
861
862         if (pos >= par.size() || !par.isInset(pos)) {
863                 return 0;
864         }
865
866         Inset /*const*/ * inset = par.getInset(pos);
867
868         if (!isEditableInset(inset)) {
869                 return 0;
870         }
871
872         Box b(insetDimensions(text, cursor));
873
874         if (!b.contained(x, y)) {
875                 lyxerr[Debug::GUI] << "Missed inset at x,y " << x << "," << y
876                         << " box " << b << endl;
877                 return 0;
878         }
879
880         text.setCursor(bv_, &par, pos, true);
881
882         x -= b.x1;
883         // The origin of an inset is on the baseline
884         y -= text.cursor.iy();
885
886         return inset;
887 }
888
889
890 Inset * BufferView::Pimpl::checkInsetHit(LyXText * text, int & x, int & y)
891 {
892         int y_tmp = y + text->first_y;
893
894         LyXCursor cursor;
895         text->setCursorFromCoordinates(bv_, cursor, x, y_tmp);
896
897         Inset * inset(checkInset(*text, cursor, x, y_tmp));
898
899         if (inset) {
900                 y = y_tmp;
901                 return inset;
902         }
903
904         // look at previous position
905
906         if (cursor.pos() == 0) {
907                 return 0;
908         }
909
910         // move back one
911         text->setCursor(bv_, cursor, cursor.par(), cursor.pos() - 1, true);
912
913         inset = checkInset(*text, cursor, x, y_tmp);
914         if (inset) {
915                 y = y_tmp;
916         }
917         return inset;
918 }
919
920
921 void BufferView::Pimpl::workAreaResize()
922 {
923         static int work_area_width;
924         static int work_area_height;
925
926         bool const widthChange = workarea().workWidth() != work_area_width;
927         bool const heightChange = workarea().workHeight() != work_area_height;
928
929         // update from work area
930         work_area_width = workarea().workWidth();
931         work_area_height = workarea().workHeight();
932
933         if (buffer_ != 0) {
934                 if (widthChange) {
935                         // The visible LyXView need a resize
936                         resizeCurrentBuffer();
937
938                         // Remove all texts from the textcache
939                         // This is not _really_ what we want to do. What
940                         // we really want to do is to delete in textcache
941                         // that does not have a BufferView with matching
942                         // width, but as long as we have only one BufferView
943                         // deleting all gives the same result.
944                         if (lyxerr.debugging())
945                                 textcache.show(lyxerr, "Expose delete all");
946                         textcache.clear();
947                         // FIXME: this is aalready done in resizeCurrentBuffer() ??
948                         buffer_->resizeInsets(bv_);
949                 } else if (heightChange) {
950                         // fitCursor() ensures we don't jump back
951                         // to the start of the document on vertical
952                         // resize
953                         fitCursor();
954                 }
955         }
956
957         if (widthChange || heightChange) {
958                 repaint();
959         }
960
961         // always make sure that the scrollbar is sane.
962         updateScrollbar();
963         owner_->updateLayoutChoice();
964         return;
965 }
966
967
968 void BufferView::Pimpl::update()
969 {
970         if (!bv_->theLockingInset() || !bv_->theLockingInset()->nodraw()) {
971                 LyXText::text_status st = bv_->text->status();
972                 screen().update(bv_->text, bv_);
973                 bool fitc = false;
974                 while (bv_->text->status() == LyXText::CHANGED_IN_DRAW) {
975                         bv_->text->fullRebreak(bv_);
976                         st = LyXText::NEED_MORE_REFRESH;
977                         bv_->text->setCursor(bv_, bv_->text->cursor.par(),
978                                              bv_->text->cursor.pos());
979                         if (bv_->text->selection.set()) {
980                                 bv_->text->setCursor(bv_, bv_->text->selection.start,
981                                                      bv_->text->selection.start.par(),
982                                                      bv_->text->selection.start.pos());
983                                 bv_->text->setCursor(bv_, bv_->text->selection.end,
984                                                      bv_->text->selection.end.par(),
985                                                      bv_->text->selection.end.pos());
986                         }
987                         fitc = true;
988                         bv_->text->status(bv_, st);
989                         screen().update(bv_->text, bv_);
990                 }
991                 // do this here instead of in the screen::update because of
992                 // the above loop!
993                 bv_->text->status(bv_, LyXText::UNCHANGED);
994                 if (fitc)
995                         fitCursor();
996         }
997 }
998
999 // Values used when calling update:
1000 // -3 - update
1001 // -2 - update, move sel_cursor if selection, fitcursor
1002 // -1 - update, move sel_cursor if selection, fitcursor, mark dirty
1003 //  0 - update, move sel_cursor if selection, fitcursor
1004 //  1 - update, move sel_cursor if selection, fitcursor, mark dirty
1005 //  3 - update, move sel_cursor if selection
1006 //
1007 // update -
1008 // a simple redraw of the parts that need refresh
1009 //
1010 // move sel_cursor if selection -
1011 // the text's sel_cursor is moved if there is selection is progress
1012 //
1013 // fitcursor -
1014 // fitCursor() is called and the scrollbar updated
1015 //
1016 // mark dirty -
1017 // the buffer is marked dirty.
1018 //
1019 // enum {
1020 //       UPDATE = 0,
1021 //       SELECT = 1,
1022 //       FITCUR = 2,
1023 //       CHANGE = 4
1024 // };
1025 //
1026 // UPDATE_ONLY = UPDATE;
1027 // UPDATE_SELECT = UPDATE | SELECT;
1028 // UPDATE_SELECT_MOVE = UPDATE | SELECT | FITCUR;
1029 // UPDATE_SELECT_MOVE_AFTER_CHANGE = UPDATE | SELECT | FITCUR | CHANGE;
1030 //
1031 // update(-3) -> update(0)         -> update(0) -> update(UPDATE)
1032 // update(-2) -> update(1 + 2)     -> update(3) -> update(SELECT|FITCUR)
1033 // update(-1) -> update(1 + 2 + 4) -> update(7) -> update(SELECT|FITCUR|CHANGE)
1034 // update(1)  -> update(1 + 2 + 4) -> update(7) -> update(SELECT|FITCUR|CHANGE)
1035 // update(3)  -> update(1)         -> update(1) -> update(SELECT)
1036
1037 void BufferView::Pimpl::update(LyXText * text, BufferView::UpdateCodes f)
1038 {
1039         owner_->updateLayoutChoice();
1040
1041         if (!text->selection.set() && (f & SELECT)) {
1042                 text->selection.cursor = text->cursor;
1043         }
1044
1045         text->fullRebreak(bv_);
1046
1047         if (text->inset_owner) {
1048                 text->inset_owner->setUpdateStatus(bv_, InsetText::NONE);
1049                 updateInset(text->inset_owner, false);
1050         } else {
1051                 update();
1052         }
1053
1054         if ((f & FITCUR)) {
1055                 fitCursor();
1056         }
1057
1058         if ((f & CHANGE)) {
1059                 buffer_->markDirty();
1060         }
1061 }
1062
1063
1064 // Callback for cursor timer
1065 void BufferView::Pimpl::cursorToggle()
1066 {
1067         if (!buffer_) {
1068                 cursor_timeout.restart();
1069                 return;
1070         }
1071
1072         /* FIXME */
1073         extern void reapSpellchecker(void);
1074         reapSpellchecker();
1075
1076         if (!bv_->theLockingInset()) {
1077                 screen().cursorToggle(bv_);
1078         } else {
1079                 bv_->theLockingInset()->toggleInsetCursor(bv_);
1080         }
1081
1082         cursor_timeout.restart();
1083 }
1084
1085
1086 bool BufferView::Pimpl::available() const
1087 {
1088         if (buffer_ && bv_->text)
1089                 return true;
1090         return false;
1091 }
1092
1093
1094 void BufferView::Pimpl::beforeChange(LyXText * text)
1095 {
1096         toggleSelection();
1097         text->clearSelection();
1098 }
1099
1100
1101 void BufferView::Pimpl::finishChange(bool fitcur)
1102 {
1103         finishUndo();
1104         moveCursorUpdate(fitcur);
1105         bv_->owner()->view_state_changed();
1106 }
1107
1108
1109 void BufferView::Pimpl::savePosition(unsigned int i)
1110 {
1111         if (i >= saved_positions_num)
1112                 return;
1113         saved_positions[i] = Position(buffer_->fileName(),
1114                                       bv_->text->cursor.par()->id(),
1115                                       bv_->text->cursor.pos());
1116         if (i > 0) {
1117                 ostringstream str;
1118                 str << _("Saved bookmark") << ' ' << i;
1119                 owner_->message(str.str().c_str());
1120         }
1121 }
1122
1123
1124 void BufferView::Pimpl::restorePosition(unsigned int i)
1125 {
1126         if (i >= saved_positions_num)
1127                 return;
1128
1129         string const fname = saved_positions[i].filename;
1130
1131         beforeChange(bv_->text);
1132
1133         if (fname != buffer_->fileName()) {
1134                 Buffer * b = bufferlist.exists(fname) ?
1135                         bufferlist.getBuffer(fname) :
1136                         bufferlist.loadLyXFile(fname); // don't ask, just load it
1137                 if (b != 0) buffer(b);
1138         }
1139
1140         Paragraph * par = buffer_->getParFromID(saved_positions[i].par_id);
1141         if (!par)
1142                 return;
1143
1144         bv_->text->setCursor(bv_, par,
1145                              min(par->size(), saved_positions[i].par_pos));
1146
1147         update(bv_->text, BufferView::SELECT | BufferView::FITCUR);
1148         if (i > 0) {
1149                 ostringstream str;
1150                 str << _("Moved to bookmark") << ' ' << i;
1151                 owner_->message(str.str().c_str());
1152         }
1153 }
1154
1155
1156 bool BufferView::Pimpl::isSavedPosition(unsigned int i)
1157 {
1158         if (i >= saved_positions_num)
1159                 return false;
1160
1161         return !saved_positions[i].filename.empty();
1162 }
1163
1164
1165 void BufferView::Pimpl::switchKeyMap()
1166 {
1167         if (!lyxrc.rtl_support)
1168                 return;
1169
1170         LyXText * text = bv_->getLyXText();
1171         if (text->real_current_font.isRightToLeft()
1172             && !(bv_->theLockingInset()
1173                  && bv_->theLockingInset()->lyxCode() == Inset::ERT_CODE))
1174         {
1175                 if (owner_->getIntl().keymap == Intl::PRIMARY)
1176                         owner_->getIntl().KeyMapSec();
1177         } else {
1178                 if (owner_->getIntl().keymap == Intl::SECONDARY)
1179                         owner_->getIntl().KeyMapPrim();
1180         }
1181 }
1182
1183
1184 void BufferView::Pimpl::insetUnlock()
1185 {
1186         if (bv_->theLockingInset()) {
1187                 bv_->theLockingInset()->insetUnlock(bv_);
1188                 bv_->theLockingInset(0);
1189                 finishUndo();
1190         }
1191 }
1192
1193
1194 void BufferView::Pimpl::showCursor()
1195 {
1196         if (bv_->theLockingInset())
1197                 bv_->theLockingInset()->showInsetCursor(bv_);
1198         else
1199                 screen().showCursor(bv_->text, bv_);
1200 }
1201
1202
1203 void BufferView::Pimpl::hideCursor()
1204 {
1205         if (!bv_->theLockingInset())
1206                 screen().hideCursor();
1207 }
1208
1209
1210 void BufferView::Pimpl::toggleSelection(bool b)
1211 {
1212         if (bv_->theLockingInset())
1213                 bv_->theLockingInset()->toggleSelection(bv_, b);
1214         screen().toggleSelection(bv_->text, bv_, b);
1215 }
1216
1217
1218 void BufferView::Pimpl::toggleToggle()
1219 {
1220         screen().toggleToggle(bv_->text, bv_);
1221 }
1222
1223
1224 void BufferView::Pimpl::center()
1225 {
1226         LyXText * t = bv_->text;
1227
1228         beforeChange(t);
1229         int const half_height = workarea().workHeight() / 2;
1230         int new_y = 0;
1231
1232         if (t->cursor.y() > half_height) {
1233                 new_y = t->cursor.y() - half_height;
1234         }
1235
1236         // FIXME: can we do this w/o calling screen directly ?
1237         // This updates first_y but means the fitCursor() call
1238         // from the update(FITCUR) doesn't realise that we might
1239         // have moved (e.g. from GOTOPARAGRAPH), so doesn't cause
1240         // the scrollbar to be updated as it should, so we have
1241         // to do it manually. Any operation that does a center()
1242         // and also might have moved first_y must make sure to call
1243         // updateScrollbar() currently. Never mind that this is a
1244         // pretty obfuscated way of updating t->first_y
1245         screen().draw(t, bv_, new_y);
1246
1247         update(t, BufferView::SELECT | BufferView::FITCUR);
1248 }
1249
1250
1251 void BufferView::Pimpl::stuffClipboard(string const & stuff) const
1252 {
1253         workarea().putClipboard(stuff);
1254 }
1255
1256
1257 /*
1258  * Dispatch functions for actions which can be valid for BufferView->text
1259  * and/or InsetText->text!!!
1260  */
1261
1262
1263 void BufferView::Pimpl::moveCursorUpdate(bool selecting, bool fitcur)
1264 {
1265         LyXText * lt = bv_->getLyXText();
1266
1267         if (selecting || lt->selection.mark()) {
1268                 lt->setSelection(bv_);
1269                 if (lt->bv_owner)
1270                         toggleToggle();
1271                 else
1272                         updateInset(lt->inset_owner, false);
1273         }
1274         if (lt->bv_owner) {
1275                 if (fitcur)
1276                         update(lt, BufferView::SELECT|BufferView::FITCUR);
1277                 else
1278                         update(lt, BufferView::SELECT);
1279                 showCursor();
1280         } else if (bv_->text->status() != LyXText::UNCHANGED) {
1281                 bv_->theLockingInset()->hideInsetCursor(bv_);
1282                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
1283                 showCursor();
1284         }
1285
1286         if (!lt->selection.set())
1287                 workarea().haveSelection(false);
1288
1289         switchKeyMap();
1290 }
1291
1292
1293 Inset * BufferView::Pimpl::getInsetByCode(Inset::Code code)
1294 {
1295 #if 0
1296         LyXCursor cursor = bv_->getLyXText()->cursor;
1297         Buffer::inset_iterator it =
1298                 find_if(Buffer::inset_iterator(
1299                         cursor.par(), cursor.pos()),
1300                         buffer_->inset_iterator_end(),
1301                         lyx::compare_memfun(&Inset::lyxCode, code));
1302         return it != buffer_->inset_iterator_end() ? (*it) : 0;
1303 #else
1304         // Ok, this is a little bit too brute force but it
1305         // should work for now. Better infrastructure is comming. (Lgb)
1306
1307         Buffer * b = bv_->buffer();
1308         LyXCursor cursor = bv_->getLyXText()->cursor;
1309
1310         Buffer::inset_iterator beg = b->inset_iterator_begin();
1311         Buffer::inset_iterator end = b->inset_iterator_end();
1312
1313         bool cursor_par_seen = false;
1314
1315         for (; beg != end; ++beg) {
1316                 if (beg.getPar() == cursor.par()) {
1317                         cursor_par_seen = true;
1318                 }
1319                 if (cursor_par_seen) {
1320                         if (beg.getPar() == cursor.par()
1321                             && beg.getPos() >= cursor.pos()) {
1322                                 break;
1323                         } else if (beg.getPar() != cursor.par()) {
1324                                 break;
1325                         }
1326                 }
1327
1328         }
1329         if (beg != end) {
1330                 // Now find the first inset that matches code.
1331                 for (; beg != end; ++beg) {
1332                         if (beg->lyxCode() == code) {
1333                                 return &(*beg);
1334                         }
1335                 }
1336         }
1337         return 0;
1338 #endif
1339 }
1340
1341
1342 void BufferView::Pimpl::MenuInsertLyXFile(string const & filen)
1343 {
1344         string filename = filen;
1345
1346         if (filename.empty()) {
1347                 // Launch a file browser
1348                 string initpath = lyxrc.document_path;
1349
1350                 if (available()) {
1351                         string const trypath = owner_->buffer()->filePath();
1352                         // If directory is writeable, use this as default.
1353                         if (IsDirWriteable(trypath))
1354                                 initpath = trypath;
1355                 }
1356
1357                 FileDialog fileDlg(bv_->owner(),
1358                                    _("Select LyX document to insert"),
1359                         LFUN_FILE_INSERT,
1360                         make_pair(string(_("Documents|#o#O")),
1361                                   string(lyxrc.document_path)),
1362                         make_pair(string(_("Examples|#E#e")),
1363                                   string(AddPath(system_lyxdir, "examples"))));
1364
1365                 FileDialog::Result result =
1366                         fileDlg.Select(initpath,
1367                                        _("*.lyx| LyX Documents (*.lyx)"));
1368
1369                 if (result.first == FileDialog::Later)
1370                         return;
1371
1372                 filename = result.second;
1373
1374                 // check selected filename
1375                 if (filename.empty()) {
1376                         owner_->message(_("Canceled."));
1377                         return;
1378                 }
1379         }
1380
1381         // get absolute path of file and add ".lyx" to the filename if
1382         // necessary
1383         filename = FileSearch(string(), filename, "lyx");
1384
1385         string const disp_fn(MakeDisplayPath(filename));
1386
1387         ostringstream s1;
1388         s1 << _("Inserting document") << ' '
1389            << disp_fn << " ...";
1390         owner_->message(s1.str().c_str());
1391         bool const res = bv_->insertLyXFile(filename);
1392         if (res) {
1393                 ostringstream str;
1394                 str << _("Document") << ' ' << disp_fn
1395                     << ' ' << _("inserted.");
1396                 owner_->message(str.str().c_str());
1397         } else {
1398                 ostringstream str;
1399                 str << _("Could not insert document") << ' '
1400                     << disp_fn;
1401                 owner_->message(str.str().c_str());
1402         }
1403 }
1404
1405
1406 bool BufferView::Pimpl::dispatch(FuncRequest const & ev)
1407 {
1408         lyxerr[Debug::ACTION] << "BufferView::Pimpl::Dispatch: action["
1409           << ev.action <<"] arg[" << ev.argument << "]" << endl;
1410
1411         LyXTextClass const & tclass = buffer_->params.getLyXTextClass();
1412
1413         switch (ev.action) {
1414
1415         case LFUN_TOC_INSERT:
1416         {
1417                 InsetCommandParams p;
1418                 p.setCmdName("tableofcontents");
1419                 Inset * inset = new InsetTOC(p);
1420                 if (!insertInset(inset, tclass.defaultLayoutName()))
1421                         delete inset;
1422                 break;
1423         }
1424
1425         case LFUN_SCROLL_INSET:
1426                 // this is not handled here as this function is only active
1427                 // if we have a locking_inset and that one is (or contains)
1428                 // a tabular-inset
1429                 break;
1430
1431         case LFUN_INSET_GRAPHICS:
1432         {
1433                 Inset * new_inset = new InsetGraphics;
1434                 if (!insertInset(new_inset)) {
1435                         delete new_inset;
1436                 } else {
1437                         // this is need because you don't use a inset->Edit()
1438                         updateInset(new_inset, true);
1439                         new_inset->edit(bv_);
1440                 }
1441                 break;
1442         }
1443
1444         case LFUN_PASTE:
1445                 bv_->paste();
1446                 switchKeyMap();
1447                 break;
1448
1449         case LFUN_CUT:
1450                 bv_->cut();
1451                 break;
1452
1453         case LFUN_COPY:
1454                 bv_->copy();
1455                 break;
1456
1457         case LFUN_LAYOUT_COPY:
1458                 bv_->copyEnvironment();
1459                 break;
1460
1461         case LFUN_LAYOUT_PASTE:
1462                 bv_->pasteEnvironment();
1463                 switchKeyMap();
1464                 break;
1465
1466         case LFUN_GOTOERROR:
1467                 gotoInset(Inset::ERROR_CODE, false);
1468                 break;
1469
1470         case LFUN_GOTONOTE:
1471                 gotoInset(Inset::NOTE_CODE, false);
1472                 break;
1473
1474         case LFUN_REFERENCE_GOTO:
1475         {
1476                 vector<Inset::Code> tmp;
1477                 tmp.push_back(Inset::LABEL_CODE);
1478                 tmp.push_back(Inset::REF_CODE);
1479                 gotoInset(tmp, true);
1480                 break;
1481         }
1482
1483         case LFUN_DEPTH_MIN:
1484                 changeDepth(bv_, bv_->getLyXText(), -1);
1485                 break;
1486
1487         case LFUN_DEPTH_PLUS:
1488                 changeDepth(bv_, bv_->getLyXText(), 1);
1489                 break;
1490
1491         case LFUN_FREE:
1492                 owner_->getDialogs().setUserFreeFont();
1493                 break;
1494
1495         case LFUN_FILE_INSERT:
1496                 MenuInsertLyXFile(ev.argument);
1497                 break;
1498
1499         case LFUN_FILE_INSERT_ASCII_PARA:
1500                 InsertAsciiFile(bv_, ev.argument, true);
1501                 break;
1502
1503         case LFUN_FILE_INSERT_ASCII:
1504                 InsertAsciiFile(bv_, ev.argument, false);
1505                 break;
1506
1507         case LFUN_LANGUAGE:
1508                 lang(bv_, ev.argument);
1509                 switchKeyMap();
1510                 owner_->view_state_changed();
1511                 break;
1512
1513         case LFUN_EMPH:
1514                 emph(bv_);
1515                 owner_->view_state_changed();
1516                 break;
1517
1518         case LFUN_BOLD:
1519                 bold(bv_);
1520                 owner_->view_state_changed();
1521                 break;
1522
1523         case LFUN_NOUN:
1524                 noun(bv_);
1525                 owner_->view_state_changed();
1526                 break;
1527
1528         case LFUN_CODE:
1529                 code(bv_);
1530                 owner_->view_state_changed();
1531                 break;
1532
1533         case LFUN_SANS:
1534                 sans(bv_);
1535                 owner_->view_state_changed();
1536                 break;
1537
1538         case LFUN_ROMAN:
1539                 roman(bv_);
1540                 owner_->view_state_changed();
1541                 break;
1542
1543         case LFUN_DEFAULT:
1544                 styleReset(bv_);
1545                 owner_->view_state_changed();
1546                 break;
1547
1548         case LFUN_UNDERLINE:
1549                 underline(bv_);
1550                 owner_->view_state_changed();
1551                 break;
1552
1553         case LFUN_FONT_SIZE:
1554                 fontSize(bv_, ev.argument);
1555                 owner_->view_state_changed();
1556                 break;
1557
1558         case LFUN_FONT_STATE:
1559                 owner_->getLyXFunc().setMessage(currentState(bv_));
1560                 break;
1561
1562         case LFUN_INSERT_LABEL:
1563                 MenuInsertLabel(bv_, ev.argument);
1564                 break;
1565
1566         case LFUN_REF_INSERT:
1567                 if (ev.argument.empty()) {
1568                         InsetCommandParams p("ref");
1569                         owner_->getDialogs().createRef(p.getAsString());
1570                 } else {
1571                         InsetCommandParams p;
1572                         p.setFromString(ev.argument);
1573
1574                         InsetRef * inset = new InsetRef(p, *buffer_);
1575                         if (!insertInset(inset))
1576                                 delete inset;
1577                         else
1578                                 updateInset(inset, true);
1579                 }
1580                 break;
1581
1582         case LFUN_BOOKMARK_SAVE:
1583                 savePosition(strToUnsignedInt(ev.argument));
1584                 break;
1585
1586         case LFUN_BOOKMARK_GOTO:
1587                 restorePosition(strToUnsignedInt(ev.argument));
1588                 break;
1589
1590         case LFUN_REF_GOTO:
1591         {
1592                 string label = ev.argument;
1593                 if (label.empty()) {
1594                         InsetRef * inset =
1595                                 static_cast<InsetRef*>(getInsetByCode(Inset::REF_CODE));
1596                         if (inset) {
1597                                 label = inset->getContents();
1598                                 savePosition(0);
1599                         }
1600                 }
1601
1602                 if (!label.empty()) {
1603                         //bv_->savePosition(0);
1604                         if (!bv_->gotoLabel(label))
1605                                 Alert::alert(_("Error"),
1606                                            _("Couldn't find this label"),
1607                                            _("in current document."));
1608                 }
1609         }
1610         break;
1611
1612         case LFUN_QUOTE:
1613                 smartQuote();
1614                 break;
1615
1616         case LFUN_HTMLURL:
1617         case LFUN_URL:
1618         {
1619                 InsetCommandParams p;
1620                 if (ev.action == LFUN_HTMLURL)
1621                         p.setCmdName("htmlurl");
1622                 else
1623                         p.setCmdName("url");
1624                 owner_->getDialogs().createUrl(p.getAsString());
1625         }
1626         break;
1627
1628         case LFUN_INSERT_URL:
1629         {
1630                 InsetCommandParams p;
1631                 p.setFromString(ev.argument);
1632
1633                 InsetUrl * inset = new InsetUrl(p);
1634                 if (!insertInset(inset))
1635                         delete inset;
1636                 else
1637                         updateInset(inset, true);
1638         }
1639         break;
1640
1641         case LFUN_INSET_ERT:
1642                 insertAndEditInset(new InsetERT(buffer_->params));
1643                 break;
1644
1645         case LFUN_INSET_EXTERNAL:
1646                 insertAndEditInset(new InsetExternal);
1647                 break;
1648
1649         case LFUN_INSET_FOOTNOTE:
1650                 insertAndEditInset(new InsetFoot(buffer_->params));
1651                 break;
1652
1653         case LFUN_INSET_MARGINAL:
1654                 insertAndEditInset(new InsetMarginal(buffer_->params));
1655                 break;
1656
1657         case LFUN_INSET_MINIPAGE:
1658                 insertAndEditInset(new InsetMinipage(buffer_->params));
1659                 break;
1660
1661         case LFUN_INSERT_NOTE:
1662                 insertAndEditInset(new InsetNote(buffer_->params));
1663                 break;
1664
1665         case LFUN_INSET_FLOAT:
1666                 // check if the float type exist
1667                 if (floatList.typeExist(ev.argument)) {
1668                         insertAndEditInset(new InsetFloat(buffer_->params,
1669                                                           ev.argument));
1670                 } else {
1671                         lyxerr << "Non-existent float type: "
1672                                << ev.argument << endl;
1673                 }
1674                 break;
1675
1676         case LFUN_INSET_WIDE_FLOAT:
1677                 // check if the float type exist
1678                 if (floatList.typeExist(ev.argument)) {
1679                         InsetFloat * new_inset =
1680                                 new InsetFloat(buffer_->params, ev.argument);
1681                         new_inset->wide(true);
1682                         insertAndEditInset(new_inset);
1683                 } else {
1684                         lyxerr << "Non-existent float type: "
1685                                << ev.argument << endl;
1686                 }
1687                 break;
1688
1689 #if 0
1690         case LFUN_INSET_LIST:
1691                 insertAndEditInset(new InsetList);
1692                 break;
1693
1694         case LFUN_INSET_THEOREM:
1695                 insertAndEditInset(new InsetTheorem);
1696                 break;
1697 #endif
1698
1699         case LFUN_INSET_CAPTION:
1700         {
1701                 // Do we have a locking inset...
1702                 if (bv_->theLockingInset()) {
1703                         lyxerr << "Locking inset code: "
1704                                << static_cast<int>(bv_->theLockingInset()->lyxCode());
1705                         InsetCaption * new_inset =
1706                                 new InsetCaption(buffer_->params);
1707                         new_inset->setOwner(bv_->theLockingInset());
1708                         new_inset->setAutoBreakRows(true);
1709                         new_inset->setDrawFrame(0, InsetText::LOCKED);
1710                         new_inset->setFrameColor(0, LColor::captionframe);
1711                         if (insertInset(new_inset))
1712                                 new_inset->edit(bv_);
1713                         else
1714                                 delete new_inset;
1715                 }
1716         }
1717         break;
1718
1719         case LFUN_TABULAR_INSERT:
1720         {
1721                 if (ev.argument.empty()) {
1722                         owner_->getDialogs().showTabularCreate();
1723                         break;
1724                 }
1725
1726                 int r = 2;
1727                 int c = 2;
1728                 ::sscanf(ev.argument.c_str(),"%d%d", &r, &c);
1729                 InsetTabular * new_inset =
1730                         new InsetTabular(*buffer_, r, c);
1731                 bool const rtl =
1732                         bv_->getLyXText()->real_current_font.isRightToLeft();
1733                 if (!open_new_inset(new_inset, rtl))
1734                         delete new_inset;
1735         }
1736         break;
1737
1738
1739         // --- accented characters ---------------------------
1740
1741         case LFUN_UMLAUT:
1742         case LFUN_CIRCUMFLEX:
1743         case LFUN_GRAVE:
1744         case LFUN_ACUTE:
1745         case LFUN_TILDE:
1746         case LFUN_CEDILLA:
1747         case LFUN_MACRON:
1748         case LFUN_DOT:
1749         case LFUN_UNDERDOT:
1750         case LFUN_UNDERBAR:
1751         case LFUN_CARON:
1752         case LFUN_SPECIAL_CARON:
1753         case LFUN_BREVE:
1754         case LFUN_TIE:
1755         case LFUN_HUNG_UMLAUT:
1756         case LFUN_CIRCLE:
1757         case LFUN_OGONEK:
1758                 if (ev.argument.empty()) {
1759                         // As always...
1760                         owner_->getLyXFunc().handleKeyFunc(ev.action);
1761                 } else {
1762                         owner_->getLyXFunc().handleKeyFunc(ev.action);
1763                         owner_->getIntl().getTransManager()
1764                                 .TranslateAndInsert(ev.argument[0], bv_->getLyXText());
1765                         update(bv_->getLyXText(),
1766                                BufferView::SELECT
1767                                | BufferView::FITCUR
1768                                | BufferView::CHANGE);
1769                 }
1770                 break;
1771
1772         case LFUN_MATH_MACRO:
1773         case LFUN_MATH_DELIM:
1774         case LFUN_INSERT_MATRIX:
1775         case LFUN_INSERT_MATH:
1776         case LFUN_MATH_IMPORT_SELECTION: // Imports LaTeX from the X selection
1777         case LFUN_MATH_DISPLAY:          // Open or create a displayed math inset
1778         case LFUN_MATH_MODE:             // Open or create an inlined math inset
1779         case LFUN_GREEK:                 // Insert a single greek letter
1780                 mathDispatch(FuncRequest(bv_, ev.action, ev.argument));
1781                 break;
1782
1783         case LFUN_CITATION_INSERT:
1784         {
1785                 InsetCommandParams p;
1786                 p.setFromString(ev.argument);
1787
1788                 InsetCitation * inset = new InsetCitation(p);
1789                 if (!insertInset(inset))
1790                         delete inset;
1791                 else
1792                         updateInset(inset, true);
1793         }
1794         break;
1795
1796         case LFUN_INSERT_BIBTEX:
1797         {
1798                 // ale970405+lasgoutt970425
1799                 // The argument can be up to two tokens separated
1800                 // by a space. The first one is the bibstyle.
1801                 string const db = token(ev.argument, ' ', 0);
1802                 string bibstyle = token(ev.argument, ' ', 1);
1803                 if (bibstyle.empty())
1804                         bibstyle = "plain";
1805
1806                 InsetCommandParams p("BibTeX", db, bibstyle);
1807                 InsetBibtex * inset = new InsetBibtex(p);
1808
1809                 if (insertInset(inset)) {
1810                         if (ev.argument.empty())
1811                                 inset->edit(bv_);
1812                 } else
1813                         delete inset;
1814         }
1815         break;
1816
1817         // BibTeX data bases
1818         case LFUN_BIBDB_ADD:
1819         {
1820                 InsetBibtex * inset =
1821                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
1822                 if (inset) {
1823                         inset->addDatabase(ev.argument);
1824                 }
1825         }
1826         break;
1827
1828         case LFUN_BIBDB_DEL:
1829         {
1830                 InsetBibtex * inset =
1831                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
1832                 if (inset) {
1833                         inset->delDatabase(ev.argument);
1834                 }
1835         }
1836         break;
1837
1838         case LFUN_BIBTEX_STYLE:
1839         {
1840                 InsetBibtex * inset =
1841                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
1842                 if (inset) {
1843                         inset->setOptions(ev.argument);
1844                 }
1845         }
1846         break;
1847
1848         case LFUN_INDEX_INSERT:
1849         {
1850                 string entry = ev.argument;
1851                 if (entry.empty())
1852                         entry = bv_->getLyXText()->getStringToIndex(bv_);
1853
1854                 if (entry.empty()) {
1855                         owner_->getDialogs().createIndex();
1856                         break;
1857                 }
1858
1859                 InsetIndex * inset = new InsetIndex(InsetCommandParams("index", entry));
1860
1861                 if (!insertInset(inset)) {
1862                         delete inset;
1863                 } else {
1864                         updateInset(inset, true);
1865                 }
1866         }
1867         break;
1868
1869         case LFUN_INDEX_PRINT:
1870         {
1871                 InsetCommandParams p("printindex");
1872                 Inset * inset = new InsetPrintIndex(p);
1873                 if (!insertInset(inset, tclass.defaultLayoutName()))
1874                         delete inset;
1875         }
1876         break;
1877
1878         case LFUN_PARENTINSERT:
1879         {
1880                 InsetCommandParams p("lyxparent", ev.argument);
1881                 Inset * inset = new InsetParent(p, *buffer_);
1882                 if (!insertInset(inset, tclass.defaultLayoutName()))
1883                         delete inset;
1884         }
1885
1886         break;
1887
1888         case LFUN_CHILD_INSERT:
1889         {
1890                 InsetInclude::Params p;
1891                 p.cparams.setFromString(ev.argument);
1892                 p.masterFilename_ = buffer_->fileName();
1893
1894                 InsetInclude * inset = new InsetInclude(p);
1895                 if (!insertInset(inset))
1896                         delete inset;
1897                 else {
1898                         updateInset(inset, true);
1899                         bv_->owner()->getDialogs().showInclude(inset);
1900                 }
1901         }
1902         break;
1903
1904         case LFUN_FLOAT_LIST:
1905                 if (floatList.typeExist(ev.argument)) {
1906                         Inset * inset = new InsetFloatList(ev.argument);
1907                         if (!insertInset(inset, tclass.defaultLayoutName()))
1908                                 delete inset;
1909                 } else {
1910                         lyxerr << "Non-existent float type: "
1911                                << ev.argument << endl;
1912                 }
1913                 break;
1914
1915         case LFUN_THESAURUS_ENTRY:
1916         {
1917                 string arg = ev.argument;
1918
1919                 if (arg.empty()) {
1920                         arg = bv_->getLyXText()->selectionAsString(buffer_,
1921                                                                    false);
1922
1923                         // FIXME
1924                         if (arg.size() > 100 || arg.empty()) {
1925                                 // Get word or selection
1926                                 bv_->getLyXText()->selectWordWhenUnderCursor(bv_, LyXText::WHOLE_WORD);
1927                                 arg = bv_->getLyXText()->selectionAsString(buffer_, false);
1928                                 // FIXME: where is getLyXText()->unselect(bv_) ?
1929                         }
1930                 }
1931
1932                 bv_->owner()->getDialogs().showThesaurus(arg);
1933         }
1934                 break;
1935
1936         case LFUN_DATE_INSERT:  // jdblair: date-insert cmd
1937         {
1938                 time_t now_time_t = time(NULL);
1939                 struct tm * now_tm = localtime(&now_time_t);
1940                 setlocale(LC_TIME, "");
1941                 string arg;
1942                 if (!ev.argument.empty())
1943                         arg = ev.argument;
1944                 else
1945                         arg = lyxrc.date_insert_format;
1946                 char datetmp[32];
1947                 int const datetmp_len =
1948                         ::strftime(datetmp, 32, arg.c_str(), now_tm);
1949
1950                 LyXText * lt = bv_->getLyXText();
1951
1952                 for (int i = 0; i < datetmp_len; i++) {
1953                         lt->insertChar(bv_, datetmp[i]);
1954                         update(lt,
1955                                BufferView::SELECT
1956                                | BufferView::FITCUR
1957                                | BufferView::CHANGE);
1958                 }
1959
1960                 lt->selection.cursor = lt->cursor;
1961                 moveCursorUpdate(false);
1962         }
1963         break;
1964
1965         case LFUN_UNKNOWN_ACTION:
1966                 ev.errorMessage(N_("Unknown function!"));
1967                 break;
1968
1969         default:
1970                 FuncRequest cmd = ev;
1971                 cmd.setView(bv_);
1972                 return bv_->getLyXText()->dispatch(cmd);
1973         } // end of switch
1974
1975         return true;
1976 }
1977
1978
1979 void BufferView::Pimpl::smartQuote()
1980 {
1981         LyXText const * lt = bv_->getLyXText();
1982         Paragraph const * par = lt->cursor.par();
1983         pos_type pos = lt->cursor.pos();
1984         char c;
1985
1986         if (!pos
1987             || (par->isInset(pos - 1)
1988                 && par->getInset(pos - 1)->isSpace()))
1989                 c = ' ';
1990         else
1991                 c = par->getChar(pos - 1);
1992
1993         hideCursor();
1994
1995         LyXLayout_ptr const & style = par->layout();
1996
1997         if (style->pass_thru ||
1998             par->getFontSettings(buffer_->params,
1999                                  pos).language()->lang() == "hebrew" ||
2000                 (!insertInset(new InsetQuotes(c, buffer_->params))))
2001                 bv_->owner()->dispatch(FuncRequest(LFUN_SELFINSERT, "\""));
2002 }
2003
2004
2005 void BufferView::Pimpl::insertAndEditInset(Inset * inset)
2006 {
2007 #if 0
2008         if (insertInset(inset))
2009                 inset->edit(bv_);
2010         else
2011                 delete inset;
2012 #else
2013         bool gotsel = false;
2014
2015         if (bv_->getLyXText()->selection.set()) {
2016                 bv_->getLyXText()->cutSelection(bv_, true, false);
2017                 gotsel = true;
2018         }
2019
2020         if (insertInset(inset)) {
2021                 inset->edit(bv_);
2022                 if (gotsel)
2023                         owner_->dispatch(FuncRequest(LFUN_PASTESELECTION));
2024         }
2025         else
2026                 delete inset;
2027 #endif
2028 }
2029
2030
2031 // Open and lock an updatable inset
2032 bool BufferView::Pimpl::open_new_inset(UpdatableInset * new_inset, bool behind)
2033 {
2034         LyXText * lt = bv_->getLyXText();
2035
2036         beforeChange(lt);
2037         finishUndo();
2038         if (!insertInset(new_inset)) {
2039                 delete new_inset;
2040                 return false;
2041         }
2042         new_inset->edit(bv_, !behind);
2043         return true;
2044 }
2045
2046
2047 bool BufferView::Pimpl::insertInset(Inset * inset, string const & lout)
2048 {
2049         // if we are in a locking inset we should try to insert the
2050         // inset there otherwise this is a illegal function now
2051         if (bv_->theLockingInset()) {
2052                 if (bv_->theLockingInset()->insetAllowed(inset))
2053                     return bv_->theLockingInset()->insertInset(bv_, inset);
2054                 return false;
2055         }
2056
2057         // not quite sure if we want this...
2058         setCursorParUndo(bv_);
2059         freezeUndo();
2060
2061         beforeChange(bv_->text);
2062         if (!lout.empty()) {
2063                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
2064                 bv_->text->breakParagraph(bv_);
2065                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
2066
2067                 if (!bv_->text->cursor.par()->empty()) {
2068                         bv_->text->cursorLeft(bv_);
2069
2070                         bv_->text->breakParagraph(bv_);
2071                         update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
2072                 }
2073
2074                 string lres = lout;
2075                 LyXTextClass const & tclass =
2076                         buffer_->params.getLyXTextClass();
2077                 bool hasLayout = tclass.hasLayout(lres);
2078                 string lay = tclass.defaultLayoutName();
2079
2080                 if (hasLayout != false) {
2081                         // layout found
2082                         lay = lres;
2083                 } else {
2084                         // layout not fount using default
2085                         lay = tclass.defaultLayoutName();
2086                 }
2087
2088                 bv_->text->setLayout(bv_, lay);
2089
2090                 bv_->text->setParagraph(bv_, 0, 0,
2091                                    0, 0,
2092                                    VSpace(VSpace::NONE), VSpace(VSpace::NONE),
2093                                    Spacing(),
2094                                    LYX_ALIGN_LAYOUT,
2095                                    string(),
2096                                    0);
2097                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
2098         }
2099
2100         bv_->text->insertInset(bv_, inset);
2101         update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
2102
2103         unFreezeUndo();
2104         return true;
2105 }
2106
2107
2108 void BufferView::Pimpl::updateInset(Inset * inset, bool mark_dirty)
2109 {
2110         if (!inset || !available())
2111                 return;
2112
2113         // first check for locking insets
2114         if (bv_->theLockingInset()) {
2115                 if (bv_->theLockingInset() == inset) {
2116                         if (bv_->text->updateInset(bv_, inset)) {
2117                                 update();
2118                                 if (mark_dirty) {
2119                                         buffer_->markDirty();
2120                                 }
2121                                 updateScrollbar();
2122                                 return;
2123                         }
2124                 } else if (bv_->theLockingInset()->updateInsetInInset(bv_, inset)) {
2125                         if (bv_->text->updateInset(bv_,  bv_->theLockingInset())) {
2126                                 update();
2127                                 if (mark_dirty) {
2128                                         buffer_->markDirty();
2129                                 }
2130                                 updateScrollbar();
2131                                 return;
2132                         }
2133                 }
2134         }
2135
2136         // then check if the inset is a top_level inset (has no owner)
2137         // if yes do the update as always otherwise we have to update the
2138         // toplevel inset where this inset is inside
2139         Inset * tl_inset = inset;
2140         while (tl_inset->owner())
2141                 tl_inset = tl_inset->owner();
2142         hideCursor();
2143         if (tl_inset == inset) {
2144                 update(bv_->text, BufferView::UPDATE);
2145                 if (bv_->text->updateInset(bv_, inset)) {
2146                         if (mark_dirty) {
2147                                 update(bv_->text,
2148                                        BufferView::SELECT
2149                                        | BufferView::FITCUR
2150                                        | BufferView::CHANGE);
2151                         } else {
2152                                 update(bv_->text, SELECT);
2153                         }
2154                         return;
2155                 }
2156         } else if (static_cast<UpdatableInset *>(tl_inset)
2157                            ->updateInsetInInset(bv_, inset))
2158         {
2159                 if (bv_->text->updateInset(bv_, tl_inset)) {
2160                         update();
2161                         updateScrollbar();
2162                 }
2163         }
2164 }
2165
2166
2167 void BufferView::Pimpl::gotoInset(vector<Inset::Code> const & codes,
2168                                   bool same_content)
2169 {
2170         if (!available()) return;
2171
2172         hideCursor();
2173         beforeChange(bv_->text);
2174         update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
2175
2176         LyXCursor const & cursor = bv_->text->cursor;
2177
2178         string contents;
2179         if (same_content &&
2180             cursor.par()->isInset(cursor.pos())) {
2181                 Inset const * inset = cursor.par()->getInset(cursor.pos());
2182                 if (find(codes.begin(), codes.end(), inset->lyxCode())
2183                     != codes.end())
2184                         contents =
2185                                 static_cast<InsetCommand const *>(inset)->getContents();
2186         }
2187
2188
2189         if (!bv_->text->gotoNextInset(bv_, codes, contents)) {
2190                 if (bv_->text->cursor.pos()
2191                     || bv_->text->cursor.par() != bv_->text->ownerParagraph()) {
2192                         LyXCursor tmp = bv_->text->cursor;
2193                         bv_->text->cursor.par(bv_->text->ownerParagraph());
2194                         bv_->text->cursor.pos(0);
2195                         if (!bv_->text->gotoNextInset(bv_, codes, contents)) {
2196                                 bv_->text->cursor = tmp;
2197                                 bv_->owner()->message(_("No more insets"));
2198                         }
2199                 } else {
2200                         bv_->owner()->message(_("No more insets"));
2201                 }
2202         }
2203         update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
2204         bv_->text->selection.cursor = bv_->text->cursor;
2205 }
2206
2207
2208 void BufferView::Pimpl::gotoInset(Inset::Code code, bool same_content)
2209 {
2210         gotoInset(vector<Inset::Code>(1, code), same_content);
2211 }