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