]> git.lyx.org Git - lyx.git/blob - src/BufferView_pimpl.C
fix bug with ever-growing bst files list (from herbert)
[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
43 #include "insets/insetbib.h"
44 #include "insets/insettext.h"
45 #include "insets/inseturl.h"
46 #include "insets/insetlatexaccent.h"
47 #include "insets/insettoc.h"
48 #include "insets/insetref.h"
49 #include "insets/insetparent.h"
50 #include "insets/insetindex.h"
51 #include "insets/insetnote.h"
52 #include "insets/insetinclude.h"
53 #include "insets/insetcite.h"
54 #include "insets/insetert.h"
55 #include "insets/insetexternal.h"
56 #include "insets/insetgraphics.h"
57 #include "insets/insetfoot.h"
58 #include "insets/insetmarginal.h"
59 #include "insets/insetminipage.h"
60 #include "insets/insetfloat.h"
61 #include "insets/insettabular.h"
62 #if 0
63 #include "insets/insettheorem.h"
64 #include "insets/insetlist.h"
65 #endif
66 #include "insets/insetcaption.h"
67 #include "insets/insetfloatlist.h"
68 #include "insets/insetspecialchar.h"
69
70 #include "mathed/formulabase.h"
71
72 #include "graphics/Previews.h"
73
74 #include "support/LAssert.h"
75 #include "support/lstrings.h"
76 #include "support/filetools.h"
77 #include "support/lyxfunctional.h"
78
79 #include <boost/bind.hpp>
80
81 #include <cstdio>
82 #include <ctime>
83 #include <unistd.h>
84 #include <sys/wait.h>
85 #include <clocale>
86
87
88 extern string current_layout;
89
90 #ifndef CXX_GLOBAL_CSTD
91 using std::tm;
92 using std::localtime;
93 using std::time;
94 using std::setlocale;
95 using std::strftime;
96 #endif
97
98 using std::vector;
99 using std::find_if;
100 using std::find;
101 using std::pair;
102 using std::endl;
103 using std::make_pair;
104 using std::min;
105
106 using lyx::pos_type;
107 using lyx::textclass_type;
108
109 /* the selection possible is needed, that only motion events are
110  * used, where the bottom press event was on the drawing area too */
111 bool selection_possible = false;
112
113 extern BufferList bufferlist;
114 extern char ascii_type;
115
116 extern int bibitemMaxWidth(BufferView *, LyXFont const &);
117
118
119 namespace {
120
121 unsigned int const saved_positions_num = 20;
122
123 // All the below connection objects are needed because of a bug in some
124 // versions of GCC (<=2.96 are on the suspects list.) By having and assigning
125 // to these connections we avoid a segfault upon startup, and also at exit.
126 // (Lgb)
127
128 boost::signals::connection timecon;
129 boost::signals::connection doccon;
130 boost::signals::connection resizecon;
131 boost::signals::connection bpresscon;
132 boost::signals::connection breleasecon;
133 boost::signals::connection motioncon;
134 boost::signals::connection doublecon;
135 boost::signals::connection triplecon;
136 boost::signals::connection kpresscon;
137 boost::signals::connection selectioncon;
138 boost::signals::connection lostcon;
139
140 } // anon namespace
141
142
143 BufferView::Pimpl::Pimpl(BufferView * b, LyXView * o,
144              int xpos, int ypos, int width, int height)
145         : bv_(b), owner_(o), buffer_(0), cursor_timeout(400),
146           using_xterm_cursor(false)
147 {
148         workarea_.reset(WorkAreaFactory::create(xpos, ypos, width, height));
149         screen_.reset(LyXScreenFactory::create(workarea()));
150
151         // Setup the signals
152         doccon = workarea().scrollDocView.connect(boost::bind(&BufferView::Pimpl::scrollDocView, this, _1));
153         resizecon = workarea().workAreaResize
154                 .connect(boost::bind(&BufferView::Pimpl::workAreaResize, this));
155         bpresscon = workarea().workAreaButtonPress
156                 .connect(boost::bind(&BufferView::Pimpl::workAreaButtonPress, this, _1, _2, _3));
157         breleasecon = workarea().workAreaButtonRelease
158                 .connect(boost::bind(&BufferView::Pimpl::workAreaButtonRelease, this, _1, _2, _3));
159         motioncon = workarea().workAreaMotionNotify
160                 .connect(boost::bind(&BufferView::Pimpl::workAreaMotionNotify, this, _1, _2, _3));
161         doublecon = workarea().workAreaDoubleClick
162                 .connect(boost::bind(&BufferView::Pimpl::doubleClick, this, _1, _2, _3));
163         triplecon = workarea().workAreaTripleClick
164                 .connect(boost::bind(&BufferView::Pimpl::tripleClick, this, _1, _2, _3));
165         kpresscon = workarea().workAreaKeyPress
166                 .connect(boost::bind(&BufferView::Pimpl::workAreaKeyPress, this, _1, _2));
167         selectioncon = workarea().selectionRequested
168                 .connect(boost::bind(&BufferView::Pimpl::selectionRequested, this));
169         lostcon = workarea().selectionLost
170                 .connect(boost::bind(&BufferView::Pimpl::selectionLost, this));
171
172         timecon = cursor_timeout.timeout.connect(boost::bind(&BufferView::Pimpl::cursorToggle, this));
173         cursor_timeout.start();
174         saved_positions.resize(saved_positions_num);
175 }
176
177
178 WorkArea & BufferView::Pimpl::workarea() const
179 {
180         return *workarea_.get();
181 }
182
183
184 LyXScreen & BufferView::Pimpl::screen() const
185 {
186         return *screen_.get();
187 }
188
189
190 Painter & BufferView::Pimpl::painter() const
191 {
192         return workarea().getPainter();
193 }
194
195
196 void BufferView::Pimpl::buffer(Buffer * b)
197 {
198         lyxerr[Debug::INFO] << "Setting buffer in BufferView ("
199                             << b << ")" << endl;
200         if (buffer_) {
201                 buffer_->delUser(bv_);
202
203                 // Put the old text into the TextCache, but
204                 // only if the buffer is still loaded.
205                 // Also set the owner of the test to 0
206                 //              bv_->text->owner(0);
207                 textcache.add(buffer_, workarea().workWidth(), bv_->text);
208                 if (lyxerr.debugging())
209                         textcache.show(lyxerr, "BufferView::buffer");
210
211                 bv_->text = 0;
212         }
213
214         // Set current buffer
215         buffer_ = b;
216
217         if (bufferlist.getState() == BufferList::CLOSING) return;
218
219         // If we are closing the buffer, use the first buffer as current
220         if (!buffer_) {
221                 buffer_ = bufferlist.first();
222         }
223
224         if (buffer_) {
225                 lyxerr[Debug::INFO] << "Buffer addr: " << buffer_ << endl;
226                 buffer_->addUser(bv_);
227
228                 // If we don't have a text object for this, we make one
229                 if (bv_->text == 0) {
230                         resizeCurrentBuffer();
231                 }
232
233                 // FIXME: needed when ?
234                 bv_->text->first_y = screen().topCursorVisible(bv_->text->cursor, bv_->text->first_y);
235
236                 // Similarly, buffer-dependent dialogs should be updated or
237                 // hidden. This should go here because some dialogs (eg ToC)
238                 // require bv_->text.
239                 owner_->getDialogs()->updateBufferDependent(true);
240         } else {
241                 lyxerr[Debug::INFO] << "  No Buffer!" << endl;
242                 owner_->getDialogs()->hideBufferDependent();
243
244                 // Also remove all remaining text's from the testcache.
245                 // (there should not be any!) (if there is any it is a
246                 // bug!)
247                 if (lyxerr.debugging())
248                         textcache.show(lyxerr, "buffer delete all");
249                 textcache.clear();
250         }
251
252         repaint();
253         updateScrollbar();
254         owner_->updateMenubar();
255         owner_->updateToolbar();
256         owner_->updateLayoutChoice();
257         owner_->updateWindowTitle();
258
259         if (grfx::Previews::activated() && buffer_)
260                 grfx::Previews::get().generateBufferPreviews(*buffer_);
261 }
262
263
264 bool BufferView::Pimpl::fitCursor()
265 {
266         bool ret;
267
268         if (bv_->theLockingInset()) {
269                 bv_->theLockingInset()->fitInsetCursor(bv_);
270                 ret = true;
271         } else {
272                 ret = screen().fitCursor(bv_->text, bv_);
273         }
274
275         bv_->owner()->getDialogs()->updateParagraph();
276         if (ret)
277                 updateScrollbar();
278         return ret;
279 }
280
281
282 void BufferView::Pimpl::redoCurrentBuffer()
283 {
284         lyxerr[Debug::INFO] << "BufferView::redoCurrentBuffer" << endl;
285         if (buffer_ && bv_->text) {
286                 resizeCurrentBuffer();
287                 owner_->updateLayoutChoice();
288         }
289 }
290
291
292 int BufferView::Pimpl::resizeCurrentBuffer()
293 {
294         lyxerr[Debug::INFO] << "resizeCurrentBuffer" << endl;
295
296         Paragraph * par = 0;
297         Paragraph * selstartpar = 0;
298         Paragraph * selendpar = 0;
299         UpdatableInset * the_locking_inset = 0;
300
301         pos_type pos = 0;
302         pos_type selstartpos = 0;
303         pos_type selendpos = 0;
304         bool selection = false;
305         bool mark_set  = false;
306
307         owner_->prohibitInput();
308
309         owner_->message(_("Formatting document..."));
310
311         if (bv_->text) {
312                 par = bv_->text->cursor.par();
313                 pos = bv_->text->cursor.pos();
314                 selstartpar = bv_->text->selection.start.par();
315                 selstartpos = bv_->text->selection.start.pos();
316                 selendpar = bv_->text->selection.end.par();
317                 selendpos = bv_->text->selection.end.pos();
318                 selection = bv_->text->selection.set();
319                 mark_set = bv_->text->selection.mark();
320                 the_locking_inset = bv_->theLockingInset();
321                 buffer_->resizeInsets(bv_);
322                 // I don't think the delete and new are necessary here we just could
323                 // call only init! (Jug 20020419)
324                 delete bv_->text;
325                 bv_->text = new LyXText(bv_);
326                 bv_->text->init(bv_);
327         } else {
328                 // See if we have a text in TextCache that fits
329                 // the new buffer_ with the correct width.
330                 bv_->text = textcache.findFit(buffer_, workarea().workWidth());
331                 if (bv_->text) {
332                         if (lyxerr.debugging()) {
333                                 lyxerr << "Found a LyXText that fits:\n";
334                                 textcache.show(lyxerr, make_pair(buffer_, make_pair(workarea().workWidth(), bv_->text)));
335                         }
336                         // Set the owner of the newly found text
337                         //      bv_->text->owner(bv_);
338                         if (lyxerr.debugging())
339                                 textcache.show(lyxerr, "resizeCurrentBuffer");
340                 } else {
341                         bv_->text = new LyXText(bv_);
342                         bv_->text->init(bv_);
343                         //buffer_->resizeInsets(bv_);
344                 }
345         }
346
347         if (par) {
348                 bv_->text->selection.set(true);
349                 // At this point just to avoid the Delete-Empty-Paragraph-
350                 // Mechanism when setting the cursor.
351                 bv_->text->selection.mark(mark_set);
352                 if (selection) {
353                         bv_->text->setCursor(bv_, selstartpar, selstartpos);
354                         bv_->text->selection.cursor = bv_->text->cursor;
355                         bv_->text->setCursor(bv_, selendpar, selendpos);
356                         bv_->text->setSelection(bv_);
357                         bv_->text->setCursor(bv_, par, pos);
358                 } else {
359                         bv_->text->setCursor(bv_, par, pos);
360                         bv_->text->selection.cursor = bv_->text->cursor;
361                         bv_->text->selection.set(false);
362                 }
363                 // remake the inset locking
364                 bv_->theLockingInset(the_locking_inset);
365         }
366
367         bv_->text->first_y = screen().topCursorVisible(bv_->text->cursor, bv_->text->first_y);
368
369         switchKeyMap();
370         owner_->allowInput();
371
372         /// clear the "Formatting Document" message
373         owner_->message("");
374
375         return 0;
376 }
377
378
379 void BufferView::Pimpl::repaint()
380 {
381         // Regenerate the screen.
382         screen().redraw(bv_->text, bv_);
383 }
384
385
386 void BufferView::Pimpl::updateScrollbar()
387 {
388         if (!bv_->text) {
389                 lyxerr[Debug::GUI] << "no text in updateScrollbar" << endl;
390                 workarea().setScrollbarParams(0, 0, 0);
391                 return;
392         }
393
394         LyXText const & t = *bv_->text;
395
396         lyxerr[Debug::GUI] << "Updating scrollbar: h " << t.height << ", first_y "
397                 << t.first_y << ", default height " << t.defaultHeight() << endl;
398
399         workarea().setScrollbarParams(t.height, t.first_y, t.defaultHeight());
400 }
401
402
403 void BufferView::Pimpl::scrollDocView(int value)
404 {
405         lyxerr[Debug::GUI] << "scrollDocView of " << value << endl;
406
407         if (!buffer_) return;
408
409         screen().draw(bv_->text, bv_, value);
410
411         if (!lyxrc.cursor_follows_scrollbar) {
412                 return;
413         }
414
415         LyXText * vbt = bv_->text;
416
417         int const height = vbt->defaultHeight();
418         int const first = static_cast<int>((bv_->text->first_y + height));
419         int const last = static_cast<int>((bv_->text->first_y + workarea().workHeight() - height));
420
421         if (vbt->cursor.y() < first)
422                 vbt->setCursorFromCoordinates(bv_, 0, first);
423         else if (vbt->cursor.y() > last)
424                 vbt->setCursorFromCoordinates(bv_, 0, last);
425 }
426
427
428 int BufferView::Pimpl::scroll(long time)
429 {
430         if (!buffer_)
431                 return 0;
432
433         LyXText const * t = bv_->text;
434
435         double const diff = t->defaultHeight()
436                 + double(time) * double(time) * 0.125;
437
438         scrollDocView(int(diff));
439         workarea().setScrollbarParams(t->height, t->first_y, t->defaultHeight());
440         return 0;
441 }
442
443
444 void BufferView::Pimpl::workAreaKeyPress(LyXKeySymPtr key,
445                                          key_modifier::state state)
446 {
447         bv_->owner()->getLyXFunc()->processKeySym(key, state);
448 }
449
450
451 void BufferView::Pimpl::workAreaMotionNotify(int x, int y, mouse_button::state state)
452 {
453         // Only use motion with button 1
454         if (!(state & mouse_button::button1))
455                 return;
456
457         if (!buffer_)
458                 return;
459
460         // Check for inset locking
461         if (bv_->theLockingInset()) {
462                 LyXCursor cursor = bv_->text->cursor;
463                 LyXFont font = bv_->text->getFont(buffer_,
464                                                   cursor.par(), cursor.pos());
465                 int width = bv_->theLockingInset()->width(bv_, font);
466                 int inset_x = font.isVisibleRightToLeft()
467                         ? cursor.ix() - width : cursor.ix();
468                 int start_x = inset_x + bv_->theLockingInset()->scroll();
469
470                 bv_->theLockingInset()->
471                         insetMotionNotify(bv_,
472                                           x - start_x,
473                                           y - cursor.iy() + bv_->text->first_y,
474                                           state);
475                 return;
476         }
477
478         /* The test for not selection possible is needed, that only motion
479            events are used, where the bottom press event was on
480            the drawing area too */
481         if (!selection_possible)
482                 return;
483
484         screen().hideCursor();
485
486         Row * cursorrow = bv_->text->cursor.row();
487         bv_->text->setCursorFromCoordinates(bv_, x, y + bv_->text->first_y);
488 #if 0
489         // sorry for this but I have a strange error that the y value jumps at
490         // a certain point. This seems like an error in my xforms library or
491         // in some other local environment, but I would like to leave this here
492         // for the moment until I can remove this (Jug 20020418)
493         if (y_before < bv_->text->cursor.y())
494                 lyxerr << y_before << ":" << bv_->text->cursor.y() << endl;
495 #endif
496         // This is to allow jumping over large insets
497         if (cursorrow == bv_->text->cursor.row()) {
498                 if (y >= int(workarea().workHeight())) {
499                         bv_->text->cursorDown(bv_, false);
500                 } else if (y < 0) {
501                         bv_->text->cursorUp(bv_, false);
502                 }
503         }
504
505         if (!bv_->text->selection.set())
506                 update(bv_->text, BufferView::UPDATE); // Maybe an empty line was deleted
507
508         bv_->text->setSelection(bv_);
509         screen().toggleToggle(bv_->text, bv_);
510         fitCursor();
511         showCursor();
512 }
513
514
515 // Single-click on work area
516 void BufferView::Pimpl::workAreaButtonPress(int xpos, int ypos,
517                                             mouse_button::state button)
518 {
519         if (!buffer_)
520                 return;
521
522         // ok ok, this is a hack (for xforms)
523         if (button == mouse_button::button4) {
524                 scroll(-lyxrc.wheel_jump);
525                 // We shouldn't go further down as we really should only do the
526                 // scrolling and be done with this. Otherwise we may open some
527                 // dialogs (Jug 20020424).
528                 return;
529         } else if (button == mouse_button::button5) {
530                 scroll(lyxrc.wheel_jump);
531                 // We shouldn't go further down as we really should only do the
532                 // scrolling and be done with this. Otherwise we may open some
533                 // dialogs (Jug 20020424).
534                 return;
535         }
536
537         Inset * inset_hit = checkInsetHit(bv_->text, xpos, ypos);
538
539         // Middle button press pastes if we have a selection
540         // We do this here as if the selection was inside an inset
541         // it could get cleared on the unlocking of the inset so
542         // we have to check this first
543         bool paste_internally = false;
544         if (button == mouse_button::button2 && bv_->getLyXText()->selection.set()) {
545                 owner_->getLyXFunc()->dispatch(LFUN_COPY);
546                 paste_internally = true;
547         }
548
549         int const screen_first = bv_->text->first_y;
550
551         if (bv_->theLockingInset()) {
552                 // We are in inset locking mode
553
554                 /* Check whether the inset was hit. If not reset mode,
555                    otherwise give the event to the inset */
556                 if (inset_hit == bv_->theLockingInset()) {
557                         bv_->theLockingInset()->
558                                 insetButtonPress(bv_, xpos, ypos, button);
559                         return;
560                 } else {
561                         bv_->unlockInset(bv_->theLockingInset());
562                 }
563         }
564
565         if (!inset_hit)
566                 selection_possible = true;
567         screen().hideCursor();
568
569         // Clear the selection
570         screen().toggleSelection(bv_->text, bv_);
571         bv_->text->clearSelection();
572         bv_->text->fullRebreak(bv_);
573         update();
574         updateScrollbar();
575
576         // Single left click in math inset?
577         if (isHighlyEditableInset(inset_hit)) {
578                 // Highly editable inset, like math
579                 UpdatableInset * inset = static_cast<UpdatableInset *>(inset_hit);
580                 selection_possible = false;
581                 owner_->updateLayoutChoice();
582                 owner_->message(inset->editMessage());
583                 //inset->edit(bv_, xpos, ypos, button);
584                 // We just have to lock the inset before calling a PressEvent on it!
585                 // we don't need the edit() call here! (Jug20020329)
586                 if (!bv_->lockInset(inset)) {
587                         lyxerr[Debug::INSETS] << "Cannot lock inset" << endl;
588                 }
589                 inset->insetButtonPress(bv_, xpos, ypos, button);
590                 return;
591         }
592         // I'm not sure we should continue here if we hit an inset (Jug20020403)
593
594         // Right click on a footnote flag opens float menu
595         if (button == mouse_button::button3) {
596                 selection_possible = false;
597                 return;
598         }
599
600         if (!inset_hit) // otherwise it was already set in checkInsetHit(...)
601                 bv_->text->setCursorFromCoordinates(bv_, xpos, ypos + screen_first);
602         finishUndo();
603         bv_->text->selection.cursor = bv_->text->cursor;
604         bv_->text->cursor.x_fix(bv_->text->cursor.x());
605
606         owner_->updateLayoutChoice();
607         if (fitCursor()) {
608                 selection_possible = false;
609         }
610
611         // Insert primary selection with middle mouse
612         // if there is a local selection in the current buffer,
613         // insert this
614         if (button == mouse_button::button2) {
615                 if (paste_internally)
616                         owner_->getLyXFunc()->dispatch(LFUN_PASTE);
617                 else
618                         owner_->getLyXFunc()->dispatch(LFUN_PASTESELECTION,
619                                                        string("paragraph"));
620                 selection_possible = false;
621                 return;
622         }
623 }
624
625
626 void BufferView::Pimpl::doubleClick(int /*x*/, int /*y*/, mouse_button::state button)
627 {
628         if (!buffer_)
629                 return;
630
631         LyXText * text = bv_->getLyXText();
632
633         if (text->bv_owner && bv_->theLockingInset())
634                 return;
635
636         if (button == mouse_button::button1) {
637                 if (text->bv_owner) {
638                         screen().hideCursor();
639                         screen().toggleSelection(text, bv_);
640                         text->selectWord(bv_, LyXText::WHOLE_WORD_STRICT);
641                         screen().toggleSelection(text, bv_, false);
642                 } else {
643                         text->selectWord(bv_, LyXText::WHOLE_WORD_STRICT);
644                 }
645                 /* This will fit the cursor on the screen
646                  * if necessary */
647                 update(text, BufferView::SELECT|BufferView::FITCUR);
648         }
649 }
650
651
652 void BufferView::Pimpl::tripleClick(int /*x*/, int /*y*/, mouse_button::state button)
653 {
654         if (!buffer_)
655                 return;
656
657         LyXText * text = bv_->getLyXText();
658
659         if (text->bv_owner && bv_->theLockingInset())
660             return;
661
662         if (button == mouse_button::button1) {
663                 if (text->bv_owner) {
664                         screen().hideCursor();
665                         screen().toggleSelection(text, bv_);
666                 }
667                 text->cursorHome(bv_);
668                 text->selection.cursor = text->cursor;
669                 text->cursorEnd(bv_);
670                 text->setSelection(bv_);
671                 if (text->bv_owner) {
672                         screen().toggleSelection(text, bv_, false);
673                 }
674                 /* This will fit the cursor on the screen
675                  * if necessary */
676                 update(text, BufferView::SELECT|BufferView::FITCUR);
677         }
678 }
679
680
681 void BufferView::Pimpl::selectionRequested()
682 {
683         static string sel;
684
685         if (!available())
686                 return;
687
688         LyXText * text = bv_->getLyXText();
689
690         if (text->selection.set() &&
691                 (!bv_->text->xsel_cache.set() ||
692                  text->selection.start != bv_->text->xsel_cache.start ||
693                  text->selection.end != bv_->text->xsel_cache.end))
694         {
695                 bv_->text->xsel_cache = text->selection;
696                 sel = text->selectionAsString(bv_->buffer(), false);
697         } else if (!text->selection.set()) {
698                 sel = string();
699                 bv_->text->xsel_cache.set(false);
700         }
701         if (!sel.empty()) {
702                 workarea().putClipboard(sel);
703         }
704 }
705
706
707 void BufferView::Pimpl::selectionLost()
708 {
709         if (available()) {
710                 hideCursor();
711                 toggleSelection();
712                 bv_->getLyXText()->clearSelection();
713                 showCursor();
714                 bv_->text->xsel_cache.set(false);
715         }
716 }
717
718
719 void BufferView::Pimpl::workAreaButtonRelease(int x, int y,
720                                               mouse_button::state button)
721 {
722         // do nothing if we used the mouse wheel
723         if (!buffer_ || button == mouse_button::button4 || button == mouse_button::button5)
724                 return;
725
726         // If we hit an inset, we have the inset coordinates in these
727         // and inset_hit points to the inset.  If we do not hit an
728         // inset, inset_hit is 0, and inset_x == x, inset_y == y.
729         Inset * inset_hit = checkInsetHit(bv_->text, x, y);
730
731         if (bv_->theLockingInset()) {
732                 // We are in inset locking mode.
733
734                 /* LyX does a kind of work-area grabbing for insets.
735                    Only a ButtonPress Event outside the inset will
736                    force a insetUnlock. */
737                 bv_->theLockingInset()->
738                         insetButtonRelease(bv_, x, y, button);
739                 return;
740         }
741
742         selection_possible = false;
743
744         if (button == mouse_button::button2)
745                 return;
746
747         // finish selection
748         if (button == mouse_button::button1) {
749                 workarea().haveSelection(bv_->getLyXText()->selection.set());
750         }
751
752         switchKeyMap();
753         owner_->view_state_changed();
754         owner_->updateMenubar();
755         owner_->updateToolbar();
756
757         // Did we hit an editable inset?
758         if (inset_hit) {
759                 selection_possible = false;
760
761                 // if we reach this point with a selection, it
762                 // must mean we are currently selecting.
763                 // But we don't want to open the inset
764                 // because that is annoying for the user.
765                 // So just pretend we didn't hit it.
766                 // this is OK because a "kosher" ButtonRelease
767                 // will follow a ButtonPress that clears
768                 // the selection.
769                 // Note this also fixes selection drawing
770                 // problems if we end up opening an inset
771                 if (bv_->getLyXText()->selection.set())
772                         return;
773
774                 // CHECK fix this proper in 0.13
775                 // well, maybe 13.0 !!!!!!!!!
776
777                 // Following a ref shouldn't issue
778                 // a push on the undo-stack
779                 // anylonger, now that we have
780                 // keybindings for following
781                 // references and returning from
782                 // references.  IMHO though, it
783                 // should be the inset's own business
784                 // to push or not push on the undo
785                 // stack. They don't *have* to
786                 // alter the document...
787                 // (Joacim)
788                 // ...or maybe the SetCursorParUndo()
789                 // below isn't necessary at all anylonger?
790                 if (inset_hit->lyxCode() == Inset::REF_CODE) {
791                         setCursorParUndo(bv_);
792                 }
793
794                 owner_->message(inset_hit->editMessage());
795
796                 if (isHighlyEditableInset(inset_hit)) {
797                         // Highly editable inset, like math
798                         UpdatableInset *inset = (UpdatableInset *)inset_hit;
799                         inset->insetButtonRelease(bv_, x, y, button);
800                 } else {
801                         inset_hit->insetButtonRelease(bv_, x, y, button);
802                         // IMO this is a grosshack! Inset's should be changed so that
803                         // they call the actions they have to do with the insetButtonRel.
804                         // function and not in the edit(). This should be changed
805                         // (Jug 20020329)
806                         inset_hit->edit(bv_, x, y, button);
807                 }
808                 return;
809         }
810
811         // Maybe we want to edit a bibitem ale970302
812         if (bv_->text->cursor.par()->bibkey) {
813                 bool const is_rtl = bv_->text->cursor.par()->isRightToLeftPar(buffer_->params);
814                 int const width = bibitemMaxWidth(bv_, buffer_->params.getLyXTextClass().defaultfont());
815                 if ((is_rtl && x > bv_->text->workWidth(bv_)-20-width) ||
816                     (!is_rtl && x < 20+width)) {
817                         bv_->text->cursor.par()->bibkey->edit(bv_, 0, 0, mouse_button::none);
818                 }
819         }
820
821         return;
822 }
823
824
825 Box BufferView::Pimpl::insetDimensions(LyXText const & text,
826                                        LyXCursor const & cursor) const
827 {
828         Paragraph /*const*/ & par = *cursor.par();
829         pos_type const pos = cursor.pos();
830
831         lyx::Assert(par.getInset(pos));
832
833         Inset const & inset(*par.getInset(pos));
834
835         LyXFont const & font = text.getFont(buffer_, &par, pos);
836
837         int const width = inset.width(bv_, font);
838         int const inset_x = font.isVisibleRightToLeft()
839                 ? (cursor.ix() - width) : cursor.ix();
840
841         return Box(
842                 inset_x + inset.scroll(),
843                 inset_x + width,
844                 cursor.iy() - inset.ascent(bv_, font),
845                 cursor.iy() + inset.descent(bv_, font));
846 }
847
848
849 Inset * BufferView::Pimpl::checkInset(LyXText const & text,
850                                       LyXCursor const & cursor,
851                                       int & x, int & y) const
852 {
853         pos_type const pos(cursor.pos());
854         Paragraph /*const*/ & par(*cursor.par());
855
856         if (pos >= par.size() || !par.isInset(pos)) {
857                 return 0;
858         }
859
860         Inset /*const*/ * inset = par.getInset(pos);
861
862         if (!isEditableInset(inset)) {
863                 return 0;
864         }
865
866         Box b(insetDimensions(text, cursor));
867
868         if (!b.contained(x, y)) {
869                 lyxerr[Debug::GUI] << "Missed inset at x,y " << x << "," << y
870                         << " box " << b << endl;
871                 return 0;
872         }
873
874         text.setCursor(bv_, &par, pos, true);
875
876         x -= b.x1;
877         // The origin of an inset is on the baseline
878         y -= text.cursor.iy();
879
880         return inset;
881 }
882
883
884 Inset * BufferView::Pimpl::checkInsetHit(LyXText * text, int & x, int & y)
885 {
886         int y_tmp = y + text->first_y;
887
888         LyXCursor cursor;
889         text->setCursorFromCoordinates(bv_, cursor, x, y_tmp);
890
891         Inset * inset(checkInset(*text, cursor, x, y_tmp));
892
893         if (inset) {
894                 y = y_tmp;
895                 return inset;
896         }
897
898         // look at previous position
899
900         if (cursor.pos() == 0) {
901                 return 0;
902         }
903
904         // move back one
905         text->setCursor(bv_, cursor, cursor.par(), cursor.pos() - 1, true);
906
907         inset = checkInset(*text, cursor, x, y_tmp);
908         if (inset) {
909                 y = y_tmp;
910         }
911         return inset;
912 }
913
914
915 void BufferView::Pimpl::workAreaResize()
916 {
917         static int work_area_width;
918         static int work_area_height;
919
920         bool const widthChange = workarea().workWidth() != work_area_width;
921         bool const heightChange = workarea().workHeight() != work_area_height;
922
923         // update from work area
924         work_area_width = workarea().workWidth();
925         work_area_height = workarea().workHeight();
926
927         if (buffer_ != 0) {
928                 if (widthChange) {
929                         // The visible LyXView need a resize
930                         resizeCurrentBuffer();
931
932                         // Remove all texts from the textcache
933                         // This is not _really_ what we want to do. What
934                         // we really want to do is to delete in textcache
935                         // that does not have a BufferView with matching
936                         // width, but as long as we have only one BufferView
937                         // deleting all gives the same result.
938                         if (lyxerr.debugging())
939                                 textcache.show(lyxerr, "Expose delete all");
940                         textcache.clear();
941                         // FIXME: this is aalready done in resizeCurrentBuffer() ??
942                         buffer_->resizeInsets(bv_);
943                 } else if (heightChange) {
944                         // fitCursor() ensures we don't jump back
945                         // to the start of the document on vertical
946                         // resize
947                         fitCursor();
948                 }
949         }
950
951         if (widthChange || heightChange) {
952                 repaint();
953         }
954
955         // always make sure that the scrollbar is sane.
956         updateScrollbar();
957         owner_->updateLayoutChoice();
958         return;
959 }
960
961
962 void BufferView::Pimpl::update()
963 {
964         if (!bv_->theLockingInset() || !bv_->theLockingInset()->nodraw()) {
965                 LyXText::text_status st = bv_->text->status();
966                 screen().update(bv_->text, bv_);
967                 bool fitc = false;
968                 while (bv_->text->status() == LyXText::CHANGED_IN_DRAW) {
969                         bv_->text->fullRebreak(bv_);
970                         st = LyXText::NEED_MORE_REFRESH;
971                         bv_->text->setCursor(bv_, bv_->text->cursor.par(),
972                                              bv_->text->cursor.pos());
973                         if (bv_->text->selection.set()) {
974                                 bv_->text->setCursor(bv_, bv_->text->selection.start,
975                                                      bv_->text->selection.start.par(),
976                                                      bv_->text->selection.start.pos());
977                                 bv_->text->setCursor(bv_, bv_->text->selection.end,
978                                                      bv_->text->selection.end.par(),
979                                                      bv_->text->selection.end.pos());
980                         }
981                         fitc = true;
982                         bv_->text->status(bv_, st);
983                         screen().update(bv_->text, bv_);
984                 }
985                 // do this here instead of in the screen::update because of
986                 // the above loop!
987                 bv_->text->status(bv_, LyXText::UNCHANGED);
988                 if (fitc)
989                         fitCursor();
990         }
991 }
992
993 // Values used when calling update:
994 // -3 - update
995 // -2 - update, move sel_cursor if selection, fitcursor
996 // -1 - update, move sel_cursor if selection, fitcursor, mark dirty
997 //  0 - update, move sel_cursor if selection, fitcursor
998 //  1 - update, move sel_cursor if selection, fitcursor, mark dirty
999 //  3 - update, move sel_cursor if selection
1000 //
1001 // update -
1002 // a simple redraw of the parts that need refresh
1003 //
1004 // move sel_cursor if selection -
1005 // the text's sel_cursor is moved if there is selection is progress
1006 //
1007 // fitcursor -
1008 // fitCursor() is called and the scrollbar updated
1009 //
1010 // mark dirty -
1011 // the buffer is marked dirty.
1012 //
1013 // enum {
1014 //       UPDATE = 0,
1015 //       SELECT = 1,
1016 //       FITCUR = 2,
1017 //       CHANGE = 4
1018 // };
1019 //
1020 // UPDATE_ONLY = UPDATE;
1021 // UPDATE_SELECT = UPDATE | SELECT;
1022 // UPDATE_SELECT_MOVE = UPDATE | SELECT | FITCUR;
1023 // UPDATE_SELECT_MOVE_AFTER_CHANGE = UPDATE | SELECT | FITCUR | CHANGE;
1024 //
1025 // update(-3) -> update(0)         -> update(0) -> update(UPDATE)
1026 // update(-2) -> update(1 + 2)     -> update(3) -> update(SELECT|FITCUR)
1027 // update(-1) -> update(1 + 2 + 4) -> update(7) -> update(SELECT|FITCUR|CHANGE)
1028 // update(1)  -> update(1 + 2 + 4) -> update(7) -> update(SELECT|FITCUR|CHANGE)
1029 // update(3)  -> update(1)         -> update(1) -> update(SELECT)
1030
1031 void BufferView::Pimpl::update(LyXText * text, BufferView::UpdateCodes f)
1032 {
1033         owner_->updateLayoutChoice();
1034
1035         if (!text->selection.set() && (f & SELECT)) {
1036                 text->selection.cursor = text->cursor;
1037         }
1038
1039         text->fullRebreak(bv_);
1040
1041         if (text->inset_owner) {
1042                 text->inset_owner->setUpdateStatus(bv_, InsetText::NONE);
1043                 updateInset(text->inset_owner, false);
1044         } else {
1045                 update();
1046         }
1047
1048         if ((f & FITCUR)) {
1049                 fitCursor();
1050         }
1051
1052         if ((f & CHANGE)) {
1053                 buffer_->markDirty();
1054         }
1055 }
1056
1057
1058 // Callback for cursor timer
1059 void BufferView::Pimpl::cursorToggle()
1060 {
1061         if (!buffer_) {
1062                 cursor_timeout.restart();
1063                 return;
1064         }
1065
1066         /* FIXME */
1067         extern void reapSpellchecker(void);
1068         reapSpellchecker();
1069
1070         if (!bv_->theLockingInset()) {
1071                 screen().cursorToggle(bv_);
1072         } else {
1073                 bv_->theLockingInset()->toggleInsetCursor(bv_);
1074         }
1075
1076         cursor_timeout.restart();
1077 }
1078
1079
1080 void BufferView::Pimpl::cursorPrevious(LyXText * text)
1081 {
1082         if (!text->cursor.row()->previous()) {
1083                 if (text->first_y > 0) {
1084                         int new_y = bv_->text->first_y - workarea().workHeight();
1085                         screen().draw(bv_->text, bv_, new_y < 0 ? 0 : new_y);
1086                         updateScrollbar();
1087                 }
1088                 return;
1089         }
1090
1091         int y = text->first_y;
1092         Row * cursorrow = text->cursor.row();
1093
1094         text->setCursorFromCoordinates(bv_, text->cursor.x_fix(), y);
1095         finishUndo();
1096
1097         int new_y;
1098         if (cursorrow == bv_->text->cursor.row()) {
1099                 // we have a row which is higher than the workarea so we leave the
1100                 // cursor on the start of the row and move only the draw up as soon
1101                 // as we move the cursor or do something while inside the row (it may
1102                 // span several workarea-heights) we'll move to the top again, but this
1103                 // is better than just jump down and only display part of the row.
1104                 new_y = bv_->text->first_y - workarea().workHeight();
1105         } else {
1106                 if (text->inset_owner) {
1107                         new_y = bv_->text->cursor.iy()
1108                                 + bv_->theLockingInset()->insetInInsetY() + y
1109                                 + text->cursor.row()->height()
1110                                 - workarea().workHeight() + 1;
1111                 } else {
1112                         new_y = text->cursor.y()
1113                                 - text->cursor.row()->baseline()
1114                                 + text->cursor.row()->height()
1115                                 - workarea().workHeight() + 1;
1116                 }
1117         }
1118         screen().draw(bv_->text, bv_,  new_y < 0 ? 0 : new_y);
1119         if (text->cursor.row()->previous()) {
1120                 LyXCursor cur;
1121                 text->setCursor(bv_, cur, text->cursor.row()->previous()->par(),
1122                                                 text->cursor.row()->previous()->pos(), false);
1123                 if (cur.y() > text->first_y) {
1124                         text->cursorUp(bv_, true);
1125                 }
1126         }
1127         updateScrollbar();
1128 }
1129
1130
1131 void BufferView::Pimpl::cursorNext(LyXText * text)
1132 {
1133         if (!text->cursor.row()->next()) {
1134                 int y = text->cursor.y() - text->cursor.row()->baseline() +
1135                         text->cursor.row()->height();
1136                 if (y > int(text->first_y + workarea().workHeight())) {
1137                         screen().draw(bv_->text, bv_,
1138                                                   bv_->text->first_y + workarea().workHeight());
1139                         updateScrollbar();
1140                 }
1141                 return;
1142         }
1143
1144         int y = text->first_y + workarea().workHeight();
1145         if (text->inset_owner && !text->first_y) {
1146                 y -= (bv_->text->cursor.iy()
1147                           - bv_->text->first_y
1148                           + bv_->theLockingInset()->insetInInsetY());
1149         }
1150
1151         text->getRowNearY(y);
1152
1153         Row * cursorrow = text->cursor.row();
1154         text->setCursorFromCoordinates(bv_, text->cursor.x_fix(), y); // + workarea().workHeight());
1155         finishUndo();
1156
1157         int new_y;
1158         if (cursorrow == bv_->text->cursor.row()) {
1159                 // we have a row which is higher than the workarea so we leave the
1160                 // cursor on the start of the row and move only the draw down as soon
1161                 // as we move the cursor or do something while inside the row (it may
1162                 // span several workarea-heights) we'll move to the top again, but this
1163                 // is better than just jump down and only display part of the row.
1164                 new_y = bv_->text->first_y + workarea().workHeight();
1165         } else {
1166                 if (text->inset_owner) {
1167                         new_y = bv_->text->cursor.iy()
1168                                 + bv_->theLockingInset()->insetInInsetY()
1169                                 + y - text->cursor.row()->baseline();
1170                 } else {
1171                         new_y =  text->cursor.y() - text->cursor.row()->baseline();
1172                 }
1173         }
1174         screen().draw(bv_->text, bv_, new_y);
1175         if (text->cursor.row()->next()) {
1176                 LyXCursor cur;
1177                 text->setCursor(bv_, cur, text->cursor.row()->next()->par(),
1178                                                 text->cursor.row()->next()->pos(), false);
1179                 if (cur.y() < int(text->first_y + workarea().workHeight())) {
1180                         text->cursorDown(bv_, true);
1181                 }
1182         }
1183         updateScrollbar();
1184 }
1185
1186
1187 bool BufferView::Pimpl::available() const
1188 {
1189         if (buffer_ && bv_->text)
1190                 return true;
1191         return false;
1192 }
1193
1194
1195 void BufferView::Pimpl::beforeChange(LyXText * text)
1196 {
1197         toggleSelection();
1198         text->clearSelection();
1199 }
1200
1201
1202 void BufferView::Pimpl::savePosition(unsigned int i)
1203 {
1204         if (i >= saved_positions_num)
1205                 return;
1206         saved_positions[i] = Position(buffer_->fileName(),
1207                                       bv_->text->cursor.par()->id(),
1208                                       bv_->text->cursor.pos());
1209         if (i > 0) {
1210                 ostringstream str;
1211                 str << _("Saved bookmark") << ' ' << i;
1212                 owner_->message(str.str().c_str());
1213         }
1214 }
1215
1216
1217 void BufferView::Pimpl::restorePosition(unsigned int i)
1218 {
1219         if (i >= saved_positions_num)
1220                 return;
1221
1222         string const fname = saved_positions[i].filename;
1223
1224         beforeChange(bv_->text);
1225
1226         if (fname != buffer_->fileName()) {
1227                 Buffer * b = bufferlist.exists(fname) ?
1228                         bufferlist.getBuffer(fname) :
1229                         bufferlist.loadLyXFile(fname); // don't ask, just load it
1230                 if (b != 0) buffer(b);
1231         }
1232
1233         Paragraph * par = buffer_->getParFromID(saved_positions[i].par_id);
1234         if (!par)
1235                 return;
1236
1237         bv_->text->setCursor(bv_, par,
1238                              min(par->size(), saved_positions[i].par_pos));
1239
1240         update(bv_->text, BufferView::SELECT | BufferView::FITCUR);
1241         if (i > 0) {
1242                 ostringstream str;
1243                 str << _("Moved to bookmark") << ' ' << i;
1244                 owner_->message(str.str().c_str());
1245         }
1246 }
1247
1248
1249 bool BufferView::Pimpl::isSavedPosition(unsigned int i)
1250 {
1251         if (i >= saved_positions_num)
1252                 return false;
1253
1254         return !saved_positions[i].filename.empty();
1255 }
1256
1257
1258 void BufferView::Pimpl::switchKeyMap()
1259 {
1260         if (!lyxrc.rtl_support)
1261                 return;
1262
1263         LyXText * text = bv_->getLyXText();
1264         if (text->real_current_font.isRightToLeft()
1265             && !(bv_->theLockingInset()
1266                  && bv_->theLockingInset()->lyxCode()== Inset::ERT_CODE))
1267         {
1268                 if (owner_->getIntl()->keymap == Intl::PRIMARY)
1269                         owner_->getIntl()->KeyMapSec();
1270         } else {
1271                 if (owner_->getIntl()->keymap == Intl::SECONDARY)
1272                         owner_->getIntl()->KeyMapPrim();
1273         }
1274 }
1275
1276
1277 void BufferView::Pimpl::insetUnlock()
1278 {
1279         if (bv_->theLockingInset()) {
1280                 bv_->theLockingInset()->insetUnlock(bv_);
1281                 bv_->theLockingInset(0);
1282                 finishUndo();
1283         }
1284 }
1285
1286
1287 void BufferView::Pimpl::showCursor()
1288 {
1289         if (bv_->theLockingInset())
1290                 bv_->theLockingInset()->showInsetCursor(bv_);
1291         else
1292                 screen().showCursor(bv_->text, bv_);
1293 }
1294
1295
1296 void BufferView::Pimpl::hideCursor()
1297 {
1298         if (!bv_->theLockingInset())
1299                 screen().hideCursor();
1300 }
1301
1302
1303 void BufferView::Pimpl::toggleSelection(bool b)
1304 {
1305         if (bv_->theLockingInset())
1306                 bv_->theLockingInset()->toggleSelection(bv_, b);
1307         screen().toggleSelection(bv_->text, bv_, b);
1308 }
1309
1310
1311 void BufferView::Pimpl::toggleToggle()
1312 {
1313         screen().toggleToggle(bv_->text, bv_);
1314 }
1315
1316
1317 void BufferView::Pimpl::center()
1318 {
1319         LyXText * t = bv_->text;
1320
1321         beforeChange(t);
1322         int const half_height = workarea().workHeight() / 2;
1323         int new_y = 0;
1324
1325         if (t->cursor.y() > half_height) {
1326                 new_y = t->cursor.y() - half_height;
1327         }
1328
1329         // FIXME: can we do this w/o calling screen directly ?
1330         // This updates first_y but means the fitCursor() call
1331         // from the update(FITCUR) doesn't realise that we might
1332         // have moved (e.g. from GOTOPARAGRAPH), so doesn't cause
1333         // the scrollbar to be updated as it should, so we have
1334         // to do it manually. Any operation that does a center()
1335         // and also might have moved first_y must make sure to call
1336         // updateScrollbar() currently. Never mind that this is a
1337         // pretty obfuscated way of updating t->first_y
1338         screen().draw(t, bv_, new_y);
1339
1340         update(t, BufferView::SELECT | BufferView::FITCUR);
1341 }
1342
1343
1344 void BufferView::Pimpl::pasteClipboard(bool asPara)
1345 {
1346         if (!buffer_)
1347                 return;
1348
1349         screen().hideCursor();
1350         beforeChange(bv_->text);
1351
1352         string const clip(workarea().getClipboard());
1353
1354         if (clip.empty())
1355                 return;
1356
1357         if (asPara) {
1358                 bv_->getLyXText()->insertStringAsParagraphs(bv_, clip);
1359         } else {
1360                 bv_->getLyXText()->insertStringAsLines(bv_, clip);
1361         }
1362         bv_->getLyXText()->clearSelection();
1363         update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
1364 }
1365
1366
1367 void BufferView::Pimpl::stuffClipboard(string const & stuff) const
1368 {
1369         workarea().putClipboard(stuff);
1370 }
1371
1372
1373 /*
1374  * Dispatch functions for actions which can be valid for BufferView->text
1375  * and/or InsetText->text!!!
1376  */
1377
1378
1379 inline
1380 void BufferView::Pimpl::moveCursorUpdate(bool selecting, bool fitcur)
1381 {
1382         LyXText * lt = bv_->getLyXText();
1383
1384         if (selecting || lt->selection.mark()) {
1385                 lt->setSelection(bv_);
1386                 if (lt->bv_owner)
1387                         toggleToggle();
1388                 else
1389                         updateInset(lt->inset_owner, false);
1390         }
1391         if (lt->bv_owner) {
1392                 if (fitcur)
1393                         update(lt, BufferView::SELECT|BufferView::FITCUR);
1394                 else
1395                         update(lt, BufferView::SELECT);
1396                 showCursor();
1397         } else if (bv_->text->status() != LyXText::UNCHANGED) {
1398                 bv_->theLockingInset()->hideInsetCursor(bv_);
1399                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
1400                 showCursor();
1401         }
1402
1403         if (!lt->selection.set())
1404                 workarea().haveSelection(false);
1405
1406         switchKeyMap();
1407 }
1408
1409
1410 Inset * BufferView::Pimpl::getInsetByCode(Inset::Code code)
1411 {
1412         LyXCursor cursor = bv_->getLyXText()->cursor;
1413         Buffer::inset_iterator it =
1414                 find_if(Buffer::inset_iterator(
1415                         cursor.par(), cursor.pos()),
1416                         buffer_->inset_iterator_end(),
1417                         lyx::compare_memfun(&Inset::lyxCode, code));
1418         return it != buffer_->inset_iterator_end() ? (*it) : 0;
1419 }
1420
1421
1422 void BufferView::Pimpl::MenuInsertLyXFile(string const & filen)
1423 {
1424         string filename = filen;
1425
1426         if (filename.empty()) {
1427                 // Launch a file browser
1428                 string initpath = lyxrc.document_path;
1429
1430                 if (available()) {
1431                         string const trypath = owner_->buffer()->filePath();
1432                         // If directory is writeable, use this as default.
1433                         if (IsDirWriteable(trypath))
1434                                 initpath = trypath;
1435                 }
1436
1437                 FileDialog fileDlg(bv_->owner(),
1438                                    _("Select LyX document to insert"),
1439                         LFUN_FILE_INSERT,
1440                         make_pair(string(_("Documents|#o#O")),
1441                                   string(lyxrc.document_path)),
1442                         make_pair(string(_("Examples|#E#e")),
1443                                   string(AddPath(system_lyxdir, "examples"))));
1444
1445                 FileDialog::Result result =
1446                         fileDlg.Select(initpath,
1447                                        _("*.lyx| LyX Documents (*.lyx)"));
1448
1449                 if (result.first == FileDialog::Later)
1450                         return;
1451
1452                 filename = result.second;
1453
1454                 // check selected filename
1455                 if (filename.empty()) {
1456                         owner_->message(_("Canceled."));
1457                         return;
1458                 }
1459         }
1460
1461         // get absolute path of file and add ".lyx" to the filename if
1462         // necessary
1463         filename = FileSearch(string(), filename, "lyx");
1464
1465         string const disp_fn(MakeDisplayPath(filename));
1466
1467         ostringstream s1;
1468         s1 << _("Inserting document") << ' '
1469            << disp_fn << " ...";
1470         owner_->message(s1.str().c_str());
1471         bool const res = bv_->insertLyXFile(filename);
1472         if (res) {
1473                 ostringstream str;
1474                 str << _("Document") << ' ' << disp_fn
1475                     << ' ' << _("inserted.");
1476                 owner_->message(str.str().c_str());
1477         } else {
1478                 ostringstream str;
1479                 str << _("Could not insert document") << ' '
1480                     << disp_fn;
1481                 owner_->message(str.str().c_str());
1482         }
1483 }
1484
1485
1486 bool BufferView::Pimpl::dispatch(kb_action action, string const & argument)
1487 {
1488         lyxerr[Debug::ACTION] << "BufferView::Pimpl::Dispatch: action["
1489                               << action <<"] arg[" << argument << "]" << endl;
1490
1491         LyXTextClass const & tclass = buffer_->params.getLyXTextClass();
1492
1493         switch (action) {
1494                 // --- Misc -------------------------------------------
1495         case LFUN_APPENDIX:
1496         {
1497                 if (available()) {
1498                         LyXText * lt = bv_->getLyXText();
1499                         lt->toggleAppendix(bv_);
1500                         update(lt,
1501                                BufferView::SELECT
1502                                | BufferView::FITCUR
1503                                | BufferView::CHANGE);
1504                 }
1505         }
1506         break;
1507
1508         case LFUN_TOC_INSERT:
1509         {
1510                 InsetCommandParams p;
1511                 p.setCmdName("tableofcontents");
1512                 Inset * inset = new InsetTOC(p);
1513                 if (!insertInset(inset, tclass.defaultLayoutName()))
1514                         delete inset;
1515                 break;
1516         }
1517
1518         case LFUN_SCROLL_INSET:
1519                 // this is not handled here as this funktion is only aktive
1520                 // if we have a locking_inset and that one is (or contains)
1521                 // a tabular-inset
1522                 break;
1523
1524         case LFUN_INSET_GRAPHICS:
1525         {
1526                 Inset * new_inset = new InsetGraphics;
1527                 if (!insertInset(new_inset)) {
1528                         delete new_inset;
1529                 } else {
1530                         // this is need because you don't use a inset->Edit()
1531                         updateInset(new_inset, true);
1532                         new_inset->edit(bv_);
1533                 }
1534                 break;
1535         }
1536
1537         case LFUN_PASTE:
1538                 bv_->paste();
1539                 switchKeyMap();
1540                 break;
1541
1542         case LFUN_PASTESELECTION:
1543         {
1544                 bool asPara = false;
1545                 if (argument == "paragraph")
1546                         asPara = true;
1547                 pasteClipboard(asPara);
1548         }
1549         break;
1550
1551         case LFUN_CUT:
1552                 bv_->cut();
1553                 break;
1554
1555         case LFUN_COPY:
1556                 bv_->copy();
1557                 break;
1558
1559         case LFUN_LAYOUT_COPY:
1560                 bv_->copyEnvironment();
1561                 break;
1562
1563         case LFUN_LAYOUT_PASTE:
1564                 bv_->pasteEnvironment();
1565                 switchKeyMap();
1566                 break;
1567
1568         case LFUN_GOTOERROR:
1569                 gotoInset(Inset::ERROR_CODE, false);
1570                 break;
1571
1572         case LFUN_GOTONOTE:
1573                 gotoInset(Inset::IGNORE_CODE, false);
1574                 break;
1575
1576         case LFUN_REFERENCE_GOTO:
1577         {
1578                 vector<Inset::Code> tmp;
1579                 tmp.push_back(Inset::LABEL_CODE);
1580                 tmp.push_back(Inset::REF_CODE);
1581                 gotoInset(tmp, true);
1582                 break;
1583         }
1584
1585         case LFUN_HYPHENATION:
1586                 specialChar(InsetSpecialChar::HYPHENATION);
1587                 break;
1588
1589         case LFUN_LIGATURE_BREAK:
1590                 specialChar(InsetSpecialChar::LIGATURE_BREAK);
1591                 break;
1592
1593         case LFUN_LDOTS:
1594                 specialChar(InsetSpecialChar::LDOTS);
1595                 break;
1596
1597         case LFUN_END_OF_SENTENCE:
1598                 specialChar(InsetSpecialChar::END_OF_SENTENCE);
1599                 break;
1600
1601         case LFUN_MENU_SEPARATOR:
1602                 specialChar(InsetSpecialChar::MENU_SEPARATOR);
1603                 break;
1604
1605         case LFUN_HFILL:
1606                 hfill();
1607                 break;
1608
1609         case LFUN_DEPTH_MIN:
1610                 changeDepth(bv_, bv_->getLyXText(), -1);
1611                 break;
1612
1613         case LFUN_DEPTH_PLUS:
1614                 changeDepth(bv_, bv_->getLyXText(), 1);
1615                 break;
1616
1617         case LFUN_FREE:
1618                 owner_->getDialogs()->setUserFreeFont();
1619                 break;
1620
1621         case LFUN_FILE_INSERT:
1622                 MenuInsertLyXFile(argument);
1623                 break;
1624
1625         case LFUN_FILE_INSERT_ASCII_PARA:
1626                 InsertAsciiFile(bv_, argument, true);
1627                 break;
1628
1629         case LFUN_FILE_INSERT_ASCII:
1630                 InsertAsciiFile(bv_, argument, false);
1631                 break;
1632
1633         case LFUN_LAYOUT:
1634         {
1635                 lyxerr[Debug::INFO] << "LFUN_LAYOUT: (arg) "
1636                                     << argument << endl;
1637
1638                 // This is not the good solution to the empty argument
1639                 // problem, but it will hopefully suffice for 1.2.0.
1640                 // The correct solution would be to augument the
1641                 // function list/array with information about what
1642                 // functions needs arguments and their type.
1643                 if (argument.empty()) {
1644                         owner_->getLyXFunc()->setErrorMessage(
1645                                 _("LyX function 'layout' needs an argument."));
1646                         break;
1647                 }
1648
1649                 // Derive layout number from given argument (string)
1650                 // and current buffer's textclass (number). */
1651                 bool hasLayout = tclass.hasLayout(argument);
1652                 string layout = argument;
1653
1654                 // If the entry is obsolete, use the new one instead.
1655                 if (hasLayout) {
1656                         string const & obs = tclass[layout]->obsoleted_by();
1657                         if (!obs.empty())
1658                                 layout = obs;
1659                 }
1660
1661                 if (!hasLayout) {
1662                         owner_->getLyXFunc()->setErrorMessage(
1663                                 string(N_("Layout ")) + argument +
1664                                 N_(" not known"));
1665                         break;
1666                 }
1667
1668                 bool change_layout = (current_layout != layout);
1669                 LyXText * lt = bv_->getLyXText();
1670                 if (!change_layout && lt->selection.set() &&
1671                         lt->selection.start.par() != lt->selection.end.par())
1672                 {
1673                         Paragraph * spar = lt->selection.start.par();
1674                         Paragraph * epar = lt->selection.end.par()->next();
1675                         while(spar != epar) {
1676                                 if (spar->layout()->name() != current_layout) {
1677                                         change_layout = true;
1678                                         break;
1679                                 }
1680                         }
1681                 }
1682                 if (change_layout) {
1683                         hideCursor();
1684                         current_layout = layout;
1685                         update(lt,
1686                                BufferView::SELECT
1687                                | BufferView::FITCUR);
1688                         lt->setLayout(bv_, layout);
1689                         owner_->setLayout(layout);
1690                         update(lt,
1691                                BufferView::SELECT
1692                                | BufferView::FITCUR
1693                                | BufferView::CHANGE);
1694                         switchKeyMap();
1695                 }
1696         }
1697         break;
1698
1699         case LFUN_LANGUAGE:
1700                 lang(bv_, argument);
1701                 switchKeyMap();
1702                 owner_->view_state_changed();
1703                 break;
1704
1705         case LFUN_EMPH:
1706                 emph(bv_);
1707                 owner_->view_state_changed();
1708                 break;
1709
1710         case LFUN_BOLD:
1711                 bold(bv_);
1712                 owner_->view_state_changed();
1713                 break;
1714
1715         case LFUN_NOUN:
1716                 noun(bv_);
1717                 owner_->view_state_changed();
1718                 break;
1719
1720         case LFUN_CODE:
1721                 code(bv_);
1722                 owner_->view_state_changed();
1723                 break;
1724
1725         case LFUN_SANS:
1726                 sans(bv_);
1727                 owner_->view_state_changed();
1728                 break;
1729
1730         case LFUN_ROMAN:
1731                 roman(bv_);
1732                 owner_->view_state_changed();
1733                 break;
1734
1735         case LFUN_DEFAULT:
1736                 styleReset(bv_);
1737                 owner_->view_state_changed();
1738                 break;
1739
1740         case LFUN_UNDERLINE:
1741                 underline(bv_);
1742                 owner_->view_state_changed();
1743                 break;
1744
1745         case LFUN_FONT_SIZE:
1746                 fontSize(bv_, argument);
1747                 owner_->view_state_changed();
1748                 break;
1749
1750         case LFUN_FONT_STATE:
1751                 owner_->getLyXFunc()->setMessage(currentState(bv_));
1752                 break;
1753
1754         case LFUN_UPCASE_WORD:
1755         {
1756                 LyXText * lt = bv_->getLyXText();
1757
1758                 update(lt,
1759                        BufferView::SELECT
1760                        | BufferView::FITCUR);
1761                 lt->changeCase(bv_, LyXText::text_uppercase);
1762                 if (lt->inset_owner)
1763                         updateInset(lt->inset_owner, true);
1764                 update(lt,
1765                        BufferView::SELECT
1766                        | BufferView::FITCUR
1767                        | BufferView::CHANGE);
1768         }
1769         break;
1770
1771         case LFUN_LOWCASE_WORD:
1772         {
1773                 LyXText * lt = bv_->getLyXText();
1774
1775                 update(lt, BufferView::SELECT|BufferView::FITCUR);
1776                 lt->changeCase(bv_, LyXText::text_lowercase);
1777                 if (lt->inset_owner)
1778                         updateInset(lt->inset_owner, true);
1779                 update(lt,
1780                        BufferView::SELECT
1781                        | BufferView::FITCUR
1782                        | BufferView::CHANGE);
1783         }
1784         break;
1785
1786         case LFUN_CAPITALIZE_WORD:
1787         {
1788                 LyXText * lt = bv_->getLyXText();
1789
1790                 update(lt, BufferView::SELECT|BufferView::FITCUR);
1791                 lt->changeCase(bv_, LyXText::text_capitalization);
1792                 if (lt->inset_owner)
1793                         updateInset(lt->inset_owner, true);
1794                 update(lt,
1795                        BufferView::SELECT
1796                        | BufferView::FITCUR
1797                        | BufferView::CHANGE);
1798         }
1799         break;
1800
1801         case LFUN_TRANSPOSE_CHARS:
1802         {
1803                 LyXText * lt = bv_->getLyXText();
1804
1805                 update(lt, BufferView::SELECT|BufferView::FITCUR);
1806                 lt->transposeChars(*bv_);
1807                 if (lt->inset_owner)
1808                         updateInset(lt->inset_owner, true);
1809                 update(lt,
1810                        BufferView::SELECT
1811                        | BufferView::FITCUR
1812                        | BufferView::CHANGE);
1813         }
1814         break;
1815
1816
1817         case LFUN_INSERT_LABEL:
1818                 MenuInsertLabel(bv_, argument);
1819                 break;
1820
1821         case LFUN_REF_INSERT:
1822                 if (argument.empty()) {
1823                         InsetCommandParams p("ref");
1824                         owner_->getDialogs()->createRef(p.getAsString());
1825                 } else {
1826                         InsetCommandParams p;
1827                         p.setFromString(argument);
1828
1829                         InsetRef * inset = new InsetRef(p, *buffer_);
1830                         if (!insertInset(inset))
1831                                 delete inset;
1832                         else
1833                                 updateInset(inset, true);
1834                 }
1835                 break;
1836
1837         case LFUN_BOOKMARK_SAVE:
1838                 savePosition(strToUnsignedInt(argument));
1839                 break;
1840
1841         case LFUN_BOOKMARK_GOTO:
1842                 restorePosition(strToUnsignedInt(argument));
1843                 break;
1844
1845         case LFUN_REF_GOTO:
1846         {
1847                 string label(argument);
1848                 if (label.empty()) {
1849                         InsetRef * inset =
1850                                 static_cast<InsetRef*>(getInsetByCode(Inset::REF_CODE));
1851                         if (inset) {
1852                                 label = inset->getContents();
1853                                 savePosition(0);
1854                         }
1855                 }
1856
1857                 if (!label.empty()) {
1858                         //bv_->savePosition(0);
1859                         if (!bv_->gotoLabel(label))
1860                                 Alert::alert(_("Error"),
1861                                            _("Couldn't find this label"),
1862                                            _("in current document."));
1863                 }
1864         }
1865         break;
1866
1867                 // --- Cursor Movements -----------------------------
1868         case LFUN_RIGHT:
1869         {
1870                 LyXText * lt = bv_->getLyXText();
1871
1872                 bool is_rtl = lt->cursor.par()->isRightToLeftPar(buffer_->params);
1873                 if (!lt->selection.mark())
1874                         beforeChange(lt);
1875                 update(lt, BufferView::SELECT|BufferView::FITCUR);
1876                 if (is_rtl)
1877                         lt->cursorLeft(bv_, false);
1878                 if (lt->cursor.pos() < lt->cursor.par()->size()
1879                     && lt->cursor.par()->isInset(lt->cursor.pos())
1880                     && isHighlyEditableInset(lt->cursor.par()->getInset(lt->cursor.pos()))) {
1881                         Inset * tmpinset = lt->cursor.par()->getInset(lt->cursor.pos());
1882                         owner_->getLyXFunc()->setMessage(tmpinset->editMessage());
1883                         if (is_rtl)
1884                                 tmpinset->edit(bv_, false);
1885                         else
1886                                 tmpinset->edit(bv_);
1887                         break;
1888                 }
1889                 if (!is_rtl)
1890                         lt->cursorRight(bv_, false);
1891                 finishUndo();
1892                 moveCursorUpdate(false);
1893                 owner_->view_state_changed();
1894         }
1895         break;
1896
1897         case LFUN_LEFT:
1898         {
1899                 // This is soooo ugly. Isn`t it possible to make
1900                 // it simpler? (Lgb)
1901                 LyXText * lt = bv_->getLyXText();
1902                 bool const is_rtl = lt->cursor.par()->isRightToLeftPar(buffer_->params);
1903                 if (!lt->selection.mark())
1904                         beforeChange(lt);
1905                 update(lt, BufferView::SELECT|BufferView::FITCUR);
1906                 LyXCursor const cur = lt->cursor;
1907                 if (!is_rtl)
1908                         lt->cursorLeft(bv_, false);
1909                 if ((is_rtl || cur != lt->cursor) && // only if really moved!
1910                     lt->cursor.pos() < lt->cursor.par()->size() &&
1911                     lt->cursor.par()->isInset(lt->cursor.pos()) &&
1912                     isHighlyEditableInset(lt->cursor.par()->getInset(lt->cursor.pos()))) {
1913                         Inset * tmpinset = lt->cursor.par()->getInset(lt->cursor.pos());
1914                         owner_->getLyXFunc()->setMessage(tmpinset->editMessage());
1915                         if (is_rtl)
1916                                 tmpinset->edit(bv_);
1917                         else
1918                                 tmpinset->edit(bv_, false);
1919                         break;
1920                 }
1921                 if  (is_rtl)
1922                         lt->cursorRight(bv_, false);
1923
1924                 finishUndo();
1925                 moveCursorUpdate(false);
1926                 owner_->view_state_changed();
1927         }
1928         break;
1929
1930         case LFUN_UP:
1931         {
1932                 LyXText * lt = bv_->getLyXText();
1933
1934                 if (!lt->selection.mark())
1935                         beforeChange(lt);
1936                 update(lt, BufferView::UPDATE);
1937                 lt->cursorUp(bv_);
1938                 finishUndo();
1939                 moveCursorUpdate(false);
1940                 owner_->view_state_changed();
1941         }
1942         break;
1943
1944         case LFUN_DOWN:
1945         {
1946                 LyXText * lt = bv_->getLyXText();
1947
1948                 if (!lt->selection.mark())
1949                         beforeChange(lt);
1950                 update(lt, BufferView::UPDATE);
1951                 lt->cursorDown(bv_);
1952                 finishUndo();
1953                 moveCursorUpdate(false);
1954                 owner_->view_state_changed();
1955         }
1956         break;
1957
1958         case LFUN_UP_PARAGRAPH:
1959         {
1960                 LyXText * lt = bv_->getLyXText();
1961
1962                 if (!lt->selection.mark())
1963                         beforeChange(lt);
1964                 update(lt, BufferView::UPDATE);
1965                 lt->cursorUpParagraph(bv_);
1966                 finishUndo();
1967                 moveCursorUpdate(false);
1968                 owner_->view_state_changed();
1969         }
1970         break;
1971
1972         case LFUN_DOWN_PARAGRAPH:
1973         {
1974                 LyXText * lt = bv_->getLyXText();
1975
1976                 if (!lt->selection.mark())
1977                         beforeChange(lt);
1978                 update(lt, BufferView::UPDATE);
1979                 lt->cursorDownParagraph(bv_);
1980                 finishUndo();
1981                 moveCursorUpdate(false);
1982                 owner_->view_state_changed();
1983         }
1984         break;
1985
1986         case LFUN_PRIOR:
1987         {
1988                 LyXText * lt = bv_->getLyXText();
1989
1990                 if (!lt->selection.mark())
1991                         beforeChange(lt);
1992                 update(lt, BufferView::UPDATE);
1993                 cursorPrevious(lt);
1994                 finishUndo();
1995                 moveCursorUpdate(false, false);
1996                 owner_->view_state_changed();
1997         }
1998         break;
1999
2000         case LFUN_NEXT:
2001         {
2002                 LyXText * lt = bv_->getLyXText();
2003
2004                 if (!lt->selection.mark())
2005                         beforeChange(lt);
2006                 update(lt, BufferView::UPDATE);
2007                 cursorNext(lt);
2008                 finishUndo();
2009                 moveCursorUpdate(false, false);
2010                 owner_->view_state_changed();
2011         }
2012         break;
2013
2014         case LFUN_HOME:
2015         {
2016                 LyXText * lt = bv_->getLyXText();
2017
2018                 if (!lt->selection.mark())
2019                         beforeChange(lt);
2020                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2021                 lt->cursorHome(bv_);
2022                 finishUndo();
2023                 moveCursorUpdate(false);
2024                 owner_->view_state_changed();
2025         }
2026         break;
2027
2028         case LFUN_END:
2029         {
2030                 LyXText * lt = bv_->getLyXText();
2031
2032                 if (!lt->selection.mark())
2033                         beforeChange(lt);
2034                 update(lt,
2035                        BufferView::SELECT|BufferView::FITCUR);
2036                 lt->cursorEnd(bv_);
2037                 finishUndo();
2038                 moveCursorUpdate(false);
2039                 owner_->view_state_changed();
2040         }
2041         break;
2042
2043         case LFUN_SHIFT_TAB:
2044         case LFUN_TAB:
2045         {
2046                 LyXText * lt = bv_->getLyXText();
2047
2048                 if (!lt->selection.mark())
2049                         beforeChange(lt);
2050                 update(lt,
2051                        BufferView::SELECT|BufferView::FITCUR);
2052                 lt->cursorTab(bv_);
2053                 finishUndo();
2054                 moveCursorUpdate(false);
2055                 owner_->view_state_changed();
2056         }
2057         break;
2058
2059         case LFUN_WORDRIGHT:
2060         {
2061                 LyXText * lt = bv_->getLyXText();
2062
2063                 if (!lt->selection.mark())
2064                         beforeChange(lt);
2065                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2066                 if (lt->cursor.par()->isRightToLeftPar(buffer_->params))
2067                         lt->cursorLeftOneWord(bv_);
2068                 else
2069                         lt->cursorRightOneWord(bv_);
2070                 finishUndo();
2071                 moveCursorUpdate(false);
2072                 owner_->view_state_changed();
2073         }
2074         break;
2075
2076         case LFUN_WORDLEFT:
2077         {
2078                 LyXText * lt = bv_->getLyXText();
2079
2080                 if (!lt->selection.mark())
2081                         beforeChange(lt);
2082                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2083                 if (lt->cursor.par()->isRightToLeftPar(buffer_->params))
2084                         lt->cursorRightOneWord(bv_);
2085                 else
2086                         lt->cursorLeftOneWord(bv_);
2087                 finishUndo();
2088                 moveCursorUpdate(false);
2089                 owner_->view_state_changed();
2090         }
2091         break;
2092
2093         case LFUN_BEGINNINGBUF:
2094         {
2095                 LyXText * lt = bv_->getLyXText();
2096
2097                 if (!lt->selection.mark())
2098                         beforeChange(lt);
2099                 update(lt,
2100                        BufferView::SELECT|BufferView::FITCUR);
2101                 lt->cursorTop(bv_);
2102                 finishUndo();
2103                 moveCursorUpdate(false);
2104                 owner_->view_state_changed();
2105         }
2106         break;
2107
2108         case LFUN_ENDBUF:
2109         {
2110                 LyXText * lt = bv_->getLyXText();
2111
2112                 if (!lt->selection.mark())
2113                         beforeChange(lt);
2114                 update(lt,
2115                        BufferView::SELECT|BufferView::FITCUR);
2116                 lt->cursorBottom(bv_);
2117                 finishUndo();
2118                 moveCursorUpdate(false);
2119                 owner_->view_state_changed();
2120         }
2121         break;
2122
2123                 /* cursor selection ---------------------------- */
2124         case LFUN_RIGHTSEL:
2125         {
2126                 LyXText * lt = bv_->getLyXText();
2127
2128                 update(lt,
2129                        BufferView::SELECT|BufferView::FITCUR);
2130                 if (lt->cursor.par()->isRightToLeftPar(buffer_->params))
2131                         lt->cursorLeft(bv_);
2132                 else
2133                         lt->cursorRight(bv_);
2134                 finishUndo();
2135                 moveCursorUpdate(true);
2136                 owner_->view_state_changed();
2137         }
2138         break;
2139
2140         case LFUN_LEFTSEL:
2141         {
2142                 LyXText * lt = bv_->getLyXText();
2143
2144                 update(lt,
2145                        BufferView::SELECT|BufferView::FITCUR);
2146                 if (lt->cursor.par()->isRightToLeftPar(buffer_->params))
2147                         lt->cursorRight(bv_);
2148                 else
2149                         lt->cursorLeft(bv_);
2150                 finishUndo();
2151                 moveCursorUpdate(true);
2152                 owner_->view_state_changed();
2153         }
2154         break;
2155
2156         case LFUN_UPSEL:
2157         {
2158                 LyXText * lt = bv_->getLyXText();
2159
2160                 update(lt,
2161                        BufferView::SELECT|BufferView::FITCUR);
2162                 lt->cursorUp(bv_, true);
2163                 finishUndo();
2164                 moveCursorUpdate(true);
2165                 owner_->view_state_changed();
2166         }
2167         break;
2168
2169         case LFUN_DOWNSEL:
2170         {
2171                 LyXText * lt = bv_->getLyXText();
2172
2173                 update(lt,
2174                        BufferView::SELECT|BufferView::FITCUR);
2175                 lt->cursorDown(bv_, true);
2176                 finishUndo();
2177                 moveCursorUpdate(true);
2178                 owner_->view_state_changed();
2179         }
2180         break;
2181
2182         case LFUN_UP_PARAGRAPHSEL:
2183         {
2184                 LyXText * lt = bv_->getLyXText();
2185
2186                 update(lt,
2187                        BufferView::SELECT|BufferView::FITCUR);
2188                 lt->cursorUpParagraph(bv_);
2189                 finishUndo();
2190                 moveCursorUpdate(true);
2191                 owner_->view_state_changed();
2192         }
2193         break;
2194
2195         case LFUN_DOWN_PARAGRAPHSEL:
2196         {
2197                 LyXText * lt = bv_->getLyXText();
2198
2199                 update(lt,
2200                        BufferView::SELECT|BufferView::FITCUR);
2201                 lt->cursorDownParagraph(bv_);
2202                 finishUndo();
2203                 moveCursorUpdate(true);
2204                 owner_->view_state_changed();
2205         }
2206         break;
2207
2208         case LFUN_PRIORSEL:
2209         {
2210                 LyXText * lt = bv_->getLyXText();
2211
2212                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2213                 cursorPrevious(lt);
2214                 finishUndo();
2215                 moveCursorUpdate(true);
2216                 owner_->view_state_changed();
2217         }
2218         break;
2219
2220         case LFUN_NEXTSEL:
2221         {
2222                 LyXText * lt = bv_->getLyXText();
2223
2224                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2225                 cursorNext(lt);
2226                 finishUndo();
2227                 moveCursorUpdate(true);
2228                 owner_->view_state_changed();
2229         }
2230         break;
2231
2232         case LFUN_HOMESEL:
2233         {
2234                 LyXText * lt = bv_->getLyXText();
2235
2236                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2237                 lt->cursorHome(bv_);
2238                 finishUndo();
2239                 moveCursorUpdate(true);
2240                 owner_->view_state_changed();
2241         }
2242         break;
2243
2244         case LFUN_ENDSEL:
2245         {
2246                 LyXText * lt = bv_->getLyXText();
2247
2248                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2249                 lt->cursorEnd(bv_);
2250                 finishUndo();
2251                 moveCursorUpdate(true);
2252                 owner_->view_state_changed();
2253         }
2254         break;
2255
2256         case LFUN_WORDRIGHTSEL:
2257         {
2258                 LyXText * lt = bv_->getLyXText();
2259
2260                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2261                 if (lt->cursor.par()->isRightToLeftPar(buffer_->params))
2262                         lt->cursorLeftOneWord(bv_);
2263                 else
2264                         lt->cursorRightOneWord(bv_);
2265                 finishUndo();
2266                 moveCursorUpdate(true);
2267                 owner_->view_state_changed();
2268         }
2269         break;
2270
2271         case LFUN_WORDLEFTSEL:
2272         {
2273                 LyXText * lt = bv_->getLyXText();
2274
2275                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2276                 if (lt->cursor.par()->isRightToLeftPar(buffer_->params))
2277                         lt->cursorRightOneWord(bv_);
2278                 else
2279                         lt->cursorLeftOneWord(bv_);
2280                 finishUndo();
2281                 moveCursorUpdate(true);
2282                 owner_->view_state_changed();
2283         }
2284         break;
2285
2286         case LFUN_BEGINNINGBUFSEL:
2287         {
2288                 LyXText * lt = bv_->getLyXText();
2289
2290                 if (lt->inset_owner)
2291                         break;
2292                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2293                 lt->cursorTop(bv_);
2294                 finishUndo();
2295                 moveCursorUpdate(true);
2296                 owner_->view_state_changed();
2297         }
2298         break;
2299
2300         case LFUN_ENDBUFSEL:
2301         {
2302                 LyXText * lt = bv_->getLyXText();
2303
2304                 if (lt->inset_owner)
2305                         break;
2306                 update(lt,
2307                        BufferView::SELECT|BufferView::FITCUR);
2308                 lt->cursorBottom(bv_);
2309                 finishUndo();
2310                 moveCursorUpdate(true);
2311                 owner_->view_state_changed();
2312         }
2313         break;
2314
2315                 // --- text changing commands ------------------------
2316         case LFUN_BREAKLINE:
2317         {
2318                 LyXText * lt = bv_->getLyXText();
2319
2320                 beforeChange(lt);
2321                 lt->insertChar(bv_, Paragraph::META_NEWLINE);
2322                 update(lt,
2323                        BufferView::SELECT
2324                        | BufferView::FITCUR
2325                        | BufferView::CHANGE);
2326                 lt->setCursor(bv_, lt->cursor.par(), lt->cursor.pos());
2327                 moveCursorUpdate(false);
2328         }
2329         break;
2330
2331         case LFUN_PROTECTEDSPACE:
2332         {
2333                 LyXText * lt = bv_->getLyXText();
2334                 LyXLayout_ptr const & style = lt->cursor.par()->layout();
2335
2336                 if (style->free_spacing) {
2337                         lt->insertChar(bv_, ' ');
2338                         update(lt,
2339                                BufferView::SELECT
2340                                | BufferView::FITCUR
2341                                | BufferView::CHANGE);
2342                 } else {
2343                         specialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2344                 }
2345                 moveCursorUpdate(false);
2346         }
2347         break;
2348
2349         case LFUN_SETMARK:
2350         {
2351                 LyXText * lt = bv_->getLyXText();
2352
2353                 if (lt->selection.mark()) {
2354                         beforeChange(lt);
2355                         update(lt,
2356                                BufferView::SELECT
2357                                | BufferView::FITCUR);
2358                         owner_->getLyXFunc()->setMessage(N_("Mark removed"));
2359                 } else {
2360                         beforeChange(lt);
2361                         lt->selection.mark(true);
2362                         update(lt,
2363                                BufferView::SELECT
2364                                | BufferView::FITCUR);
2365                         owner_->getLyXFunc()->setMessage(N_("Mark set"));
2366                 }
2367                 lt->selection.cursor = lt->cursor;
2368         }
2369         break;
2370
2371         case LFUN_DELETE:
2372         {
2373                 LyXText * lt = bv_->getLyXText();
2374
2375                 if (!lt->selection.set()) {
2376                         lt->Delete(bv_);
2377                         lt->selection.cursor = lt->cursor;
2378                         update(lt,
2379                                BufferView::SELECT
2380                                | BufferView::FITCUR
2381                                | BufferView::CHANGE);
2382                         // It is possible to make it a lot faster still
2383                         // just comment out the line below...
2384                         showCursor();
2385                 } else {
2386                         bv_->cut(false);
2387                 }
2388                 moveCursorUpdate(false);
2389                 owner_->view_state_changed();
2390                 switchKeyMap();
2391         }
2392         break;
2393
2394         case LFUN_DELETE_SKIP:
2395         {
2396                 LyXText * lt = bv_->getLyXText();
2397
2398                 // Reverse the effect of LFUN_BREAKPARAGRAPH_SKIP.
2399
2400                 LyXCursor cursor = lt->cursor;
2401
2402                 if (!lt->selection.set()) {
2403                         if (cursor.pos() == cursor.par()->size()) {
2404                                 lt->cursorRight(bv_);
2405                                 cursor = lt->cursor;
2406                                 if (cursor.pos() == 0
2407                                     && !(cursor.par()->params().spaceTop()
2408                                          == VSpace (VSpace::NONE))) {
2409                                         lt->setParagraph
2410                                                 (bv_,
2411                                                  cursor.par()->params().lineTop(),
2412                                                  cursor.par()->params().lineBottom(),
2413                                                  cursor.par()->params().pagebreakTop(),
2414                                                  cursor.par()->params().pagebreakBottom(),
2415                                                  VSpace(VSpace::NONE),
2416                                                  cursor.par()->params().spaceBottom(),
2417                                                  cursor.par()->params().spacing(),
2418                                                  cursor.par()->params().align(),
2419                                                  cursor.par()->params().labelWidthString(), 0);
2420                                         lt->cursorLeft(bv_);
2421                                         update(lt,
2422                                                BufferView::SELECT
2423                                                | BufferView::FITCUR
2424                                                | BufferView::CHANGE);
2425                                 } else {
2426                                         lt->cursorLeft(bv_);
2427                                         lt->Delete(bv_);
2428                                         lt->selection.cursor = lt->cursor;
2429                                         update(lt,
2430                                                BufferView::SELECT
2431                                                | BufferView::FITCUR
2432                                                | BufferView::CHANGE);
2433                                 }
2434                         } else {
2435                                 lt->Delete(bv_);
2436                                 lt->selection.cursor = lt->cursor;
2437                                 update(lt,
2438                                        BufferView::SELECT
2439                                        | BufferView::FITCUR
2440                                        | BufferView::CHANGE);
2441                         }
2442                 } else {
2443                         bv_->cut(false);
2444                 }
2445         }
2446         break;
2447
2448         /* -------> Delete word forward. */
2449         case LFUN_DELETE_WORD_FORWARD:
2450                 update(bv_->getLyXText(), BufferView::SELECT|BufferView::FITCUR);
2451                 bv_->getLyXText()->deleteWordForward(bv_);
2452                 update(bv_->getLyXText(), BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
2453                 moveCursorUpdate(false);
2454                 owner_->view_state_changed();
2455                 break;
2456
2457                 /* -------> Delete word backward. */
2458         case LFUN_DELETE_WORD_BACKWARD:
2459         {
2460                 LyXText * lt = bv_->getLyXText();
2461
2462                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2463                 lt->deleteWordBackward(bv_);
2464                 update(lt,
2465                        BufferView::SELECT
2466                        | BufferView::FITCUR
2467                        | BufferView::CHANGE);
2468                 moveCursorUpdate(false);
2469                 owner_->view_state_changed();
2470         }
2471         break;
2472
2473                 /* -------> Kill to end of line. */
2474         case LFUN_DELETE_LINE_FORWARD:
2475         {
2476                 LyXText * lt = bv_->getLyXText();
2477
2478                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2479                 lt->deleteLineForward(bv_);
2480                 update(lt,
2481                        BufferView::SELECT
2482                        | BufferView::FITCUR
2483                        | BufferView::CHANGE);
2484                 moveCursorUpdate(false);
2485         }
2486         break;
2487
2488                 /* -------> Set mark off. */
2489         case LFUN_MARK_OFF:
2490         {
2491                 LyXText * lt = bv_->getLyXText();
2492
2493                 beforeChange(lt);
2494                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2495                 lt->selection.cursor = lt->cursor;
2496                 owner_->getLyXFunc()->setMessage(N_("Mark off"));
2497         }
2498         break;
2499
2500                 /* -------> Set mark on. */
2501         case LFUN_MARK_ON:
2502         {
2503                 LyXText * lt = bv_->getLyXText();
2504
2505                 beforeChange(lt);
2506                 lt->selection.mark(true);
2507                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2508                 lt->selection.cursor = lt->cursor;
2509                 owner_->getLyXFunc()->setMessage(N_("Mark on"));
2510         }
2511         break;
2512
2513         case LFUN_BACKSPACE:
2514         {
2515                 LyXText * lt = bv_->getLyXText();
2516
2517                 if (!lt->selection.set()) {
2518                         if (owner_->getIntl()->getTransManager().backspace()) {
2519                                 lt->backspace(bv_);
2520                                 lt->selection.cursor = lt->cursor;
2521                                 update(lt,
2522                                        BufferView::SELECT
2523                                        | BufferView::FITCUR
2524                                        | BufferView::CHANGE);
2525                                 // It is possible to make it a lot faster still
2526                                 // just comment out the line below...
2527                                 showCursor();
2528                         }
2529                 } else {
2530                         bv_->cut(false);
2531                 }
2532                 owner_->view_state_changed();
2533                 switchKeyMap();
2534         }
2535         break;
2536
2537         case LFUN_BACKSPACE_SKIP:
2538         {
2539                 // Reverse the effect of LFUN_BREAKPARAGRAPH_SKIP.
2540                 LyXText * lt = bv_->getLyXText();
2541
2542                 LyXCursor cursor = lt->cursor;
2543
2544                 if (!lt->selection.set()) {
2545                         if (cursor.pos() == 0
2546                             && !(cursor.par()->params().spaceTop()
2547                                  == VSpace (VSpace::NONE))) {
2548                                 lt->setParagraph
2549                                         (bv_,
2550                                          cursor.par()->params().lineTop(),
2551                                          cursor.par()->params().lineBottom(),
2552                                          cursor.par()->params().pagebreakTop(),
2553                                          cursor.par()->params().pagebreakBottom(),
2554                                          VSpace(VSpace::NONE), cursor.par()->params().spaceBottom(),
2555                                          cursor.par()->params().spacing(),
2556                                          cursor.par()->params().align(),
2557                                          cursor.par()->params().labelWidthString(), 0);
2558                                 update(lt,
2559                                        BufferView::SELECT
2560                                        | BufferView::FITCUR
2561                                        | BufferView::CHANGE);
2562                         } else {
2563                                 lt->backspace(bv_);
2564                                 lt->selection.cursor = cursor;
2565                                 update(lt,
2566                                        BufferView::SELECT
2567                                        | BufferView::FITCUR
2568                                        | BufferView::CHANGE);
2569                         }
2570                 } else
2571                         bv_->cut(false);
2572         }
2573         break;
2574
2575         case LFUN_BREAKPARAGRAPH:
2576         {
2577                 LyXText * lt = bv_->getLyXText();
2578
2579                 beforeChange(lt);
2580                 lt->breakParagraph(bv_, 0);
2581                 update(lt,
2582                        BufferView::SELECT
2583                        | BufferView::FITCUR
2584                        | BufferView::CHANGE);
2585                 lt->selection.cursor = lt->cursor;
2586                 switchKeyMap();
2587                 owner_->view_state_changed();
2588                 break;
2589         }
2590
2591         case LFUN_BREAKPARAGRAPHKEEPLAYOUT:
2592         {
2593                 LyXText * lt = bv_->getLyXText();
2594
2595                 beforeChange(lt);
2596                 lt->breakParagraph(bv_, 1);
2597                 update(lt,
2598                        BufferView::SELECT
2599                        | BufferView::FITCUR
2600                        | BufferView::CHANGE);
2601                 lt->selection.cursor = lt->cursor;
2602                 switchKeyMap();
2603                 owner_->view_state_changed();
2604                 break;
2605         }
2606
2607         case LFUN_BREAKPARAGRAPH_SKIP:
2608         {
2609                 // When at the beginning of a paragraph, remove
2610                 // indentation and add a "defskip" at the top.
2611                 // Otherwise, do the same as LFUN_BREAKPARAGRAPH.
2612                 LyXText * lt = bv_->getLyXText();
2613
2614                 LyXCursor cursor = lt->cursor;
2615
2616                 beforeChange(lt);
2617                 if (cursor.pos() == 0) {
2618                         if (cursor.par()->params().spaceTop() == VSpace(VSpace::NONE)) {
2619                                 lt->setParagraph
2620                                         (bv_,
2621                                          cursor.par()->params().lineTop(),
2622                                          cursor.par()->params().lineBottom(),
2623                                          cursor.par()->params().pagebreakTop(),
2624                                          cursor.par()->params().pagebreakBottom(),
2625                                          VSpace(VSpace::DEFSKIP), cursor.par()->params().spaceBottom(),
2626                                          cursor.par()->params().spacing(),
2627                                          cursor.par()->params().align(),
2628                                          cursor.par()->params().labelWidthString(), 1);
2629                                 //update(BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
2630                         }
2631                 }
2632                 else {
2633                         lt->breakParagraph(bv_, 0);
2634                         //update(BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
2635                 }
2636
2637                 update(lt,
2638                        BufferView::SELECT
2639                        | BufferView::FITCUR
2640                        | BufferView::CHANGE);
2641                 lt->selection.cursor = cursor;
2642                 switchKeyMap();
2643                 owner_->view_state_changed();
2644         }
2645         break;
2646
2647         case LFUN_PARAGRAPH_SPACING:
2648         {
2649                 LyXText * lt = bv_->getLyXText();
2650
2651                 Paragraph * par = lt->cursor.par();
2652                 Spacing::Space cur_spacing = par->params().spacing().getSpace();
2653                 float cur_value = 1.0;
2654                 if (cur_spacing == Spacing::Other) {
2655                         cur_value = par->params().spacing().getValue();
2656                 }
2657
2658                 istringstream istr(argument.c_str());
2659
2660                 string tmp;
2661                 istr >> tmp;
2662                 Spacing::Space new_spacing = cur_spacing;
2663                 float new_value = cur_value;
2664                 if (tmp.empty()) {
2665                         lyxerr << "Missing argument to `paragraph-spacing'"
2666                                << endl;
2667                 } else if (tmp == "single") {
2668                         new_spacing = Spacing::Single;
2669                 } else if (tmp == "onehalf") {
2670                         new_spacing = Spacing::Onehalf;
2671                 } else if (tmp == "double") {
2672                         new_spacing = Spacing::Double;
2673                 } else if (tmp == "other") {
2674                         new_spacing = Spacing::Other;
2675                         float tmpval = 0.0;
2676                         istr >> tmpval;
2677                         lyxerr << "new_value = " << tmpval << endl;
2678                         if (tmpval != 0.0)
2679                                 new_value = tmpval;
2680                 } else if (tmp == "default") {
2681                         new_spacing = Spacing::Default;
2682                 } else {
2683                         lyxerr << _("Unknown spacing argument: ")
2684                                << argument << endl;
2685                 }
2686                 if (cur_spacing != new_spacing || cur_value != new_value) {
2687                         par->params().spacing(Spacing(new_spacing, new_value));
2688                         lt->redoParagraph(bv_);
2689                         update(lt,
2690                                BufferView::SELECT
2691                                | BufferView::FITCUR
2692                                | BufferView::CHANGE);
2693                 }
2694         }
2695         break;
2696
2697         case LFUN_INSET_TOGGLE:
2698         {
2699                 LyXText * lt = bv_->getLyXText();
2700                 hideCursor();
2701                 beforeChange(lt);
2702                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2703                 lt->toggleInset(bv_);
2704                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2705                 switchKeyMap();
2706         }
2707                 break;
2708
2709         case LFUN_QUOTE:
2710                 smartQuote();
2711                 break;
2712
2713         case LFUN_HTMLURL:
2714         case LFUN_URL:
2715         {
2716                 InsetCommandParams p;
2717                 if (action == LFUN_HTMLURL)
2718                         p.setCmdName("htmlurl");
2719                 else
2720                         p.setCmdName("url");
2721                 owner_->getDialogs()->createUrl(p.getAsString());
2722         }
2723         break;
2724
2725         case LFUN_INSERT_URL:
2726         {
2727                 InsetCommandParams p;
2728                 p.setFromString(argument);
2729
2730                 InsetUrl * inset = new InsetUrl(p);
2731                 if (!insertInset(inset))
2732                         delete inset;
2733                 else
2734                         updateInset(inset, true);
2735         }
2736         break;
2737
2738         case LFUN_INSET_ERT:
2739                 insertAndEditInset(new InsetERT(buffer_->params));
2740                 break;
2741
2742         case LFUN_INSET_EXTERNAL:
2743                 insertAndEditInset(new InsetExternal);
2744                 break;
2745
2746         case LFUN_INSET_FOOTNOTE:
2747                 insertAndEditInset(new InsetFoot(buffer_->params));
2748                 break;
2749
2750         case LFUN_INSET_MARGINAL:
2751                 insertAndEditInset(new InsetMarginal(buffer_->params));
2752                 break;
2753
2754         case LFUN_INSET_MINIPAGE:
2755                 insertAndEditInset(new InsetMinipage(buffer_->params));
2756                 break;
2757
2758         case LFUN_INSERT_NOTE:
2759                 insertAndEditInset(new InsetNote(buffer_->params));
2760                 break;
2761
2762         case LFUN_INSET_FLOAT:
2763                 // check if the float type exist
2764                 if (floatList.typeExist(argument)) {
2765                         insertAndEditInset(new InsetFloat(buffer_->params,
2766                                                           argument));
2767                 } else {
2768                         lyxerr << "Non-existent float type: "
2769                                << argument << endl;
2770                 }
2771                 break;
2772
2773         case LFUN_INSET_WIDE_FLOAT:
2774                 // check if the float type exist
2775                 if (floatList.typeExist(argument)) {
2776                         InsetFloat * new_inset =
2777                                 new InsetFloat(buffer_->params, argument);
2778                         new_inset->wide(true);
2779                         insertAndEditInset(new_inset);
2780                 } else {
2781                         lyxerr << "Non-existent float type: "
2782                                << argument << endl;
2783                 }
2784                 break;
2785
2786 #if 0
2787         case LFUN_INSET_LIST:
2788                 insertAndEditInset(new InsetList);
2789                 break;
2790
2791         case LFUN_INSET_THEOREM:
2792                 insertAndEditInset(new InsetTheorem);
2793                 break;
2794 #endif
2795
2796         case LFUN_INSET_CAPTION:
2797         {
2798                 // Do we have a locking inset...
2799                 if (bv_->theLockingInset()) {
2800                         lyxerr << "Locking inset code: "
2801                                << static_cast<int>(bv_->theLockingInset()->lyxCode());
2802                         InsetCaption * new_inset =
2803                                 new InsetCaption(buffer_->params);
2804                         new_inset->setOwner(bv_->theLockingInset());
2805                         new_inset->setAutoBreakRows(true);
2806                         new_inset->setDrawFrame(0, InsetText::LOCKED);
2807                         new_inset->setFrameColor(0, LColor::captionframe);
2808                         if (insertInset(new_inset))
2809                                 new_inset->edit(bv_);
2810                         else
2811                                 delete new_inset;
2812                 }
2813         }
2814         break;
2815
2816         case LFUN_INSET_TABULAR:
2817         {
2818                 int r = 2;
2819                 int c = 2;
2820                 if (!argument.empty())
2821                         ::sscanf(argument.c_str(),"%d%d", &r, &c);
2822                 InsetTabular * new_inset =
2823                         new InsetTabular(*buffer_, r, c);
2824                 bool const rtl =
2825                         bv_->getLyXText()->real_current_font.isRightToLeft();
2826                 if (!open_new_inset(new_inset, rtl))
2827                         delete new_inset;
2828         }
2829         break;
2830
2831         // --- lyxserver commands ----------------------------
2832
2833         case LFUN_CHARATCURSOR:
2834         {
2835                 pos_type pos = bv_->getLyXText()->cursor.pos();
2836                 if (pos < bv_->getLyXText()->cursor.par()->size())
2837                         owner_->getLyXFunc()->setMessage(
2838                                 tostr(bv_->getLyXText()->cursor.par()->getChar(pos)));
2839                 else
2840                         owner_->getLyXFunc()->setMessage("EOF");
2841         }
2842         break;
2843
2844         case LFUN_GETXY:
2845                 owner_->getLyXFunc()->setMessage(tostr(bv_->getLyXText()->cursor.x())
2846                                                  + ' '
2847                                                  + tostr(bv_->getLyXText()->cursor.y()));
2848                 break;
2849
2850         case LFUN_SETXY:
2851         {
2852                 int x = 0;
2853                 int y = 0;
2854                 if (::sscanf(argument.c_str(), " %d %d", &x, &y) != 2) {
2855                         lyxerr << "SETXY: Could not parse coordinates in '"
2856                                << argument << std::endl;
2857                 }
2858                 bv_->getLyXText()->setCursorFromCoordinates(bv_, x, y);
2859         }
2860         break;
2861
2862         case LFUN_GETLAYOUT:
2863                 owner_->getLyXFunc()->setMessage(tostr(bv_->getLyXText()->cursor.par()->layout()));
2864                 break;
2865
2866         case LFUN_GETFONT:
2867         {
2868                 LyXFont & font = bv_->getLyXText()->current_font;
2869                 if (font.shape() == LyXFont::ITALIC_SHAPE)
2870                         owner_->getLyXFunc()->setMessage("E");
2871                 else if (font.shape() == LyXFont::SMALLCAPS_SHAPE)
2872                         owner_->getLyXFunc()->setMessage("N");
2873                 else
2874                         owner_->getLyXFunc()->setMessage("0");
2875
2876         }
2877         break;
2878
2879         // --- accented characters ---------------------------
2880
2881         case LFUN_UMLAUT:
2882         case LFUN_CIRCUMFLEX:
2883         case LFUN_GRAVE:
2884         case LFUN_ACUTE:
2885         case LFUN_TILDE:
2886         case LFUN_CEDILLA:
2887         case LFUN_MACRON:
2888         case LFUN_DOT:
2889         case LFUN_UNDERDOT:
2890         case LFUN_UNDERBAR:
2891         case LFUN_CARON:
2892         case LFUN_SPECIAL_CARON:
2893         case LFUN_BREVE:
2894         case LFUN_TIE:
2895         case LFUN_HUNG_UMLAUT:
2896         case LFUN_CIRCLE:
2897         case LFUN_OGONEK:
2898                 if (argument.empty()) {
2899                         // As always...
2900                         owner_->getLyXFunc()->handleKeyFunc(action);
2901                 } else {
2902                         owner_->getLyXFunc()->handleKeyFunc(action);
2903                         owner_->getIntl()->getTransManager()
2904                                 .TranslateAndInsert(argument[0], bv_->getLyXText());
2905                         update(bv_->getLyXText(),
2906                                BufferView::SELECT
2907                                | BufferView::FITCUR
2908                                | BufferView::CHANGE);
2909                 }
2910                 break;
2911
2912         case LFUN_MATH:
2913                 mathDispatch(bv_, argument);
2914                 break;
2915
2916         case LFUN_MATH_MACRO:
2917                 mathDispatchMathMacro(bv_, argument);
2918                 break;
2919
2920         case LFUN_MATH_DELIM:
2921                 mathDispatchMathDelim(bv_, argument);
2922                 break;
2923
2924         case LFUN_INSERT_MATRIX:
2925                 mathDispatchInsertMatrix(bv_, argument);
2926                 break;
2927
2928         case LFUN_INSERT_MATH:
2929                 mathDispatchInsertMath(bv_, argument);
2930                 break;
2931
2932         case LFUN_MATH_IMPORT_SELECTION: // Imports LaTeX from the X selection
2933                 mathDispatchMathImportSelection(bv_, argument);
2934                 break;
2935
2936         case LFUN_MATH_DISPLAY:          // Open or create a displayed math inset
2937                 mathDispatchMathDisplay(bv_, argument);
2938                 break;
2939
2940         case LFUN_MATH_MODE:             // Open or create an inlined math inset
2941                 mathDispatchMathMode(bv_, argument);
2942                 break;
2943
2944         case LFUN_GREEK:                 // Insert a single greek letter
2945                 mathDispatchGreek(bv_, argument);
2946                 break;
2947
2948         case LFUN_CITATION_INSERT:
2949         {
2950                 InsetCommandParams p;
2951                 p.setFromString(argument);
2952
2953                 InsetCitation * inset = new InsetCitation(p);
2954                 if (!insertInset(inset))
2955                         delete inset;
2956                 else
2957                         updateInset(inset, true);
2958         }
2959         break;
2960
2961         case LFUN_INSERT_BIBTEX:
2962         {
2963                 // ale970405+lasgoutt970425
2964                 // The argument can be up to two tokens separated
2965                 // by a space. The first one is the bibstyle.
2966                 string const db       = token(argument, ' ', 0);
2967                 string const bibstyle = token(argument, ' ', 1);
2968
2969                 InsetCommandParams p("BibTeX", db, bibstyle);
2970                 InsetBibtex * inset = new InsetBibtex(p);
2971
2972                 if (insertInset(inset)) {
2973                         if (argument.empty())
2974                                 inset->edit(bv_);
2975                 } else
2976                         delete inset;
2977         }
2978         break;
2979
2980         // BibTeX data bases
2981         case LFUN_BIBDB_ADD:
2982         {
2983                 InsetBibtex * inset =
2984                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
2985                 if (inset) {
2986                         inset->addDatabase(argument);
2987                 }
2988         }
2989         break;
2990
2991         case LFUN_BIBDB_DEL:
2992         {
2993                 InsetBibtex * inset =
2994                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
2995                 if (inset) {
2996                         inset->delDatabase(argument);
2997                 }
2998         }
2999         break;
3000
3001         case LFUN_BIBTEX_STYLE:
3002         {
3003                 InsetBibtex * inset =
3004                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
3005                 if (inset) {
3006                         inset->setOptions(argument);
3007                 }
3008         }
3009         break;
3010
3011         case LFUN_INDEX_INSERT:
3012         {
3013                 string entry = argument;
3014                 if (entry.empty()) {
3015                         entry = bv_->getLyXText()->getStringToIndex(bv_);
3016                 }
3017
3018                 if (entry.empty()) {
3019                         owner_->getDialogs()->createIndex();
3020                         break;
3021                 }
3022
3023                 InsetIndex * inset = new InsetIndex(InsetCommandParams("index", entry));
3024
3025                 if (!insertInset(inset)) {
3026                         delete inset;
3027                 } else {
3028                         updateInset(inset, true);
3029                 }
3030         }
3031         break;
3032
3033         case LFUN_INDEX_PRINT:
3034         {
3035                 InsetCommandParams p("printindex");
3036                 Inset * inset = new InsetPrintIndex(p);
3037                 if (!insertInset(inset, tclass.defaultLayoutName()))
3038                         delete inset;
3039         }
3040         break;
3041
3042         case LFUN_PARENTINSERT:
3043         {
3044                 InsetCommandParams p("lyxparent", argument);
3045                 Inset * inset = new InsetParent(p, *buffer_);
3046                 if (!insertInset(inset, tclass.defaultLayoutName()))
3047                         delete inset;
3048         }
3049
3050         break;
3051
3052         case LFUN_CHILD_INSERT:
3053         {
3054                 InsetInclude::Params p;
3055                 p.cparams.setFromString(argument);
3056                 p.masterFilename_ = buffer_->fileName();
3057
3058                 InsetInclude * inset = new InsetInclude(p);
3059                 if (!insertInset(inset))
3060                         delete inset;
3061                 else {
3062                         updateInset(inset, true);
3063                         bv_->owner()->getDialogs()->showInclude(inset);
3064                 }
3065         }
3066         break;
3067
3068         case LFUN_FLOAT_LIST:
3069                 if (floatList.typeExist(argument)) {
3070                         Inset * inset = new InsetFloatList(argument);
3071                         if (!insertInset(inset, tclass.defaultLayoutName()))
3072                                 delete inset;
3073                 } else {
3074                         lyxerr << "Non-existent float type: "
3075                                << argument << endl;
3076                 }
3077                 break;
3078
3079         case LFUN_THESAURUS_ENTRY:
3080         {
3081                 string arg = argument;
3082
3083                 if (arg.empty()) {
3084                         arg = bv_->getLyXText()->selectionAsString(buffer_,
3085                                                                    false);
3086
3087                         // FIXME
3088                         if (arg.size() > 100 || arg.empty()) {
3089                                 // Get word or selection
3090                                 bv_->getLyXText()->selectWordWhenUnderCursor(bv_, LyXText::WHOLE_WORD);
3091                                 arg = bv_->getLyXText()->selectionAsString(buffer_, false);
3092                                 // FIXME: where is getLyXText()->unselect(bv_) ?
3093                         }
3094                 }
3095
3096                 bv_->owner()->getDialogs()->showThesaurus(arg);
3097         }
3098                 break;
3099
3100         case LFUN_SELFINSERT:
3101         {
3102                 if (argument.empty()) break;
3103
3104                 /* Automatically delete the currently selected
3105                  * text and replace it with what is being
3106                  * typed in now. Depends on lyxrc settings
3107                  * "auto_region_delete", which defaults to
3108                  * true (on). */
3109
3110                 LyXText * lt = bv_->getLyXText();
3111
3112                 if (lyxrc.auto_region_delete) {
3113                         if (lt->selection.set()) {
3114                                 lt->cutSelection(bv_, false, false);
3115                                 bv_->update(lt,
3116                                             BufferView::SELECT
3117                                             | BufferView::FITCUR
3118                                             | BufferView::CHANGE);
3119                         }
3120                         workarea().haveSelection(false);
3121                 }
3122
3123                 beforeChange(lt);
3124                 LyXFont const old_font(lt->real_current_font);
3125
3126                 string::const_iterator cit = argument.begin();
3127                 string::const_iterator end = argument.end();
3128                 for (; cit != end; ++cit) {
3129                         owner_->getIntl()->getTransManager().
3130                                 TranslateAndInsert(*cit, lt);
3131                 }
3132
3133                 bv_->update(lt,
3134                             BufferView::SELECT
3135                             | BufferView::FITCUR
3136                             | BufferView::CHANGE);
3137
3138                 lt->selection.cursor = lt->cursor;
3139                 moveCursorUpdate(false);
3140
3141                 // real_current_font.number can change so we need to
3142                 // update the minibuffer
3143                 if (old_font != lt->real_current_font)
3144                         owner_->view_state_changed();
3145                 //return string();
3146         }
3147         break;
3148
3149         case LFUN_DATE_INSERT:  // jdblair: date-insert cmd
3150         {
3151                 time_t now_time_t = time(NULL);
3152                 struct tm * now_tm = localtime(&now_time_t);
3153                 setlocale(LC_TIME, "");
3154                 string arg;
3155                 if (!argument.empty())
3156                         arg = argument;
3157                 else
3158                         arg = lyxrc.date_insert_format;
3159                 char datetmp[32];
3160                 int const datetmp_len =
3161                         ::strftime(datetmp, 32, arg.c_str(), now_tm);
3162
3163                 LyXText * lt = bv_->getLyXText();
3164
3165                 for (int i = 0; i < datetmp_len; i++) {
3166                         lt->insertChar(bv_, datetmp[i]);
3167                         update(lt,
3168                                BufferView::SELECT
3169                                | BufferView::FITCUR
3170                                | BufferView::CHANGE);
3171                 }
3172
3173                 lt->selection.cursor = lt->cursor;
3174                 moveCursorUpdate(false);
3175         }
3176         break;
3177
3178         case LFUN_UNKNOWN_ACTION:
3179                 owner_->getLyXFunc()->setErrorMessage(N_("Unknown function!"));
3180                 break;
3181
3182         default:
3183                 return false;
3184         } // end of switch
3185
3186         return true;
3187 }
3188
3189
3190 void BufferView::Pimpl::newline()
3191 {
3192         if (available()) {
3193                 LyXText * lt = bv_->getLyXText();
3194                 hideCursor();
3195                 update(lt,
3196                        BufferView::SELECT
3197                        | BufferView::FITCUR);
3198                 lt->insertChar(bv_, Paragraph::META_NEWLINE);
3199                 update(lt,
3200                        BufferView::SELECT
3201                        | BufferView::FITCUR
3202                        | BufferView::CHANGE);
3203         }
3204 }
3205
3206
3207 void BufferView::Pimpl::hfill()
3208 {
3209         if (available()) {
3210                 LyXText * lt = bv_->getLyXText();
3211                 hideCursor();
3212                 update(lt,
3213                        BufferView::SELECT
3214                        | BufferView::FITCUR);
3215                 lt->insertChar(bv_, Paragraph::META_HFILL);
3216                 update(lt,
3217                        BufferView::SELECT
3218                        | BufferView::FITCUR
3219                        | BufferView::CHANGE);
3220         }
3221 }
3222
3223
3224 void BufferView::Pimpl::specialChar(InsetSpecialChar::Kind kind)
3225 {
3226         if (available()) {
3227                 LyXText * lt = bv_->getLyXText();
3228
3229                 hideCursor();
3230                 update(lt, BufferView::SELECT|BufferView::FITCUR);
3231                 InsetSpecialChar * new_inset =
3232                         new InsetSpecialChar(kind);
3233                 if (!insertInset(new_inset))
3234                         delete new_inset;
3235                 else
3236                         updateInset(new_inset, true);
3237         }
3238 }
3239
3240
3241 void BufferView::Pimpl::smartQuote()
3242 {
3243         LyXText const * lt = bv_->getLyXText();
3244         Paragraph const * par = lt->cursor.par();
3245         pos_type pos = lt->cursor.pos();
3246         char c;
3247
3248         if (!pos
3249             || (par->isInset(pos - 1)
3250                 && par->getInset(pos - 1)->isSpace()))
3251                 c = ' ';
3252         else
3253                 c = par->getChar(pos - 1);
3254
3255
3256         hideCursor();
3257
3258         LyXLayout_ptr const & style = par->layout();
3259
3260         if (style->pass_thru ||
3261                 (!insertInset(new InsetQuotes(c, bv_->buffer()->params))))
3262                 bv_->owner()->getLyXFunc()->dispatch(LFUN_SELFINSERT, string("\""));
3263 }
3264
3265
3266 void BufferView::Pimpl::insertAndEditInset(Inset * inset)
3267 {
3268 #if 0
3269         if (insertInset(inset))
3270                 inset->edit(bv_);
3271         else
3272                 delete inset;
3273 #else
3274         bool gotsel = false;
3275
3276         if (bv_->getLyXText()->selection.set()) {
3277                 bv_->getLyXText()->cutSelection(bv_, true, false);
3278                 gotsel = true;
3279         }
3280
3281         if (insertInset(inset)) {
3282                 inset->edit(bv_);
3283                 if (gotsel)
3284                         owner_->getLyXFunc()->dispatch(LFUN_PASTESELECTION);
3285         }
3286         else
3287                 delete inset;
3288 #endif
3289 }
3290
3291
3292 // Open and lock an updatable inset
3293 bool BufferView::Pimpl::open_new_inset(UpdatableInset * new_inset, bool behind)
3294 {
3295         LyXText * lt = bv_->getLyXText();
3296
3297         beforeChange(lt);
3298         finishUndo();
3299         if (!insertInset(new_inset)) {
3300                 delete new_inset;
3301                 return false;
3302         }
3303         new_inset->edit(bv_, !behind);
3304         return true;
3305 }
3306
3307
3308 bool BufferView::Pimpl::insertInset(Inset * inset, string const & lout)
3309 {
3310         // if we are in a locking inset we should try to insert the
3311         // inset there otherwise this is a illegal function now
3312         if (bv_->theLockingInset()) {
3313                 if (bv_->theLockingInset()->insetAllowed(inset))
3314                     return bv_->theLockingInset()->insertInset(bv_, inset);
3315                 return false;
3316         }
3317
3318         // not quite sure if we want this...
3319         setCursorParUndo(bv_);
3320         freezeUndo();
3321
3322         beforeChange(bv_->text);
3323         if (!lout.empty()) {
3324                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
3325                 bv_->text->breakParagraph(bv_);
3326                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
3327
3328                 if (bv_->text->cursor.par()->size()) {
3329                         bv_->text->cursorLeft(bv_);
3330
3331                         bv_->text->breakParagraph(bv_);
3332                         update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
3333                 }
3334
3335                 string lres = lout;
3336                 LyXTextClass const & tclass =
3337                         buffer_->params.getLyXTextClass();
3338                 bool hasLayout = tclass.hasLayout(lres);
3339                 string lay = tclass.defaultLayoutName();
3340
3341                 if (hasLayout != false) {
3342                         // layout found
3343                         lay = lres;
3344                 } else {
3345                         // layout not fount using default
3346                         lay = tclass.defaultLayoutName();
3347                 }
3348
3349                 bv_->text->setLayout(bv_, lay);
3350
3351                 bv_->text->setParagraph(bv_, 0, 0,
3352                                    0, 0,
3353                                    VSpace(VSpace::NONE), VSpace(VSpace::NONE),
3354                                    Spacing(),
3355                                    LYX_ALIGN_LAYOUT,
3356                                    string(),
3357                                    0);
3358                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
3359         }
3360
3361         bv_->text->insertInset(bv_, inset);
3362         update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
3363
3364         unFreezeUndo();
3365         return true;
3366 }
3367
3368
3369 void BufferView::Pimpl::updateInset(Inset * inset, bool mark_dirty)
3370 {
3371         if (!inset || !available())
3372                 return;
3373
3374         // first check for locking insets
3375         if (bv_->theLockingInset()) {
3376                 if (bv_->theLockingInset() == inset) {
3377                         if (bv_->text->updateInset(bv_, inset)) {
3378                                 update();
3379                                 if (mark_dirty) {
3380                                         buffer_->markDirty();
3381                                 }
3382                                 updateScrollbar();
3383                                 return;
3384                         }
3385                 } else if (bv_->theLockingInset()->updateInsetInInset(bv_, inset)) {
3386                         if (bv_->text->updateInset(bv_,  bv_->theLockingInset())) {
3387                                 update();
3388                                 if (mark_dirty) {
3389                                         buffer_->markDirty();
3390                                 }
3391                                 updateScrollbar();
3392                                 return;
3393                         }
3394                 }
3395         }
3396
3397         // then check if the inset is a top_level inset (has no owner)
3398         // if yes do the update as always otherwise we have to update the
3399         // toplevel inset where this inset is inside
3400         Inset * tl_inset = inset;
3401         while(tl_inset->owner())
3402                 tl_inset = tl_inset->owner();
3403         hideCursor();
3404         if (tl_inset == inset) {
3405                 update(bv_->text, BufferView::UPDATE);
3406                 if (bv_->text->updateInset(bv_, inset)) {
3407                         if (mark_dirty) {
3408                                 update(bv_->text,
3409                                        BufferView::SELECT
3410                                        | BufferView::FITCUR
3411                                        | BufferView::CHANGE);
3412                         } else {
3413                                 update(bv_->text, SELECT);
3414                         }
3415                         return;
3416                 }
3417         } else if (static_cast<UpdatableInset *>(tl_inset)
3418                            ->updateInsetInInset(bv_, inset))
3419         {
3420                         if (bv_->text->updateInset(bv_,  tl_inset)) {
3421                                 update();
3422                                 updateScrollbar();
3423                         }
3424         }
3425 }
3426
3427
3428 void BufferView::Pimpl::gotoInset(vector<Inset::Code> const & codes,
3429                                   bool same_content)
3430 {
3431         if (!available()) return;
3432
3433         hideCursor();
3434         beforeChange(bv_->text);
3435         update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
3436
3437         LyXCursor const & cursor = bv_->text->cursor;
3438
3439         string contents;
3440         if (same_content &&
3441             cursor.par()->isInset(cursor.pos())) {
3442                 Inset const * inset = cursor.par()->getInset(cursor.pos());
3443                 if (find(codes.begin(), codes.end(), inset->lyxCode())
3444                     != codes.end())
3445                         contents =
3446                                 static_cast<InsetCommand const *>(inset)->getContents();
3447         }
3448
3449
3450         if (!bv_->text->gotoNextInset(bv_, codes, contents)) {
3451                 if (bv_->text->cursor.pos()
3452                     || bv_->text->cursor.par() != bv_->text->ownerParagraph()) {
3453                         LyXCursor tmp = bv_->text->cursor;
3454                         bv_->text->cursor.par(bv_->text->ownerParagraph());
3455                         bv_->text->cursor.pos(0);
3456                         if (!bv_->text->gotoNextInset(bv_, codes, contents)) {
3457                                 bv_->text->cursor = tmp;
3458                                 bv_->owner()->message(_("No more insets"));
3459                         }
3460                 } else {
3461                         bv_->owner()->message(_("No more insets"));
3462                 }
3463         }
3464         update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
3465         bv_->text->selection.cursor = bv_->text->cursor;
3466 }
3467
3468
3469 void BufferView::Pimpl::gotoInset(Inset::Code code, bool same_content)
3470 {
3471         gotoInset(vector<Inset::Code>(1, code), same_content);
3472 }