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