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