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