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