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