]> git.lyx.org Git - lyx.git/blob - src/BufferView_pimpl.C
another 100+ lines bite the dust...
[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 inline
1371 void BufferView::Pimpl::moveCursorUpdate(bool selecting, bool fitcur)
1372 {
1373         LyXText * lt = bv_->getLyXText();
1374
1375         if (selecting || lt->selection.mark()) {
1376                 lt->setSelection(bv_);
1377                 if (lt->bv_owner)
1378                         toggleToggle();
1379                 else
1380                         updateInset(lt->inset_owner, false);
1381         }
1382         if (lt->bv_owner) {
1383                 if (fitcur)
1384                         update(lt, BufferView::SELECT|BufferView::FITCUR);
1385                 else
1386                         update(lt, BufferView::SELECT);
1387                 showCursor();
1388         } else if (bv_->text->status() != LyXText::UNCHANGED) {
1389                 bv_->theLockingInset()->hideInsetCursor(bv_);
1390                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
1391                 showCursor();
1392         }
1393
1394         if (!lt->selection.set())
1395                 workarea().haveSelection(false);
1396
1397         switchKeyMap();
1398 }
1399
1400
1401 Inset * BufferView::Pimpl::getInsetByCode(Inset::Code code)
1402 {
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 }
1411
1412
1413 void BufferView::Pimpl::MenuInsertLyXFile(string const & filen)
1414 {
1415         string filename = filen;
1416
1417         if (filename.empty()) {
1418                 // Launch a file browser
1419                 string initpath = lyxrc.document_path;
1420
1421                 if (available()) {
1422                         string const trypath = owner_->buffer()->filePath();
1423                         // If directory is writeable, use this as default.
1424                         if (IsDirWriteable(trypath))
1425                                 initpath = trypath;
1426                 }
1427
1428                 FileDialog fileDlg(bv_->owner(),
1429                                    _("Select LyX document to insert"),
1430                         LFUN_FILE_INSERT,
1431                         make_pair(string(_("Documents|#o#O")),
1432                                   string(lyxrc.document_path)),
1433                         make_pair(string(_("Examples|#E#e")),
1434                                   string(AddPath(system_lyxdir, "examples"))));
1435
1436                 FileDialog::Result result =
1437                         fileDlg.Select(initpath,
1438                                        _("*.lyx| LyX Documents (*.lyx)"));
1439
1440                 if (result.first == FileDialog::Later)
1441                         return;
1442
1443                 filename = result.second;
1444
1445                 // check selected filename
1446                 if (filename.empty()) {
1447                         owner_->message(_("Canceled."));
1448                         return;
1449                 }
1450         }
1451
1452         // get absolute path of file and add ".lyx" to the filename if
1453         // necessary
1454         filename = FileSearch(string(), filename, "lyx");
1455
1456         string const disp_fn(MakeDisplayPath(filename));
1457
1458         ostringstream s1;
1459         s1 << _("Inserting document") << ' '
1460            << disp_fn << " ...";
1461         owner_->message(s1.str().c_str());
1462         bool const res = bv_->insertLyXFile(filename);
1463         if (res) {
1464                 ostringstream str;
1465                 str << _("Document") << ' ' << disp_fn
1466                     << ' ' << _("inserted.");
1467                 owner_->message(str.str().c_str());
1468         } else {
1469                 ostringstream str;
1470                 str << _("Could not insert document") << ' '
1471                     << disp_fn;
1472                 owner_->message(str.str().c_str());
1473         }
1474 }
1475
1476
1477 bool BufferView::Pimpl::dispatch(FuncRequest const & ev)
1478 {
1479         lyxerr[Debug::ACTION] << "BufferView::Pimpl::Dispatch: action["
1480           << ev.action <<"] arg[" << ev.argument << "]" << endl;
1481
1482         LyXTextClass const & tclass = buffer_->params.getLyXTextClass();
1483
1484         switch (ev.action) {
1485
1486         case LFUN_TOC_INSERT:
1487         {
1488                 InsetCommandParams p;
1489                 p.setCmdName("tableofcontents");
1490                 Inset * inset = new InsetTOC(p);
1491                 if (!insertInset(inset, tclass.defaultLayoutName()))
1492                         delete inset;
1493                 break;
1494         }
1495
1496         case LFUN_SCROLL_INSET:
1497                 // this is not handled here as this function is only active
1498                 // if we have a locking_inset and that one is (or contains)
1499                 // a tabular-inset
1500                 break;
1501
1502         case LFUN_INSET_GRAPHICS:
1503         {
1504                 Inset * new_inset = new InsetGraphics;
1505                 if (!insertInset(new_inset)) {
1506                         delete new_inset;
1507                 } else {
1508                         // this is need because you don't use a inset->Edit()
1509                         updateInset(new_inset, true);
1510                         new_inset->edit(bv_);
1511                 }
1512                 break;
1513         }
1514
1515         case LFUN_PASTE:
1516                 bv_->paste();
1517                 switchKeyMap();
1518                 break;
1519
1520         case LFUN_CUT:
1521                 bv_->cut();
1522                 break;
1523
1524         case LFUN_COPY:
1525                 bv_->copy();
1526                 break;
1527
1528         case LFUN_LAYOUT_COPY:
1529                 bv_->copyEnvironment();
1530                 break;
1531
1532         case LFUN_LAYOUT_PASTE:
1533                 bv_->pasteEnvironment();
1534                 switchKeyMap();
1535                 break;
1536
1537         case LFUN_GOTOERROR:
1538                 gotoInset(Inset::ERROR_CODE, false);
1539                 break;
1540
1541         case LFUN_GOTONOTE:
1542                 gotoInset(Inset::NOTE_CODE, false);
1543                 break;
1544
1545         case LFUN_REFERENCE_GOTO:
1546         {
1547                 vector<Inset::Code> tmp;
1548                 tmp.push_back(Inset::LABEL_CODE);
1549                 tmp.push_back(Inset::REF_CODE);
1550                 gotoInset(tmp, true);
1551                 break;
1552         }
1553
1554         case LFUN_DEPTH_MIN:
1555                 changeDepth(bv_, bv_->getLyXText(), -1);
1556                 break;
1557
1558         case LFUN_DEPTH_PLUS:
1559                 changeDepth(bv_, bv_->getLyXText(), 1);
1560                 break;
1561
1562         case LFUN_FREE:
1563                 owner_->getDialogs().setUserFreeFont();
1564                 break;
1565
1566         case LFUN_FILE_INSERT:
1567                 MenuInsertLyXFile(ev.argument);
1568                 break;
1569
1570         case LFUN_FILE_INSERT_ASCII_PARA:
1571                 InsertAsciiFile(bv_, ev.argument, true);
1572                 break;
1573
1574         case LFUN_FILE_INSERT_ASCII:
1575                 InsertAsciiFile(bv_, ev.argument, false);
1576                 break;
1577
1578         case LFUN_LANGUAGE:
1579                 lang(bv_, ev.argument);
1580                 switchKeyMap();
1581                 owner_->view_state_changed();
1582                 break;
1583
1584         case LFUN_EMPH:
1585                 emph(bv_);
1586                 owner_->view_state_changed();
1587                 break;
1588
1589         case LFUN_BOLD:
1590                 bold(bv_);
1591                 owner_->view_state_changed();
1592                 break;
1593
1594         case LFUN_NOUN:
1595                 noun(bv_);
1596                 owner_->view_state_changed();
1597                 break;
1598
1599         case LFUN_CODE:
1600                 code(bv_);
1601                 owner_->view_state_changed();
1602                 break;
1603
1604         case LFUN_SANS:
1605                 sans(bv_);
1606                 owner_->view_state_changed();
1607                 break;
1608
1609         case LFUN_ROMAN:
1610                 roman(bv_);
1611                 owner_->view_state_changed();
1612                 break;
1613
1614         case LFUN_DEFAULT:
1615                 styleReset(bv_);
1616                 owner_->view_state_changed();
1617                 break;
1618
1619         case LFUN_UNDERLINE:
1620                 underline(bv_);
1621                 owner_->view_state_changed();
1622                 break;
1623
1624         case LFUN_FONT_SIZE:
1625                 fontSize(bv_, ev.argument);
1626                 owner_->view_state_changed();
1627                 break;
1628
1629         case LFUN_FONT_STATE:
1630                 owner_->getLyXFunc().setMessage(currentState(bv_));
1631                 break;
1632
1633         case LFUN_INSERT_LABEL:
1634                 MenuInsertLabel(bv_, ev.argument);
1635                 break;
1636
1637         case LFUN_REF_INSERT:
1638                 if (ev.argument.empty()) {
1639                         InsetCommandParams p("ref");
1640                         owner_->getDialogs().createRef(p.getAsString());
1641                 } else {
1642                         InsetCommandParams p;
1643                         p.setFromString(ev.argument);
1644
1645                         InsetRef * inset = new InsetRef(p, *buffer_);
1646                         if (!insertInset(inset))
1647                                 delete inset;
1648                         else
1649                                 updateInset(inset, true);
1650                 }
1651                 break;
1652
1653         case LFUN_BOOKMARK_SAVE:
1654                 savePosition(strToUnsignedInt(ev.argument));
1655                 break;
1656
1657         case LFUN_BOOKMARK_GOTO:
1658                 restorePosition(strToUnsignedInt(ev.argument));
1659                 break;
1660
1661         case LFUN_REF_GOTO:
1662         {
1663                 string label = ev.argument;
1664                 if (label.empty()) {
1665                         InsetRef * inset =
1666                                 static_cast<InsetRef*>(getInsetByCode(Inset::REF_CODE));
1667                         if (inset) {
1668                                 label = inset->getContents();
1669                                 savePosition(0);
1670                         }
1671                 }
1672
1673                 if (!label.empty()) {
1674                         //bv_->savePosition(0);
1675                         if (!bv_->gotoLabel(label))
1676                                 Alert::alert(_("Error"),
1677                                            _("Couldn't find this label"),
1678                                            _("in current document."));
1679                 }
1680         }
1681         break;
1682
1683         case LFUN_QUOTE:
1684                 smartQuote();
1685                 break;
1686
1687         case LFUN_HTMLURL:
1688         case LFUN_URL:
1689         {
1690                 InsetCommandParams p;
1691                 if (ev.action == LFUN_HTMLURL)
1692                         p.setCmdName("htmlurl");
1693                 else
1694                         p.setCmdName("url");
1695                 owner_->getDialogs().createUrl(p.getAsString());
1696         }
1697         break;
1698
1699         case LFUN_INSERT_URL:
1700         {
1701                 InsetCommandParams p;
1702                 p.setFromString(ev.argument);
1703
1704                 InsetUrl * inset = new InsetUrl(p);
1705                 if (!insertInset(inset))
1706                         delete inset;
1707                 else
1708                         updateInset(inset, true);
1709         }
1710         break;
1711
1712         case LFUN_INSET_ERT:
1713                 insertAndEditInset(new InsetERT(buffer_->params));
1714                 break;
1715
1716         case LFUN_INSET_EXTERNAL:
1717                 insertAndEditInset(new InsetExternal);
1718                 break;
1719
1720         case LFUN_INSET_FOOTNOTE:
1721                 insertAndEditInset(new InsetFoot(buffer_->params));
1722                 break;
1723
1724         case LFUN_INSET_MARGINAL:
1725                 insertAndEditInset(new InsetMarginal(buffer_->params));
1726                 break;
1727
1728         case LFUN_INSET_MINIPAGE:
1729                 insertAndEditInset(new InsetMinipage(buffer_->params));
1730                 break;
1731
1732         case LFUN_INSERT_NOTE:
1733                 insertAndEditInset(new InsetNote(buffer_->params));
1734                 break;
1735
1736         case LFUN_INSET_FLOAT:
1737                 // check if the float type exist
1738                 if (floatList.typeExist(ev.argument)) {
1739                         insertAndEditInset(new InsetFloat(buffer_->params,
1740                                                           ev.argument));
1741                 } else {
1742                         lyxerr << "Non-existent float type: "
1743                                << ev.argument << endl;
1744                 }
1745                 break;
1746
1747         case LFUN_INSET_WIDE_FLOAT:
1748                 // check if the float type exist
1749                 if (floatList.typeExist(ev.argument)) {
1750                         InsetFloat * new_inset =
1751                                 new InsetFloat(buffer_->params, ev.argument);
1752                         new_inset->wide(true);
1753                         insertAndEditInset(new_inset);
1754                 } else {
1755                         lyxerr << "Non-existent float type: "
1756                                << ev.argument << endl;
1757                 }
1758                 break;
1759
1760 #if 0
1761         case LFUN_INSET_LIST:
1762                 insertAndEditInset(new InsetList);
1763                 break;
1764
1765         case LFUN_INSET_THEOREM:
1766                 insertAndEditInset(new InsetTheorem);
1767                 break;
1768 #endif
1769
1770         case LFUN_INSET_CAPTION:
1771         {
1772                 // Do we have a locking inset...
1773                 if (bv_->theLockingInset()) {
1774                         lyxerr << "Locking inset code: "
1775                                << static_cast<int>(bv_->theLockingInset()->lyxCode());
1776                         InsetCaption * new_inset =
1777                                 new InsetCaption(buffer_->params);
1778                         new_inset->setOwner(bv_->theLockingInset());
1779                         new_inset->setAutoBreakRows(true);
1780                         new_inset->setDrawFrame(0, InsetText::LOCKED);
1781                         new_inset->setFrameColor(0, LColor::captionframe);
1782                         if (insertInset(new_inset))
1783                                 new_inset->edit(bv_);
1784                         else
1785                                 delete new_inset;
1786                 }
1787         }
1788         break;
1789
1790         case LFUN_TABULAR_INSERT:
1791         {
1792                 if (ev.argument.empty()) {
1793                         owner_->getDialogs().showTabularCreate();
1794                         break;
1795                 }
1796
1797                 int r = 2;
1798                 int c = 2;
1799                 ::sscanf(ev.argument.c_str(),"%d%d", &r, &c);
1800                 InsetTabular * new_inset =
1801                         new InsetTabular(*buffer_, r, c);
1802                 bool const rtl =
1803                         bv_->getLyXText()->real_current_font.isRightToLeft();
1804                 if (!open_new_inset(new_inset, rtl))
1805                         delete new_inset;
1806         }
1807         break;
1808
1809
1810         // --- accented characters ---------------------------
1811
1812         case LFUN_UMLAUT:
1813         case LFUN_CIRCUMFLEX:
1814         case LFUN_GRAVE:
1815         case LFUN_ACUTE:
1816         case LFUN_TILDE:
1817         case LFUN_CEDILLA:
1818         case LFUN_MACRON:
1819         case LFUN_DOT:
1820         case LFUN_UNDERDOT:
1821         case LFUN_UNDERBAR:
1822         case LFUN_CARON:
1823         case LFUN_SPECIAL_CARON:
1824         case LFUN_BREVE:
1825         case LFUN_TIE:
1826         case LFUN_HUNG_UMLAUT:
1827         case LFUN_CIRCLE:
1828         case LFUN_OGONEK:
1829                 if (ev.argument.empty()) {
1830                         // As always...
1831                         owner_->getLyXFunc().handleKeyFunc(ev.action);
1832                 } else {
1833                         owner_->getLyXFunc().handleKeyFunc(ev.action);
1834                         owner_->getIntl().getTransManager()
1835                                 .TranslateAndInsert(ev.argument[0], bv_->getLyXText());
1836                         update(bv_->getLyXText(),
1837                                BufferView::SELECT
1838                                | BufferView::FITCUR
1839                                | BufferView::CHANGE);
1840                 }
1841                 break;
1842
1843         case LFUN_MATH_MACRO:
1844         case LFUN_MATH_DELIM:
1845         case LFUN_INSERT_MATRIX:
1846         case LFUN_INSERT_MATH:
1847         case LFUN_MATH_IMPORT_SELECTION: // Imports LaTeX from the X selection
1848         case LFUN_MATH_DISPLAY:          // Open or create a displayed math inset
1849         case LFUN_MATH_MODE:             // Open or create an inlined math inset
1850         case LFUN_GREEK:                 // Insert a single greek letter
1851                 mathDispatch(FuncRequest(bv_, ev.action, ev.argument));
1852                 break;
1853
1854         case LFUN_CITATION_INSERT:
1855         {
1856                 InsetCommandParams p;
1857                 p.setFromString(ev.argument);
1858
1859                 InsetCitation * inset = new InsetCitation(p);
1860                 if (!insertInset(inset))
1861                         delete inset;
1862                 else
1863                         updateInset(inset, true);
1864         }
1865         break;
1866
1867         case LFUN_INSERT_BIBTEX:
1868         {
1869                 // ale970405+lasgoutt970425
1870                 // The argument can be up to two tokens separated
1871                 // by a space. The first one is the bibstyle.
1872                 string const db = token(ev.argument, ' ', 0);
1873                 string bibstyle = token(ev.argument, ' ', 1);
1874                 if (bibstyle.empty())
1875                         bibstyle = "plain";
1876
1877                 InsetCommandParams p("BibTeX", db, bibstyle);
1878                 InsetBibtex * inset = new InsetBibtex(p);
1879
1880                 if (insertInset(inset)) {
1881                         if (ev.argument.empty())
1882                                 inset->edit(bv_);
1883                 } else
1884                         delete inset;
1885         }
1886         break;
1887
1888         // BibTeX data bases
1889         case LFUN_BIBDB_ADD:
1890         {
1891                 InsetBibtex * inset =
1892                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
1893                 if (inset) {
1894                         inset->addDatabase(ev.argument);
1895                 }
1896         }
1897         break;
1898
1899         case LFUN_BIBDB_DEL:
1900         {
1901                 InsetBibtex * inset =
1902                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
1903                 if (inset) {
1904                         inset->delDatabase(ev.argument);
1905                 }
1906         }
1907         break;
1908
1909         case LFUN_BIBTEX_STYLE:
1910         {
1911                 InsetBibtex * inset =
1912                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
1913                 if (inset) {
1914                         inset->setOptions(ev.argument);
1915                 }
1916         }
1917         break;
1918
1919         case LFUN_INDEX_INSERT:
1920         {
1921                 string entry = ev.argument;
1922                 if (entry.empty())
1923                         entry = bv_->getLyXText()->getStringToIndex(bv_);
1924
1925                 if (entry.empty()) {
1926                         owner_->getDialogs().createIndex();
1927                         break;
1928                 }
1929
1930                 InsetIndex * inset = new InsetIndex(InsetCommandParams("index", entry));
1931
1932                 if (!insertInset(inset)) {
1933                         delete inset;
1934                 } else {
1935                         updateInset(inset, true);
1936                 }
1937         }
1938         break;
1939
1940         case LFUN_INDEX_PRINT:
1941         {
1942                 InsetCommandParams p("printindex");
1943                 Inset * inset = new InsetPrintIndex(p);
1944                 if (!insertInset(inset, tclass.defaultLayoutName()))
1945                         delete inset;
1946         }
1947         break;
1948
1949         case LFUN_PARENTINSERT:
1950         {
1951                 InsetCommandParams p("lyxparent", ev.argument);
1952                 Inset * inset = new InsetParent(p, *buffer_);
1953                 if (!insertInset(inset, tclass.defaultLayoutName()))
1954                         delete inset;
1955         }
1956
1957         break;
1958
1959         case LFUN_CHILD_INSERT:
1960         {
1961                 InsetInclude::Params p;
1962                 p.cparams.setFromString(ev.argument);
1963                 p.masterFilename_ = buffer_->fileName();
1964
1965                 InsetInclude * inset = new InsetInclude(p);
1966                 if (!insertInset(inset))
1967                         delete inset;
1968                 else {
1969                         updateInset(inset, true);
1970                         bv_->owner()->getDialogs().showInclude(inset);
1971                 }
1972         }
1973         break;
1974
1975         case LFUN_FLOAT_LIST:
1976                 if (floatList.typeExist(ev.argument)) {
1977                         Inset * inset = new InsetFloatList(ev.argument);
1978                         if (!insertInset(inset, tclass.defaultLayoutName()))
1979                                 delete inset;
1980                 } else {
1981                         lyxerr << "Non-existent float type: "
1982                                << ev.argument << endl;
1983                 }
1984                 break;
1985
1986         case LFUN_THESAURUS_ENTRY:
1987         {
1988                 string arg = ev.argument;
1989
1990                 if (arg.empty()) {
1991                         arg = bv_->getLyXText()->selectionAsString(buffer_,
1992                                                                    false);
1993
1994                         // FIXME
1995                         if (arg.size() > 100 || arg.empty()) {
1996                                 // Get word or selection
1997                                 bv_->getLyXText()->selectWordWhenUnderCursor(bv_, LyXText::WHOLE_WORD);
1998                                 arg = bv_->getLyXText()->selectionAsString(buffer_, false);
1999                                 // FIXME: where is getLyXText()->unselect(bv_) ?
2000                         }
2001                 }
2002
2003                 bv_->owner()->getDialogs().showThesaurus(arg);
2004         }
2005                 break;
2006
2007         case LFUN_DATE_INSERT:  // jdblair: date-insert cmd
2008         {
2009                 time_t now_time_t = time(NULL);
2010                 struct tm * now_tm = localtime(&now_time_t);
2011                 setlocale(LC_TIME, "");
2012                 string arg;
2013                 if (!ev.argument.empty())
2014                         arg = ev.argument;
2015                 else
2016                         arg = lyxrc.date_insert_format;
2017                 char datetmp[32];
2018                 int const datetmp_len =
2019                         ::strftime(datetmp, 32, arg.c_str(), now_tm);
2020
2021                 LyXText * lt = bv_->getLyXText();
2022
2023                 for (int i = 0; i < datetmp_len; i++) {
2024                         lt->insertChar(bv_, datetmp[i]);
2025                         update(lt,
2026                                BufferView::SELECT
2027                                | BufferView::FITCUR
2028                                | BufferView::CHANGE);
2029                 }
2030
2031                 lt->selection.cursor = lt->cursor;
2032                 moveCursorUpdate(false);
2033         }
2034         break;
2035
2036         case LFUN_UNKNOWN_ACTION:
2037                 ev.errorMessage(N_("Unknown function!"));
2038                 break;
2039
2040         default:
2041                 FuncRequest cmd = ev;
2042                 cmd.setView(bv_);
2043                 return bv_->getLyXText()->dispatch(cmd);
2044         } // end of switch
2045
2046         return true;
2047 }
2048
2049
2050 void BufferView::Pimpl::smartQuote()
2051 {
2052         LyXText const * lt = bv_->getLyXText();
2053         Paragraph const * par = lt->cursor.par();
2054         pos_type pos = lt->cursor.pos();
2055         char c;
2056
2057         if (!pos
2058             || (par->isInset(pos - 1)
2059                 && par->getInset(pos - 1)->isSpace()))
2060                 c = ' ';
2061         else
2062                 c = par->getChar(pos - 1);
2063
2064         hideCursor();
2065
2066         LyXLayout_ptr const & style = par->layout();
2067
2068         if (style->pass_thru ||
2069             par->getFontSettings(buffer_->params,
2070                                  pos).language()->lang() == "hebrew" ||
2071                 (!insertInset(new InsetQuotes(c, buffer_->params))))
2072                 bv_->owner()->dispatch(FuncRequest(LFUN_SELFINSERT, "\""));
2073 }
2074
2075
2076 void BufferView::Pimpl::insertAndEditInset(Inset * inset)
2077 {
2078 #if 0
2079         if (insertInset(inset))
2080                 inset->edit(bv_);
2081         else
2082                 delete inset;
2083 #else
2084         bool gotsel = false;
2085
2086         if (bv_->getLyXText()->selection.set()) {
2087                 bv_->getLyXText()->cutSelection(bv_, true, false);
2088                 gotsel = true;
2089         }
2090
2091         if (insertInset(inset)) {
2092                 inset->edit(bv_);
2093                 if (gotsel)
2094                         owner_->dispatch(FuncRequest(LFUN_PASTESELECTION));
2095         }
2096         else
2097                 delete inset;
2098 #endif
2099 }
2100
2101
2102 // Open and lock an updatable inset
2103 bool BufferView::Pimpl::open_new_inset(UpdatableInset * new_inset, bool behind)
2104 {
2105         LyXText * lt = bv_->getLyXText();
2106
2107         beforeChange(lt);
2108         finishUndo();
2109         if (!insertInset(new_inset)) {
2110                 delete new_inset;
2111                 return false;
2112         }
2113         new_inset->edit(bv_, !behind);
2114         return true;
2115 }
2116
2117
2118 bool BufferView::Pimpl::insertInset(Inset * inset, string const & lout)
2119 {
2120         // if we are in a locking inset we should try to insert the
2121         // inset there otherwise this is a illegal function now
2122         if (bv_->theLockingInset()) {
2123                 if (bv_->theLockingInset()->insetAllowed(inset))
2124                     return bv_->theLockingInset()->insertInset(bv_, inset);
2125                 return false;
2126         }
2127
2128         // not quite sure if we want this...
2129         setCursorParUndo(bv_);
2130         freezeUndo();
2131
2132         beforeChange(bv_->text);
2133         if (!lout.empty()) {
2134                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
2135                 bv_->text->breakParagraph(bv_);
2136                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
2137
2138                 if (!bv_->text->cursor.par()->empty()) {
2139                         bv_->text->cursorLeft(bv_);
2140
2141                         bv_->text->breakParagraph(bv_);
2142                         update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
2143                 }
2144
2145                 string lres = lout;
2146                 LyXTextClass const & tclass =
2147                         buffer_->params.getLyXTextClass();
2148                 bool hasLayout = tclass.hasLayout(lres);
2149                 string lay = tclass.defaultLayoutName();
2150
2151                 if (hasLayout != false) {
2152                         // layout found
2153                         lay = lres;
2154                 } else {
2155                         // layout not fount using default
2156                         lay = tclass.defaultLayoutName();
2157                 }
2158
2159                 bv_->text->setLayout(bv_, lay);
2160
2161                 bv_->text->setParagraph(bv_, 0, 0,
2162                                    0, 0,
2163                                    VSpace(VSpace::NONE), VSpace(VSpace::NONE),
2164                                    Spacing(),
2165                                    LYX_ALIGN_LAYOUT,
2166                                    string(),
2167                                    0);
2168                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
2169         }
2170
2171         bv_->text->insertInset(bv_, inset);
2172         update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
2173
2174         unFreezeUndo();
2175         return true;
2176 }
2177
2178
2179 void BufferView::Pimpl::updateInset(Inset * inset, bool mark_dirty)
2180 {
2181         if (!inset || !available())
2182                 return;
2183
2184         // first check for locking insets
2185         if (bv_->theLockingInset()) {
2186                 if (bv_->theLockingInset() == inset) {
2187                         if (bv_->text->updateInset(bv_, inset)) {
2188                                 update();
2189                                 if (mark_dirty) {
2190                                         buffer_->markDirty();
2191                                 }
2192                                 updateScrollbar();
2193                                 return;
2194                         }
2195                 } else if (bv_->theLockingInset()->updateInsetInInset(bv_, inset)) {
2196                         if (bv_->text->updateInset(bv_,  bv_->theLockingInset())) {
2197                                 update();
2198                                 if (mark_dirty) {
2199                                         buffer_->markDirty();
2200                                 }
2201                                 updateScrollbar();
2202                                 return;
2203                         }
2204                 }
2205         }
2206
2207         // then check if the inset is a top_level inset (has no owner)
2208         // if yes do the update as always otherwise we have to update the
2209         // toplevel inset where this inset is inside
2210         Inset * tl_inset = inset;
2211         while (tl_inset->owner())
2212                 tl_inset = tl_inset->owner();
2213         hideCursor();
2214         if (tl_inset == inset) {
2215                 update(bv_->text, BufferView::UPDATE);
2216                 if (bv_->text->updateInset(bv_, inset)) {
2217                         if (mark_dirty) {
2218                                 update(bv_->text,
2219                                        BufferView::SELECT
2220                                        | BufferView::FITCUR
2221                                        | BufferView::CHANGE);
2222                         } else {
2223                                 update(bv_->text, SELECT);
2224                         }
2225                         return;
2226                 }
2227         } else if (static_cast<UpdatableInset *>(tl_inset)
2228                            ->updateInsetInInset(bv_, inset))
2229         {
2230                 if (bv_->text->updateInset(bv_, tl_inset)) {
2231                         update();
2232                         updateScrollbar();
2233                 }
2234         }
2235 }
2236
2237
2238 void BufferView::Pimpl::gotoInset(vector<Inset::Code> const & codes,
2239                                   bool same_content)
2240 {
2241         if (!available()) return;
2242
2243         hideCursor();
2244         beforeChange(bv_->text);
2245         update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
2246
2247         LyXCursor const & cursor = bv_->text->cursor;
2248
2249         string contents;
2250         if (same_content &&
2251             cursor.par()->isInset(cursor.pos())) {
2252                 Inset const * inset = cursor.par()->getInset(cursor.pos());
2253                 if (find(codes.begin(), codes.end(), inset->lyxCode())
2254                     != codes.end())
2255                         contents =
2256                                 static_cast<InsetCommand const *>(inset)->getContents();
2257         }
2258
2259
2260         if (!bv_->text->gotoNextInset(bv_, codes, contents)) {
2261                 if (bv_->text->cursor.pos()
2262                     || bv_->text->cursor.par() != bv_->text->ownerParagraph()) {
2263                         LyXCursor tmp = bv_->text->cursor;
2264                         bv_->text->cursor.par(bv_->text->ownerParagraph());
2265                         bv_->text->cursor.pos(0);
2266                         if (!bv_->text->gotoNextInset(bv_, codes, contents)) {
2267                                 bv_->text->cursor = tmp;
2268                                 bv_->owner()->message(_("No more insets"));
2269                         }
2270                 } else {
2271                         bv_->owner()->message(_("No more insets"));
2272                 }
2273         }
2274         update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
2275         bv_->text->selection.cursor = bv_->text->cursor;
2276 }
2277
2278
2279 void BufferView::Pimpl::gotoInset(Inset::Code code, bool same_content)
2280 {
2281         gotoInset(vector<Inset::Code>(1, code), same_content);
2282 }