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