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