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