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