]> git.lyx.org Git - lyx.git/blob - src/BufferView_pimpl.C
use boost::bind instead of home grown stuff
[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 void BufferView::Pimpl::cursorPrevious(LyXText * text)
1087 {
1088         if (!text->cursor.row()->previous()) {
1089                 if (text->first_y > 0) {
1090                         int new_y = bv_->text->first_y - workarea().workHeight();
1091                         screen().draw(bv_->text, bv_, new_y < 0 ? 0 : new_y);
1092                         updateScrollbar();
1093                 }
1094                 return;
1095         }
1096
1097         int y = text->first_y;
1098         Row * cursorrow = text->cursor.row();
1099
1100         text->setCursorFromCoordinates(bv_, text->cursor.x_fix(), y);
1101         finishUndo();
1102
1103         int new_y;
1104         if (cursorrow == bv_->text->cursor.row()) {
1105                 // we have a row which is higher than the workarea so we leave the
1106                 // cursor on the start of the row and move only the draw up as soon
1107                 // as we move the cursor or do something while inside the row (it may
1108                 // span several workarea-heights) we'll move to the top again, but this
1109                 // is better than just jump down and only display part of the row.
1110                 new_y = bv_->text->first_y - workarea().workHeight();
1111         } else {
1112                 if (text->inset_owner) {
1113                         new_y = bv_->text->cursor.iy()
1114                                 + bv_->theLockingInset()->insetInInsetY() + y
1115                                 + text->cursor.row()->height()
1116                                 - workarea().workHeight() + 1;
1117                 } else {
1118                         new_y = text->cursor.y()
1119                                 - text->cursor.row()->baseline()
1120                                 + text->cursor.row()->height()
1121                                 - workarea().workHeight() + 1;
1122                 }
1123         }
1124         screen().draw(bv_->text, bv_,  new_y < 0 ? 0 : new_y);
1125         if (text->cursor.row()->previous()) {
1126                 LyXCursor cur;
1127                 text->setCursor(bv_, cur, text->cursor.row()->previous()->par(),
1128                                                 text->cursor.row()->previous()->pos(), false);
1129                 if (cur.y() > text->first_y) {
1130                         text->cursorUp(bv_, true);
1131                 }
1132         }
1133         updateScrollbar();
1134 }
1135
1136
1137 void BufferView::Pimpl::cursorNext(LyXText * text)
1138 {
1139         if (!text->cursor.row()->next()) {
1140                 int y = text->cursor.y() - text->cursor.row()->baseline() +
1141                         text->cursor.row()->height();
1142                 if (y > int(text->first_y + workarea().workHeight())) {
1143                         screen().draw(bv_->text, bv_,
1144                                                   bv_->text->first_y + workarea().workHeight());
1145                         updateScrollbar();
1146                 }
1147                 return;
1148         }
1149
1150         int y = text->first_y + workarea().workHeight();
1151         if (text->inset_owner && !text->first_y) {
1152                 y -= (bv_->text->cursor.iy()
1153                           - bv_->text->first_y
1154                           + bv_->theLockingInset()->insetInInsetY());
1155         }
1156
1157         text->getRowNearY(y);
1158
1159         Row * cursorrow = text->cursor.row();
1160         text->setCursorFromCoordinates(bv_, text->cursor.x_fix(), y); // + workarea().workHeight());
1161         finishUndo();
1162
1163         int new_y;
1164         if (cursorrow == bv_->text->cursor.row()) {
1165                 // we have a row which is higher than the workarea so we leave the
1166                 // cursor on the start of the row and move only the draw down as soon
1167                 // as we move the cursor or do something while inside the row (it may
1168                 // span several workarea-heights) we'll move to the top again, but this
1169                 // is better than just jump down and only display part of the row.
1170                 new_y = bv_->text->first_y + workarea().workHeight();
1171         } else {
1172                 if (text->inset_owner) {
1173                         new_y = bv_->text->cursor.iy()
1174                                 + bv_->theLockingInset()->insetInInsetY()
1175                                 + y - text->cursor.row()->baseline();
1176                 } else {
1177                         new_y =  text->cursor.y() - text->cursor.row()->baseline();
1178                 }
1179         }
1180         screen().draw(bv_->text, bv_, new_y);
1181         if (text->cursor.row()->next()) {
1182                 LyXCursor cur;
1183                 text->setCursor(bv_, cur, text->cursor.row()->next()->par(),
1184                                                 text->cursor.row()->next()->pos(), false);
1185                 if (cur.y() < int(text->first_y + workarea().workHeight())) {
1186                         text->cursorDown(bv_, true);
1187                 }
1188         }
1189         updateScrollbar();
1190 }
1191
1192
1193 bool BufferView::Pimpl::available() const
1194 {
1195         if (buffer_ && bv_->text)
1196                 return true;
1197         return false;
1198 }
1199
1200
1201 void BufferView::Pimpl::beforeChange(LyXText * text)
1202 {
1203         toggleSelection();
1204         text->clearSelection();
1205 }
1206
1207
1208 void BufferView::Pimpl::finishChange(bool fitcur)
1209 {
1210         finishUndo();
1211         moveCursorUpdate(fitcur);
1212         bv_->owner()->view_state_changed();
1213 }
1214
1215
1216 void BufferView::Pimpl::savePosition(unsigned int i)
1217 {
1218         if (i >= saved_positions_num)
1219                 return;
1220         saved_positions[i] = Position(buffer_->fileName(),
1221                                       bv_->text->cursor.par()->id(),
1222                                       bv_->text->cursor.pos());
1223         if (i > 0) {
1224                 ostringstream str;
1225                 str << _("Saved bookmark") << ' ' << i;
1226                 owner_->message(str.str().c_str());
1227         }
1228 }
1229
1230
1231 void BufferView::Pimpl::restorePosition(unsigned int i)
1232 {
1233         if (i >= saved_positions_num)
1234                 return;
1235
1236         string const fname = saved_positions[i].filename;
1237
1238         beforeChange(bv_->text);
1239
1240         if (fname != buffer_->fileName()) {
1241                 Buffer * b = bufferlist.exists(fname) ?
1242                         bufferlist.getBuffer(fname) :
1243                         bufferlist.loadLyXFile(fname); // don't ask, just load it
1244                 if (b != 0) buffer(b);
1245         }
1246
1247         Paragraph * par = buffer_->getParFromID(saved_positions[i].par_id);
1248         if (!par)
1249                 return;
1250
1251         bv_->text->setCursor(bv_, par,
1252                              min(par->size(), saved_positions[i].par_pos));
1253
1254         update(bv_->text, BufferView::SELECT | BufferView::FITCUR);
1255         if (i > 0) {
1256                 ostringstream str;
1257                 str << _("Moved to bookmark") << ' ' << i;
1258                 owner_->message(str.str().c_str());
1259         }
1260 }
1261
1262
1263 bool BufferView::Pimpl::isSavedPosition(unsigned int i)
1264 {
1265         if (i >= saved_positions_num)
1266                 return false;
1267
1268         return !saved_positions[i].filename.empty();
1269 }
1270
1271
1272 void BufferView::Pimpl::switchKeyMap()
1273 {
1274         if (!lyxrc.rtl_support)
1275                 return;
1276
1277         LyXText * text = bv_->getLyXText();
1278         if (text->real_current_font.isRightToLeft()
1279             && !(bv_->theLockingInset()
1280                  && bv_->theLockingInset()->lyxCode() == Inset::ERT_CODE))
1281         {
1282                 if (owner_->getIntl().keymap == Intl::PRIMARY)
1283                         owner_->getIntl().KeyMapSec();
1284         } else {
1285                 if (owner_->getIntl().keymap == Intl::SECONDARY)
1286                         owner_->getIntl().KeyMapPrim();
1287         }
1288 }
1289
1290
1291 void BufferView::Pimpl::insetUnlock()
1292 {
1293         if (bv_->theLockingInset()) {
1294                 bv_->theLockingInset()->insetUnlock(bv_);
1295                 bv_->theLockingInset(0);
1296                 finishUndo();
1297         }
1298 }
1299
1300
1301 void BufferView::Pimpl::showCursor()
1302 {
1303         if (bv_->theLockingInset())
1304                 bv_->theLockingInset()->showInsetCursor(bv_);
1305         else
1306                 screen().showCursor(bv_->text, bv_);
1307 }
1308
1309
1310 void BufferView::Pimpl::hideCursor()
1311 {
1312         if (!bv_->theLockingInset())
1313                 screen().hideCursor();
1314 }
1315
1316
1317 void BufferView::Pimpl::toggleSelection(bool b)
1318 {
1319         if (bv_->theLockingInset())
1320                 bv_->theLockingInset()->toggleSelection(bv_, b);
1321         screen().toggleSelection(bv_->text, bv_, b);
1322 }
1323
1324
1325 void BufferView::Pimpl::toggleToggle()
1326 {
1327         screen().toggleToggle(bv_->text, bv_);
1328 }
1329
1330
1331 void BufferView::Pimpl::center()
1332 {
1333         LyXText * t = bv_->text;
1334
1335         beforeChange(t);
1336         int const half_height = workarea().workHeight() / 2;
1337         int new_y = 0;
1338
1339         if (t->cursor.y() > half_height) {
1340                 new_y = t->cursor.y() - half_height;
1341         }
1342
1343         // FIXME: can we do this w/o calling screen directly ?
1344         // This updates first_y but means the fitCursor() call
1345         // from the update(FITCUR) doesn't realise that we might
1346         // have moved (e.g. from GOTOPARAGRAPH), so doesn't cause
1347         // the scrollbar to be updated as it should, so we have
1348         // to do it manually. Any operation that does a center()
1349         // and also might have moved first_y must make sure to call
1350         // updateScrollbar() currently. Never mind that this is a
1351         // pretty obfuscated way of updating t->first_y
1352         screen().draw(t, bv_, new_y);
1353
1354         update(t, BufferView::SELECT | BufferView::FITCUR);
1355 }
1356
1357
1358 void BufferView::Pimpl::stuffClipboard(string const & stuff) const
1359 {
1360         workarea().putClipboard(stuff);
1361 }
1362
1363
1364 /*
1365  * Dispatch functions for actions which can be valid for BufferView->text
1366  * and/or InsetText->text!!!
1367  */
1368
1369
1370 void BufferView::Pimpl::moveCursorUpdate(bool selecting, bool fitcur)
1371 {
1372         LyXText * lt = bv_->getLyXText();
1373
1374         if (selecting || lt->selection.mark()) {
1375                 lt->setSelection(bv_);
1376                 if (lt->bv_owner)
1377                         toggleToggle();
1378                 else
1379                         updateInset(lt->inset_owner, false);
1380         }
1381         if (lt->bv_owner) {
1382                 if (fitcur)
1383                         update(lt, BufferView::SELECT|BufferView::FITCUR);
1384                 else
1385                         update(lt, BufferView::SELECT);
1386                 showCursor();
1387         } else if (bv_->text->status() != LyXText::UNCHANGED) {
1388                 bv_->theLockingInset()->hideInsetCursor(bv_);
1389                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
1390                 showCursor();
1391         }
1392
1393         if (!lt->selection.set())
1394                 workarea().haveSelection(false);
1395
1396         switchKeyMap();
1397 }
1398
1399
1400 Inset * BufferView::Pimpl::getInsetByCode(Inset::Code code)
1401 {
1402 #if 0
1403         LyXCursor cursor = bv_->getLyXText()->cursor;
1404         Buffer::inset_iterator it =
1405                 find_if(Buffer::inset_iterator(
1406                         cursor.par(), cursor.pos()),
1407                         buffer_->inset_iterator_end(),
1408                         lyx::compare_memfun(&Inset::lyxCode, code));
1409         return it != buffer_->inset_iterator_end() ? (*it) : 0;
1410 #else
1411         // Ok, this is a little bit too brute force but it
1412         // should work for now. Better infrastructure is comming. (Lgb)
1413
1414         Buffer * b = bv_->buffer();
1415         LyXCursor cursor = bv_->getLyXText()->cursor;
1416
1417         Buffer::inset_iterator beg = b->inset_iterator_begin();
1418         Buffer::inset_iterator end = b->inset_iterator_end();
1419
1420         bool cursor_par_seen = false;
1421
1422         for (; beg != end; ++beg) {
1423                 if (beg.getPar() == cursor.par()) {
1424                         cursor_par_seen = true;
1425                 }
1426                 if (cursor_par_seen) {
1427                         if (beg.getPar() == cursor.par()
1428                             && beg.getPos() >= cursor.pos()) {
1429                                 break;
1430                         } else if (beg.getPar() != cursor.par()) {
1431                                 break;
1432                         }
1433                 }
1434
1435         }
1436         if (beg != end) {
1437                 // Now find the first inset that matches code.
1438                 for (; beg != end; ++beg) {
1439                         if (beg->lyxCode() == code) {
1440                                 return &(*beg);
1441                         }
1442                 }
1443         }
1444         return 0;
1445 #endif
1446 }
1447
1448
1449 void BufferView::Pimpl::MenuInsertLyXFile(string const & filen)
1450 {
1451         string filename = filen;
1452
1453         if (filename.empty()) {
1454                 // Launch a file browser
1455                 string initpath = lyxrc.document_path;
1456
1457                 if (available()) {
1458                         string const trypath = owner_->buffer()->filePath();
1459                         // If directory is writeable, use this as default.
1460                         if (IsDirWriteable(trypath))
1461                                 initpath = trypath;
1462                 }
1463
1464                 FileDialog fileDlg(bv_->owner(),
1465                                    _("Select LyX document to insert"),
1466                         LFUN_FILE_INSERT,
1467                         make_pair(string(_("Documents|#o#O")),
1468                                   string(lyxrc.document_path)),
1469                         make_pair(string(_("Examples|#E#e")),
1470                                   string(AddPath(system_lyxdir, "examples"))));
1471
1472                 FileDialog::Result result =
1473                         fileDlg.Select(initpath,
1474                                        _("*.lyx| LyX Documents (*.lyx)"));
1475
1476                 if (result.first == FileDialog::Later)
1477                         return;
1478
1479                 filename = result.second;
1480
1481                 // check selected filename
1482                 if (filename.empty()) {
1483                         owner_->message(_("Canceled."));
1484                         return;
1485                 }
1486         }
1487
1488         // get absolute path of file and add ".lyx" to the filename if
1489         // necessary
1490         filename = FileSearch(string(), filename, "lyx");
1491
1492         string const disp_fn(MakeDisplayPath(filename));
1493
1494         ostringstream s1;
1495         s1 << _("Inserting document") << ' '
1496            << disp_fn << " ...";
1497         owner_->message(s1.str().c_str());
1498         bool const res = bv_->insertLyXFile(filename);
1499         if (res) {
1500                 ostringstream str;
1501                 str << _("Document") << ' ' << disp_fn
1502                     << ' ' << _("inserted.");
1503                 owner_->message(str.str().c_str());
1504         } else {
1505                 ostringstream str;
1506                 str << _("Could not insert document") << ' '
1507                     << disp_fn;
1508                 owner_->message(str.str().c_str());
1509         }
1510 }
1511
1512
1513 bool BufferView::Pimpl::dispatch(FuncRequest const & ev)
1514 {
1515         lyxerr[Debug::ACTION] << "BufferView::Pimpl::Dispatch: action["
1516           << ev.action <<"] arg[" << ev.argument << "]" << endl;
1517
1518         LyXTextClass const & tclass = buffer_->params.getLyXTextClass();
1519
1520         switch (ev.action) {
1521
1522         case LFUN_TOC_INSERT:
1523         {
1524                 InsetCommandParams p;
1525                 p.setCmdName("tableofcontents");
1526                 Inset * inset = new InsetTOC(p);
1527                 if (!insertInset(inset, tclass.defaultLayoutName()))
1528                         delete inset;
1529                 break;
1530         }
1531
1532         case LFUN_SCROLL_INSET:
1533                 // this is not handled here as this function is only active
1534                 // if we have a locking_inset and that one is (or contains)
1535                 // a tabular-inset
1536                 break;
1537
1538         case LFUN_INSET_GRAPHICS:
1539         {
1540                 Inset * new_inset = new InsetGraphics;
1541                 if (!insertInset(new_inset)) {
1542                         delete new_inset;
1543                 } else {
1544                         // this is need because you don't use a inset->Edit()
1545                         updateInset(new_inset, true);
1546                         new_inset->edit(bv_);
1547                 }
1548                 break;
1549         }
1550
1551         case LFUN_PASTE:
1552                 bv_->paste();
1553                 switchKeyMap();
1554                 break;
1555
1556         case LFUN_CUT:
1557                 bv_->cut();
1558                 break;
1559
1560         case LFUN_COPY:
1561                 bv_->copy();
1562                 break;
1563
1564         case LFUN_LAYOUT_COPY:
1565                 bv_->copyEnvironment();
1566                 break;
1567
1568         case LFUN_LAYOUT_PASTE:
1569                 bv_->pasteEnvironment();
1570                 switchKeyMap();
1571                 break;
1572
1573         case LFUN_GOTOERROR:
1574                 gotoInset(Inset::ERROR_CODE, false);
1575                 break;
1576
1577         case LFUN_GOTONOTE:
1578                 gotoInset(Inset::NOTE_CODE, false);
1579                 break;
1580
1581         case LFUN_REFERENCE_GOTO:
1582         {
1583                 vector<Inset::Code> tmp;
1584                 tmp.push_back(Inset::LABEL_CODE);
1585                 tmp.push_back(Inset::REF_CODE);
1586                 gotoInset(tmp, true);
1587                 break;
1588         }
1589
1590         case LFUN_DEPTH_MIN:
1591                 changeDepth(bv_, bv_->getLyXText(), -1);
1592                 break;
1593
1594         case LFUN_DEPTH_PLUS:
1595                 changeDepth(bv_, bv_->getLyXText(), 1);
1596                 break;
1597
1598         case LFUN_FREE:
1599                 owner_->getDialogs().setUserFreeFont();
1600                 break;
1601
1602         case LFUN_FILE_INSERT:
1603                 MenuInsertLyXFile(ev.argument);
1604                 break;
1605
1606         case LFUN_FILE_INSERT_ASCII_PARA:
1607                 InsertAsciiFile(bv_, ev.argument, true);
1608                 break;
1609
1610         case LFUN_FILE_INSERT_ASCII:
1611                 InsertAsciiFile(bv_, ev.argument, false);
1612                 break;
1613
1614         case LFUN_LANGUAGE:
1615                 lang(bv_, ev.argument);
1616                 switchKeyMap();
1617                 owner_->view_state_changed();
1618                 break;
1619
1620         case LFUN_EMPH:
1621                 emph(bv_);
1622                 owner_->view_state_changed();
1623                 break;
1624
1625         case LFUN_BOLD:
1626                 bold(bv_);
1627                 owner_->view_state_changed();
1628                 break;
1629
1630         case LFUN_NOUN:
1631                 noun(bv_);
1632                 owner_->view_state_changed();
1633                 break;
1634
1635         case LFUN_CODE:
1636                 code(bv_);
1637                 owner_->view_state_changed();
1638                 break;
1639
1640         case LFUN_SANS:
1641                 sans(bv_);
1642                 owner_->view_state_changed();
1643                 break;
1644
1645         case LFUN_ROMAN:
1646                 roman(bv_);
1647                 owner_->view_state_changed();
1648                 break;
1649
1650         case LFUN_DEFAULT:
1651                 styleReset(bv_);
1652                 owner_->view_state_changed();
1653                 break;
1654
1655         case LFUN_UNDERLINE:
1656                 underline(bv_);
1657                 owner_->view_state_changed();
1658                 break;
1659
1660         case LFUN_FONT_SIZE:
1661                 fontSize(bv_, ev.argument);
1662                 owner_->view_state_changed();
1663                 break;
1664
1665         case LFUN_FONT_STATE:
1666                 owner_->getLyXFunc().setMessage(currentState(bv_));
1667                 break;
1668
1669         case LFUN_INSERT_LABEL:
1670                 MenuInsertLabel(bv_, ev.argument);
1671                 break;
1672
1673         case LFUN_REF_INSERT:
1674                 if (ev.argument.empty()) {
1675                         InsetCommandParams p("ref");
1676                         owner_->getDialogs().createRef(p.getAsString());
1677                 } else {
1678                         InsetCommandParams p;
1679                         p.setFromString(ev.argument);
1680
1681                         InsetRef * inset = new InsetRef(p, *buffer_);
1682                         if (!insertInset(inset))
1683                                 delete inset;
1684                         else
1685                                 updateInset(inset, true);
1686                 }
1687                 break;
1688
1689         case LFUN_BOOKMARK_SAVE:
1690                 savePosition(strToUnsignedInt(ev.argument));
1691                 break;
1692
1693         case LFUN_BOOKMARK_GOTO:
1694                 restorePosition(strToUnsignedInt(ev.argument));
1695                 break;
1696
1697         case LFUN_REF_GOTO:
1698         {
1699                 string label = ev.argument;
1700                 if (label.empty()) {
1701                         InsetRef * inset =
1702                                 static_cast<InsetRef*>(getInsetByCode(Inset::REF_CODE));
1703                         if (inset) {
1704                                 label = inset->getContents();
1705                                 savePosition(0);
1706                         }
1707                 }
1708
1709                 if (!label.empty()) {
1710                         //bv_->savePosition(0);
1711                         if (!bv_->gotoLabel(label))
1712                                 Alert::alert(_("Error"),
1713                                            _("Couldn't find this label"),
1714                                            _("in current document."));
1715                 }
1716         }
1717         break;
1718
1719         case LFUN_QUOTE:
1720                 smartQuote();
1721                 break;
1722
1723         case LFUN_HTMLURL:
1724         case LFUN_URL:
1725         {
1726                 InsetCommandParams p;
1727                 if (ev.action == LFUN_HTMLURL)
1728                         p.setCmdName("htmlurl");
1729                 else
1730                         p.setCmdName("url");
1731                 owner_->getDialogs().createUrl(p.getAsString());
1732         }
1733         break;
1734
1735         case LFUN_INSERT_URL:
1736         {
1737                 InsetCommandParams p;
1738                 p.setFromString(ev.argument);
1739
1740                 InsetUrl * inset = new InsetUrl(p);
1741                 if (!insertInset(inset))
1742                         delete inset;
1743                 else
1744                         updateInset(inset, true);
1745         }
1746         break;
1747
1748         case LFUN_INSET_ERT:
1749                 insertAndEditInset(new InsetERT(buffer_->params));
1750                 break;
1751
1752         case LFUN_INSET_EXTERNAL:
1753                 insertAndEditInset(new InsetExternal);
1754                 break;
1755
1756         case LFUN_INSET_FOOTNOTE:
1757                 insertAndEditInset(new InsetFoot(buffer_->params));
1758                 break;
1759
1760         case LFUN_INSET_MARGINAL:
1761                 insertAndEditInset(new InsetMarginal(buffer_->params));
1762                 break;
1763
1764         case LFUN_INSET_MINIPAGE:
1765                 insertAndEditInset(new InsetMinipage(buffer_->params));
1766                 break;
1767
1768         case LFUN_INSERT_NOTE:
1769                 insertAndEditInset(new InsetNote(buffer_->params));
1770                 break;
1771
1772         case LFUN_INSET_FLOAT:
1773                 // check if the float type exist
1774                 if (floatList.typeExist(ev.argument)) {
1775                         insertAndEditInset(new InsetFloat(buffer_->params,
1776                                                           ev.argument));
1777                 } else {
1778                         lyxerr << "Non-existent float type: "
1779                                << ev.argument << endl;
1780                 }
1781                 break;
1782
1783         case LFUN_INSET_WIDE_FLOAT:
1784                 // check if the float type exist
1785                 if (floatList.typeExist(ev.argument)) {
1786                         InsetFloat * new_inset =
1787                                 new InsetFloat(buffer_->params, ev.argument);
1788                         new_inset->wide(true);
1789                         insertAndEditInset(new_inset);
1790                 } else {
1791                         lyxerr << "Non-existent float type: "
1792                                << ev.argument << endl;
1793                 }
1794                 break;
1795
1796 #if 0
1797         case LFUN_INSET_LIST:
1798                 insertAndEditInset(new InsetList);
1799                 break;
1800
1801         case LFUN_INSET_THEOREM:
1802                 insertAndEditInset(new InsetTheorem);
1803                 break;
1804 #endif
1805
1806         case LFUN_INSET_CAPTION:
1807         {
1808                 // Do we have a locking inset...
1809                 if (bv_->theLockingInset()) {
1810                         lyxerr << "Locking inset code: "
1811                                << static_cast<int>(bv_->theLockingInset()->lyxCode());
1812                         InsetCaption * new_inset =
1813                                 new InsetCaption(buffer_->params);
1814                         new_inset->setOwner(bv_->theLockingInset());
1815                         new_inset->setAutoBreakRows(true);
1816                         new_inset->setDrawFrame(0, InsetText::LOCKED);
1817                         new_inset->setFrameColor(0, LColor::captionframe);
1818                         if (insertInset(new_inset))
1819                                 new_inset->edit(bv_);
1820                         else
1821                                 delete new_inset;
1822                 }
1823         }
1824         break;
1825
1826         case LFUN_TABULAR_INSERT:
1827         {
1828                 if (ev.argument.empty()) {
1829                         owner_->getDialogs().showTabularCreate();
1830                         break;
1831                 }
1832
1833                 int r = 2;
1834                 int c = 2;
1835                 ::sscanf(ev.argument.c_str(),"%d%d", &r, &c);
1836                 InsetTabular * new_inset =
1837                         new InsetTabular(*buffer_, r, c);
1838                 bool const rtl =
1839                         bv_->getLyXText()->real_current_font.isRightToLeft();
1840                 if (!open_new_inset(new_inset, rtl))
1841                         delete new_inset;
1842         }
1843         break;
1844
1845
1846         // --- accented characters ---------------------------
1847
1848         case LFUN_UMLAUT:
1849         case LFUN_CIRCUMFLEX:
1850         case LFUN_GRAVE:
1851         case LFUN_ACUTE:
1852         case LFUN_TILDE:
1853         case LFUN_CEDILLA:
1854         case LFUN_MACRON:
1855         case LFUN_DOT:
1856         case LFUN_UNDERDOT:
1857         case LFUN_UNDERBAR:
1858         case LFUN_CARON:
1859         case LFUN_SPECIAL_CARON:
1860         case LFUN_BREVE:
1861         case LFUN_TIE:
1862         case LFUN_HUNG_UMLAUT:
1863         case LFUN_CIRCLE:
1864         case LFUN_OGONEK:
1865                 if (ev.argument.empty()) {
1866                         // As always...
1867                         owner_->getLyXFunc().handleKeyFunc(ev.action);
1868                 } else {
1869                         owner_->getLyXFunc().handleKeyFunc(ev.action);
1870                         owner_->getIntl().getTransManager()
1871                                 .TranslateAndInsert(ev.argument[0], bv_->getLyXText());
1872                         update(bv_->getLyXText(),
1873                                BufferView::SELECT
1874                                | BufferView::FITCUR
1875                                | BufferView::CHANGE);
1876                 }
1877                 break;
1878
1879         case LFUN_MATH_MACRO:
1880         case LFUN_MATH_DELIM:
1881         case LFUN_INSERT_MATRIX:
1882         case LFUN_INSERT_MATH:
1883         case LFUN_MATH_IMPORT_SELECTION: // Imports LaTeX from the X selection
1884         case LFUN_MATH_DISPLAY:          // Open or create a displayed math inset
1885         case LFUN_MATH_MODE:             // Open or create an inlined math inset
1886         case LFUN_GREEK:                 // Insert a single greek letter
1887                 mathDispatch(FuncRequest(bv_, ev.action, ev.argument));
1888                 break;
1889
1890         case LFUN_CITATION_INSERT:
1891         {
1892                 InsetCommandParams p;
1893                 p.setFromString(ev.argument);
1894
1895                 InsetCitation * inset = new InsetCitation(p);
1896                 if (!insertInset(inset))
1897                         delete inset;
1898                 else
1899                         updateInset(inset, true);
1900         }
1901         break;
1902
1903         case LFUN_INSERT_BIBTEX:
1904         {
1905                 // ale970405+lasgoutt970425
1906                 // The argument can be up to two tokens separated
1907                 // by a space. The first one is the bibstyle.
1908                 string const db = token(ev.argument, ' ', 0);
1909                 string bibstyle = token(ev.argument, ' ', 1);
1910                 if (bibstyle.empty())
1911                         bibstyle = "plain";
1912
1913                 InsetCommandParams p("BibTeX", db, bibstyle);
1914                 InsetBibtex * inset = new InsetBibtex(p);
1915
1916                 if (insertInset(inset)) {
1917                         if (ev.argument.empty())
1918                                 inset->edit(bv_);
1919                 } else
1920                         delete inset;
1921         }
1922         break;
1923
1924         // BibTeX data bases
1925         case LFUN_BIBDB_ADD:
1926         {
1927                 InsetBibtex * inset =
1928                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
1929                 if (inset) {
1930                         inset->addDatabase(ev.argument);
1931                 }
1932         }
1933         break;
1934
1935         case LFUN_BIBDB_DEL:
1936         {
1937                 InsetBibtex * inset =
1938                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
1939                 if (inset) {
1940                         inset->delDatabase(ev.argument);
1941                 }
1942         }
1943         break;
1944
1945         case LFUN_BIBTEX_STYLE:
1946         {
1947                 InsetBibtex * inset =
1948                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
1949                 if (inset) {
1950                         inset->setOptions(ev.argument);
1951                 }
1952         }
1953         break;
1954
1955         case LFUN_INDEX_INSERT:
1956         {
1957                 string entry = ev.argument;
1958                 if (entry.empty())
1959                         entry = bv_->getLyXText()->getStringToIndex(bv_);
1960
1961                 if (entry.empty()) {
1962                         owner_->getDialogs().createIndex();
1963                         break;
1964                 }
1965
1966                 InsetIndex * inset = new InsetIndex(InsetCommandParams("index", entry));
1967
1968                 if (!insertInset(inset)) {
1969                         delete inset;
1970                 } else {
1971                         updateInset(inset, true);
1972                 }
1973         }
1974         break;
1975
1976         case LFUN_INDEX_PRINT:
1977         {
1978                 InsetCommandParams p("printindex");
1979                 Inset * inset = new InsetPrintIndex(p);
1980                 if (!insertInset(inset, tclass.defaultLayoutName()))
1981                         delete inset;
1982         }
1983         break;
1984
1985         case LFUN_PARENTINSERT:
1986         {
1987                 InsetCommandParams p("lyxparent", ev.argument);
1988                 Inset * inset = new InsetParent(p, *buffer_);
1989                 if (!insertInset(inset, tclass.defaultLayoutName()))
1990                         delete inset;
1991         }
1992
1993         break;
1994
1995         case LFUN_CHILD_INSERT:
1996         {
1997                 InsetInclude::Params p;
1998                 p.cparams.setFromString(ev.argument);
1999                 p.masterFilename_ = buffer_->fileName();
2000
2001                 InsetInclude * inset = new InsetInclude(p);
2002                 if (!insertInset(inset))
2003                         delete inset;
2004                 else {
2005                         updateInset(inset, true);
2006                         bv_->owner()->getDialogs().showInclude(inset);
2007                 }
2008         }
2009         break;
2010
2011         case LFUN_FLOAT_LIST:
2012                 if (floatList.typeExist(ev.argument)) {
2013                         Inset * inset = new InsetFloatList(ev.argument);
2014                         if (!insertInset(inset, tclass.defaultLayoutName()))
2015                                 delete inset;
2016                 } else {
2017                         lyxerr << "Non-existent float type: "
2018                                << ev.argument << endl;
2019                 }
2020                 break;
2021
2022         case LFUN_THESAURUS_ENTRY:
2023         {
2024                 string arg = ev.argument;
2025
2026                 if (arg.empty()) {
2027                         arg = bv_->getLyXText()->selectionAsString(buffer_,
2028                                                                    false);
2029
2030                         // FIXME
2031                         if (arg.size() > 100 || arg.empty()) {
2032                                 // Get word or selection
2033                                 bv_->getLyXText()->selectWordWhenUnderCursor(bv_, LyXText::WHOLE_WORD);
2034                                 arg = bv_->getLyXText()->selectionAsString(buffer_, false);
2035                                 // FIXME: where is getLyXText()->unselect(bv_) ?
2036                         }
2037                 }
2038
2039                 bv_->owner()->getDialogs().showThesaurus(arg);
2040         }
2041                 break;
2042
2043         case LFUN_DATE_INSERT:  // jdblair: date-insert cmd
2044         {
2045                 time_t now_time_t = time(NULL);
2046                 struct tm * now_tm = localtime(&now_time_t);
2047                 setlocale(LC_TIME, "");
2048                 string arg;
2049                 if (!ev.argument.empty())
2050                         arg = ev.argument;
2051                 else
2052                         arg = lyxrc.date_insert_format;
2053                 char datetmp[32];
2054                 int const datetmp_len =
2055                         ::strftime(datetmp, 32, arg.c_str(), now_tm);
2056
2057                 LyXText * lt = bv_->getLyXText();
2058
2059                 for (int i = 0; i < datetmp_len; i++) {
2060                         lt->insertChar(bv_, datetmp[i]);
2061                         update(lt,
2062                                BufferView::SELECT
2063                                | BufferView::FITCUR
2064                                | BufferView::CHANGE);
2065                 }
2066
2067                 lt->selection.cursor = lt->cursor;
2068                 moveCursorUpdate(false);
2069         }
2070         break;
2071
2072         case LFUN_UNKNOWN_ACTION:
2073                 ev.errorMessage(N_("Unknown function!"));
2074                 break;
2075
2076         default:
2077                 FuncRequest cmd = ev;
2078                 cmd.setView(bv_);
2079                 return bv_->getLyXText()->dispatch(cmd);
2080         } // end of switch
2081
2082         return true;
2083 }
2084
2085
2086 void BufferView::Pimpl::smartQuote()
2087 {
2088         LyXText const * lt = bv_->getLyXText();
2089         Paragraph const * par = lt->cursor.par();
2090         pos_type pos = lt->cursor.pos();
2091         char c;
2092
2093         if (!pos
2094             || (par->isInset(pos - 1)
2095                 && par->getInset(pos - 1)->isSpace()))
2096                 c = ' ';
2097         else
2098                 c = par->getChar(pos - 1);
2099
2100         hideCursor();
2101
2102         LyXLayout_ptr const & style = par->layout();
2103
2104         if (style->pass_thru ||
2105             par->getFontSettings(buffer_->params,
2106                                  pos).language()->lang() == "hebrew" ||
2107                 (!insertInset(new InsetQuotes(c, buffer_->params))))
2108                 bv_->owner()->dispatch(FuncRequest(LFUN_SELFINSERT, "\""));
2109 }
2110
2111
2112 void BufferView::Pimpl::insertAndEditInset(Inset * inset)
2113 {
2114 #if 0
2115         if (insertInset(inset))
2116                 inset->edit(bv_);
2117         else
2118                 delete inset;
2119 #else
2120         bool gotsel = false;
2121
2122         if (bv_->getLyXText()->selection.set()) {
2123                 bv_->getLyXText()->cutSelection(bv_, true, false);
2124                 gotsel = true;
2125         }
2126
2127         if (insertInset(inset)) {
2128                 inset->edit(bv_);
2129                 if (gotsel)
2130                         owner_->dispatch(FuncRequest(LFUN_PASTESELECTION));
2131         }
2132         else
2133                 delete inset;
2134 #endif
2135 }
2136
2137
2138 // Open and lock an updatable inset
2139 bool BufferView::Pimpl::open_new_inset(UpdatableInset * new_inset, bool behind)
2140 {
2141         LyXText * lt = bv_->getLyXText();
2142
2143         beforeChange(lt);
2144         finishUndo();
2145         if (!insertInset(new_inset)) {
2146                 delete new_inset;
2147                 return false;
2148         }
2149         new_inset->edit(bv_, !behind);
2150         return true;
2151 }
2152
2153
2154 bool BufferView::Pimpl::insertInset(Inset * inset, string const & lout)
2155 {
2156         // if we are in a locking inset we should try to insert the
2157         // inset there otherwise this is a illegal function now
2158         if (bv_->theLockingInset()) {
2159                 if (bv_->theLockingInset()->insetAllowed(inset))
2160                     return bv_->theLockingInset()->insertInset(bv_, inset);
2161                 return false;
2162         }
2163
2164         // not quite sure if we want this...
2165         setCursorParUndo(bv_);
2166         freezeUndo();
2167
2168         beforeChange(bv_->text);
2169         if (!lout.empty()) {
2170                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
2171                 bv_->text->breakParagraph(bv_);
2172                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
2173
2174                 if (!bv_->text->cursor.par()->empty()) {
2175                         bv_->text->cursorLeft(bv_);
2176
2177                         bv_->text->breakParagraph(bv_);
2178                         update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
2179                 }
2180
2181                 string lres = lout;
2182                 LyXTextClass const & tclass =
2183                         buffer_->params.getLyXTextClass();
2184                 bool hasLayout = tclass.hasLayout(lres);
2185                 string lay = tclass.defaultLayoutName();
2186
2187                 if (hasLayout != false) {
2188                         // layout found
2189                         lay = lres;
2190                 } else {
2191                         // layout not fount using default
2192                         lay = tclass.defaultLayoutName();
2193                 }
2194
2195                 bv_->text->setLayout(bv_, lay);
2196
2197                 bv_->text->setParagraph(bv_, 0, 0,
2198                                    0, 0,
2199                                    VSpace(VSpace::NONE), VSpace(VSpace::NONE),
2200                                    Spacing(),
2201                                    LYX_ALIGN_LAYOUT,
2202                                    string(),
2203                                    0);
2204                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
2205         }
2206
2207         bv_->text->insertInset(bv_, inset);
2208         update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
2209
2210         unFreezeUndo();
2211         return true;
2212 }
2213
2214
2215 void BufferView::Pimpl::updateInset(Inset * inset, bool mark_dirty)
2216 {
2217         if (!inset || !available())
2218                 return;
2219
2220         // first check for locking insets
2221         if (bv_->theLockingInset()) {
2222                 if (bv_->theLockingInset() == inset) {
2223                         if (bv_->text->updateInset(bv_, inset)) {
2224                                 update();
2225                                 if (mark_dirty) {
2226                                         buffer_->markDirty();
2227                                 }
2228                                 updateScrollbar();
2229                                 return;
2230                         }
2231                 } else if (bv_->theLockingInset()->updateInsetInInset(bv_, inset)) {
2232                         if (bv_->text->updateInset(bv_,  bv_->theLockingInset())) {
2233                                 update();
2234                                 if (mark_dirty) {
2235                                         buffer_->markDirty();
2236                                 }
2237                                 updateScrollbar();
2238                                 return;
2239                         }
2240                 }
2241         }
2242
2243         // then check if the inset is a top_level inset (has no owner)
2244         // if yes do the update as always otherwise we have to update the
2245         // toplevel inset where this inset is inside
2246         Inset * tl_inset = inset;
2247         while (tl_inset->owner())
2248                 tl_inset = tl_inset->owner();
2249         hideCursor();
2250         if (tl_inset == inset) {
2251                 update(bv_->text, BufferView::UPDATE);
2252                 if (bv_->text->updateInset(bv_, inset)) {
2253                         if (mark_dirty) {
2254                                 update(bv_->text,
2255                                        BufferView::SELECT
2256                                        | BufferView::FITCUR
2257                                        | BufferView::CHANGE);
2258                         } else {
2259                                 update(bv_->text, SELECT);
2260                         }
2261                         return;
2262                 }
2263         } else if (static_cast<UpdatableInset *>(tl_inset)
2264                            ->updateInsetInInset(bv_, inset))
2265         {
2266                 if (bv_->text->updateInset(bv_, tl_inset)) {
2267                         update();
2268                         updateScrollbar();
2269                 }
2270         }
2271 }
2272
2273
2274 void BufferView::Pimpl::gotoInset(vector<Inset::Code> const & codes,
2275                                   bool same_content)
2276 {
2277         if (!available()) return;
2278
2279         hideCursor();
2280         beforeChange(bv_->text);
2281         update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
2282
2283         LyXCursor const & cursor = bv_->text->cursor;
2284
2285         string contents;
2286         if (same_content &&
2287             cursor.par()->isInset(cursor.pos())) {
2288                 Inset const * inset = cursor.par()->getInset(cursor.pos());
2289                 if (find(codes.begin(), codes.end(), inset->lyxCode())
2290                     != codes.end())
2291                         contents =
2292                                 static_cast<InsetCommand const *>(inset)->getContents();
2293         }
2294
2295
2296         if (!bv_->text->gotoNextInset(bv_, codes, contents)) {
2297                 if (bv_->text->cursor.pos()
2298                     || bv_->text->cursor.par() != bv_->text->ownerParagraph()) {
2299                         LyXCursor tmp = bv_->text->cursor;
2300                         bv_->text->cursor.par(bv_->text->ownerParagraph());
2301                         bv_->text->cursor.pos(0);
2302                         if (!bv_->text->gotoNextInset(bv_, codes, contents)) {
2303                                 bv_->text->cursor = tmp;
2304                                 bv_->owner()->message(_("No more insets"));
2305                         }
2306                 } else {
2307                         bv_->owner()->message(_("No more insets"));
2308                 }
2309         }
2310         update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
2311         bv_->text->selection.cursor = bv_->text->cursor;
2312 }
2313
2314
2315 void BufferView::Pimpl::gotoInset(Inset::Code code, bool same_content)
2316 {
2317         gotoInset(vector<Inset::Code>(1, code), same_content);
2318 }