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