]> git.lyx.org Git - lyx.git/blob - src/BufferView_pimpl.C
the workarea changes plus small math stuff
[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 "frontends/mouse_state.h"
24 #include "lyxtext.h"
25 #include "lyxrow.h"
26 #include "paragraph.h"
27 #include "frontends/LyXView.h"
28 #include "commandtags.h"
29 #include "lyxfunc.h"
30 #include "debug.h"
31 #include "bufferview_funcs.h"
32 #include "TextCache.h"
33 #include "bufferlist.h"
34 #include "lyxrc.h"
35 #include "intl.h"
36 // added for Dispatch functions
37 #include "lyx_cb.h"
38 #include "lyx_main.h"
39 #include "FloatList.h"
40 #include "gettext.h"
41 #include "ParagraphParameters.h"
42 #include "undo_funcs.h"
43 #include "funcrequest.h"
44 #include "box.h"
45
46 #include "insets/insetbib.h"
47 #include "insets/insettext.h"
48 #include "insets/inseturl.h"
49 #include "insets/insetlatexaccent.h"
50 #include "insets/insettoc.h"
51 #include "insets/insetref.h"
52 #include "insets/insetparent.h"
53 #include "insets/insetindex.h"
54 #include "insets/insetinclude.h"
55 #include "insets/insetcite.h"
56 #include "insets/insetgraphics.h"
57 #include "insets/insetmarginal.h"
58 #include "insets/insetcaption.h"
59 #include "insets/insetfloatlist.h"
60
61 #include "mathed/formulabase.h"
62
63 #include "graphics/Previews.h"
64
65 #include "support/LAssert.h"
66 #include "support/lstrings.h"
67 #include "support/filetools.h"
68
69 #include <boost/bind.hpp>
70 #include <boost/signals/connection.hpp>
71
72 #include <unistd.h>
73 #include <sys/wait.h>
74
75
76 using std::vector;
77 using std::find_if;
78 using std::find;
79 using std::pair;
80 using std::endl;
81 using std::make_pair;
82 using std::min;
83
84 using lyx::pos_type;
85
86 /* the selection possible is needed, that only motion events are
87  * used, where the bottom press event was on the drawing area too */
88 bool selection_possible = false;
89
90 extern BufferList bufferlist;
91 extern int bibitemMaxWidth(BufferView *, LyXFont const &);
92
93
94 namespace {
95
96 unsigned int const saved_positions_num = 20;
97
98 // All the below connection objects are needed because of a bug in some
99 // versions of GCC (<=2.96 are on the suspects list.) By having and assigning
100 // to these connections we avoid a segfault upon startup, and also at exit.
101 // (Lgb)
102
103 boost::signals::connection dispatchcon;
104 boost::signals::connection timecon;
105 boost::signals::connection doccon;
106 boost::signals::connection resizecon;
107 boost::signals::connection kpresscon;
108 boost::signals::connection selectioncon;
109 boost::signals::connection lostcon;
110
111
112         /**
113          * Return the on-screen dimensions of the inset at the cursor.
114          * Pre-condition: the cursor must be at an inset.
115          */
116 Box insetDimensions(BufferView * bv, LyXText const & text,
117                                        LyXCursor const & cursor)
118 {
119         Paragraph /*const*/ & par = *cursor.par();
120         pos_type const pos = cursor.pos();
121
122         lyx::Assert(par.getInset(pos));
123
124         Inset const & inset(*par.getInset(pos));
125
126         LyXFont const & font = text.getFont(bv->buffer(), &par, pos);
127
128         int const width = inset.width(bv, font);
129         int const inset_x = font.isVisibleRightToLeft()
130                 ? (cursor.ix() - width) : cursor.ix();
131
132         return Box(
133                 inset_x + inset.scroll(),
134                 inset_x + width,
135                 cursor.iy() - inset.ascent(bv, font),
136                 cursor.iy() + inset.descent(bv, font));
137 }
138
139
140 /**
141  * check if the given co-ordinates are inside an inset at the
142  * given cursor, if one exists. If so, the inset is returned,
143  * and the co-ordinates are made relative. Otherwise, 0 is returned.
144  */
145 Inset * checkInset(BufferView * bv, LyXText const & text,
146                                       LyXCursor const & cursor, int & x, int & y)
147 {
148         pos_type const pos = cursor.pos();
149         Paragraph /*const*/ & par(*cursor.par());
150
151         if (pos >= par.size() || !par.isInset(pos)) {
152                 return 0;
153         }
154
155         Inset /*const*/ * inset = par.getInset(pos);
156
157         if (!isEditableInset(inset)) 
158                 return 0;
159
160         Box b = insetDimensions(bv, text, cursor);
161
162         if (!b.contained(x, y)) {
163                 lyxerr[Debug::GUI] << "Missed inset at x,y " << x << "," << y
164                         << " box " << b << endl;
165                 return 0;
166         }
167
168         text.setCursor(bv, &par, pos, true);
169
170         x -= b.x1;
171         // The origin of an inset is on the baseline
172         y -= text.cursor.iy();
173
174         return inset;
175 }
176
177 } // anon namespace
178
179
180 BufferView::Pimpl::Pimpl(BufferView * bv, LyXView * owner,
181              int xpos, int ypos, int width, int height)
182         : bv_(bv), owner_(owner), buffer_(0), cursor_timeout(400),
183           using_xterm_cursor(false)
184 {
185         workarea_.reset(WorkAreaFactory::create(xpos, ypos, width, height));
186         screen_.reset(LyXScreenFactory::create(workarea()));
187
188         // Setup the signals
189         doccon = workarea().scrollDocView
190                 .connect(boost::bind(&BufferView::Pimpl::scrollDocView, this, _1));
191         resizecon = workarea().workAreaResize
192                 .connect(boost::bind(&BufferView::Pimpl::workAreaResize, this));
193         dispatchcon = workarea().dispatch
194                 .connect(boost::bind(&BufferView::Pimpl::dispatch, this, _1));
195         kpresscon = workarea().workAreaKeyPress
196                 .connect(boost::bind(&BufferView::Pimpl::workAreaKeyPress, this, _1, _2));
197         selectioncon = workarea().selectionRequested
198                 .connect(boost::bind(&BufferView::Pimpl::selectionRequested, this));
199         lostcon = workarea().selectionLost
200                 .connect(boost::bind(&BufferView::Pimpl::selectionLost, this));
201
202         timecon = cursor_timeout.timeout
203                 .connect(boost::bind(&BufferView::Pimpl::cursorToggle, this));
204         cursor_timeout.start();
205         saved_positions.resize(saved_positions_num);
206 }
207
208
209 WorkArea & BufferView::Pimpl::workarea() const
210 {
211         return *workarea_.get();
212 }
213
214
215 LyXScreen & BufferView::Pimpl::screen() const
216 {
217         return *screen_.get();
218 }
219
220
221 Painter & BufferView::Pimpl::painter() const
222 {
223         return workarea().getPainter();
224 }
225
226
227 void BufferView::Pimpl::buffer(Buffer * b)
228 {
229         lyxerr[Debug::INFO] << "Setting buffer in BufferView ("
230                             << b << ")" << endl;
231         if (buffer_) {
232                 buffer_->delUser(bv_);
233
234                 // Put the old text into the TextCache, but
235                 // only if the buffer is still loaded.
236                 // Also set the owner of the test to 0
237                 //              bv_->text->owner(0);
238                 textcache.add(buffer_, workarea().workWidth(), bv_->text);
239                 if (lyxerr.debugging())
240                         textcache.show(lyxerr, "BufferView::buffer");
241
242                 bv_->text = 0;
243         }
244
245         // set current buffer
246         buffer_ = b;
247
248         if (bufferlist.getState() == BufferList::CLOSING) return;
249
250         // if we are closing the buffer, use the first buffer as current
251         if (!buffer_) {
252                 buffer_ = bufferlist.first();
253         }
254
255         if (buffer_) {
256                 lyxerr[Debug::INFO] << "Buffer addr: " << buffer_ << endl;
257                 buffer_->addUser(bv_);
258
259                 // If we don't have a text object for this, we make one
260                 if (bv_->text == 0) {
261                         resizeCurrentBuffer();
262                 }
263
264                 // FIXME: needed when ?
265                 bv_->text->first_y =
266                         screen().topCursorVisible(bv_->text->cursor, bv_->text->first_y);
267
268                 // Similarly, buffer-dependent dialogs should be updated or
269                 // hidden. This should go here because some dialogs (eg ToC)
270                 // require bv_->text.
271                 owner_->getDialogs().updateBufferDependent(true);
272         } else {
273                 lyxerr[Debug::INFO] << "  No Buffer!" << endl;
274                 owner_->getDialogs().hideBufferDependent();
275
276                 // Also remove all remaining text's from the testcache.
277                 // (there should not be any!) (if there is any it is a
278                 // bug!)
279                 if (lyxerr.debugging())
280                         textcache.show(lyxerr, "buffer delete all");
281                 textcache.clear();
282         }
283
284         repaint();
285         updateScrollbar();
286         owner_->updateMenubar();
287         owner_->updateToolbar();
288         owner_->updateLayoutChoice();
289         owner_->updateWindowTitle();
290
291         if (grfx::Previews::activated() && buffer_)
292                 grfx::Previews::get().generateBufferPreviews(*buffer_);
293 }
294
295
296 bool BufferView::Pimpl::fitCursor()
297 {
298         bool ret;
299
300         if (bv_->theLockingInset()) {
301                 bv_->theLockingInset()->fitInsetCursor(bv_);
302                 ret = true;
303         } else {
304                 ret = screen().fitCursor(bv_->text, bv_);
305         }
306
307         bv_->owner()->getDialogs().updateParagraph();
308         if (ret)
309                 updateScrollbar();
310         return ret;
311 }
312
313
314 void BufferView::Pimpl::redoCurrentBuffer()
315 {
316         lyxerr[Debug::INFO] << "BufferView::redoCurrentBuffer" << endl;
317         if (buffer_ && bv_->text) {
318                 resizeCurrentBuffer();
319                 updateScrollbar();
320                 owner_->updateLayoutChoice();
321                 repaint();
322         }
323 }
324
325
326 int BufferView::Pimpl::resizeCurrentBuffer()
327 {
328         lyxerr[Debug::INFO] << "resizeCurrentBuffer" << endl;
329
330         Paragraph * par = 0;
331         Paragraph * selstartpar = 0;
332         Paragraph * selendpar = 0;
333         UpdatableInset * the_locking_inset = 0;
334
335         pos_type pos = 0;
336         pos_type selstartpos = 0;
337         pos_type selendpos = 0;
338         bool selection = false;
339         bool mark_set  = false;
340
341         owner_->prohibitInput();
342
343         owner_->message(_("Formatting document..."));
344
345         if (bv_->text) {
346                 par = bv_->text->cursor.par();
347                 pos = bv_->text->cursor.pos();
348                 selstartpar = bv_->text->selection.start.par();
349                 selstartpos = bv_->text->selection.start.pos();
350                 selendpar = bv_->text->selection.end.par();
351                 selendpos = bv_->text->selection.end.pos();
352                 selection = bv_->text->selection.set();
353                 mark_set = bv_->text->selection.mark();
354                 the_locking_inset = bv_->theLockingInset();
355                 buffer_->resizeInsets(bv_);
356                 // I don't think the delete and new are necessary here we just could
357                 // call only init! (Jug 20020419)
358                 delete bv_->text;
359                 bv_->text = new LyXText(bv_);
360                 bv_->text->init(bv_);
361         } else {
362                 // See if we have a text in TextCache that fits
363                 // the new buffer_ with the correct width.
364                 bv_->text = textcache.findFit(buffer_, workarea().workWidth());
365                 if (bv_->text) {
366                         if (lyxerr.debugging()) {
367                                 lyxerr << "Found a LyXText that fits:\n";
368                                 textcache.show(lyxerr, make_pair(buffer_, make_pair(workarea().workWidth(), bv_->text)));
369                         }
370                         // Set the owner of the newly found text
371                         //      bv_->text->owner(bv_);
372                         if (lyxerr.debugging())
373                                 textcache.show(lyxerr, "resizeCurrentBuffer");
374                 } else {
375                         bv_->text = new LyXText(bv_);
376                         bv_->text->init(bv_);
377                         //buffer_->resizeInsets(bv_);
378                 }
379         }
380
381         if (par) {
382                 bv_->text->selection.set(true);
383                 // At this point just to avoid the Delete-Empty-Paragraph-
384                 // Mechanism when setting the cursor.
385                 bv_->text->selection.mark(mark_set);
386                 if (selection) {
387                         bv_->text->setCursor(bv_, selstartpar, selstartpos);
388                         bv_->text->selection.cursor = bv_->text->cursor;
389                         bv_->text->setCursor(bv_, selendpar, selendpos);
390                         bv_->text->setSelection(bv_);
391                         bv_->text->setCursor(bv_, par, pos);
392                 } else {
393                         bv_->text->setCursor(bv_, par, pos);
394                         bv_->text->selection.cursor = bv_->text->cursor;
395                         bv_->text->selection.set(false);
396                 }
397                 // remake the inset locking
398                 bv_->theLockingInset(the_locking_inset);
399         }
400
401         bv_->text->first_y = screen().topCursorVisible(bv_->text->cursor, bv_->text->first_y);
402
403         switchKeyMap();
404         owner_->allowInput();
405
406         updateScrollbar();
407
408         return 0;
409 }
410
411
412 void BufferView::Pimpl::repaint()
413 {
414         // Regenerate the screen.
415         screen().redraw(bv_->text, bv_);
416 }
417
418
419 void BufferView::Pimpl::updateScrollbar()
420 {
421         if (!bv_->text) {
422                 lyxerr[Debug::GUI] << "no text in updateScrollbar" << endl;
423                 workarea().setScrollbarParams(0, 0, 0);
424                 return;
425         }
426
427         LyXText const & t = *bv_->text;
428
429         lyxerr[Debug::GUI] << "Updating scrollbar: h " << t.height << ", first_y "
430                 << t.first_y << ", default height " << t.defaultHeight() << endl;
431
432         workarea().setScrollbarParams(t.height, t.first_y, t.defaultHeight());
433 }
434
435
436 void BufferView::Pimpl::scrollDocView(int value)
437 {
438         lyxerr[Debug::GUI] << "scrollDocView of " << value << endl;
439
440         if (!buffer_) return;
441
442         screen().draw(bv_->text, bv_, value);
443
444         if (!lyxrc.cursor_follows_scrollbar) {
445                 return;
446         }
447
448         LyXText * vbt = bv_->text;
449
450         int const height = vbt->defaultHeight();
451         int const first = static_cast<int>((bv_->text->first_y + height));
452         int const last = static_cast<int>((bv_->text->first_y + workarea().workHeight() - height));
453
454         if (vbt->cursor.y() < first)
455                 vbt->setCursorFromCoordinates(bv_, 0, first);
456         else if (vbt->cursor.y() > last)
457                 vbt->setCursorFromCoordinates(bv_, 0, last);
458 }
459
460
461 int BufferView::Pimpl::scroll(long time)
462 {
463         if (!buffer_)
464                 return 0;
465
466         LyXText const * t = bv_->text;
467
468         double const diff = t->defaultHeight()
469                 + double(time) * double(time) * 0.125;
470
471         scrollDocView(int(diff));
472         workarea().setScrollbarParams(t->height, t->first_y, t->defaultHeight());
473         return 0;
474 }
475
476
477 void BufferView::Pimpl::workAreaKeyPress(LyXKeySymPtr key,
478                                          key_modifier::state state)
479 {
480         bv_->owner()->getLyXFunc().processKeySym(key, state);
481 }
482
483
484 void BufferView::Pimpl::selectionRequested()
485 {
486         static string sel;
487
488         if (!available())
489                 return;
490
491         LyXText * text = bv_->getLyXText();
492
493         if (text->selection.set() &&
494                 (!bv_->text->xsel_cache.set() ||
495                  text->selection.start != bv_->text->xsel_cache.start ||
496                  text->selection.end != bv_->text->xsel_cache.end))
497         {
498                 bv_->text->xsel_cache = text->selection;
499                 sel = text->selectionAsString(bv_->buffer(), false);
500         } else if (!text->selection.set()) {
501                 sel = string();
502                 bv_->text->xsel_cache.set(false);
503         }
504         if (!sel.empty()) {
505                 workarea().putClipboard(sel);
506         }
507 }
508
509
510 void BufferView::Pimpl::selectionLost()
511 {
512         if (available()) {
513                 hideCursor();
514                 toggleSelection();
515                 bv_->getLyXText()->clearSelection();
516                 showCursor();
517                 bv_->text->xsel_cache.set(false);
518         }
519 }
520
521
522
523 Inset * BufferView::Pimpl::checkInsetHit(LyXText * text, int & x, int & y)
524 {
525         int y_tmp = y + text->first_y;
526
527         LyXCursor cursor;
528         text->setCursorFromCoordinates(bv_, cursor, x, y_tmp);
529
530         Inset * inset = checkInset(bv_, *text, cursor, x, y_tmp);
531
532         if (inset) {
533                 y = y_tmp;
534                 return inset;
535         }
536
537         // look at previous position
538         if (cursor.pos() == 0) {
539                 return 0;
540         }
541
542         // move back one
543         text->setCursor(bv_, cursor, cursor.par(), cursor.pos() - 1, true);
544
545         inset = checkInset(bv_, *text, cursor, x, y_tmp);
546         if (inset) {
547                 y = y_tmp;
548         }
549         return inset;
550 }
551
552
553 void BufferView::Pimpl::workAreaResize()
554 {
555         static int work_area_width;
556         static int work_area_height;
557
558         bool const widthChange = workarea().workWidth() != work_area_width;
559         bool const heightChange = workarea().workHeight() != work_area_height;
560
561         // update from work area
562         work_area_width = workarea().workWidth();
563         work_area_height = workarea().workHeight();
564
565         if (buffer_ != 0) {
566                 if (widthChange) {
567                         // The visible LyXView need a resize
568                         resizeCurrentBuffer();
569
570                         // Remove all texts from the textcache
571                         // This is not _really_ what we want to do. What
572                         // we really want to do is to delete in textcache
573                         // that does not have a BufferView with matching
574                         // width, but as long as we have only one BufferView
575                         // deleting all gives the same result.
576                         if (lyxerr.debugging())
577                                 textcache.show(lyxerr, "Expose delete all");
578                         textcache.clear();
579                         // FIXME: this is already done in resizeCurrentBuffer() ??
580                         buffer_->resizeInsets(bv_);
581                 } else if (heightChange) {
582                         // fitCursor() ensures we don't jump back
583                         // to the start of the document on vertical
584                         // resize
585                         fitCursor();
586                 }
587         }
588
589         if (widthChange || heightChange) {
590                 repaint();
591         }
592
593         // always make sure that the scrollbar is sane.
594         updateScrollbar();
595         owner_->updateLayoutChoice();
596         return;
597 }
598
599
600 void BufferView::Pimpl::update()
601 {
602         if (!bv_->theLockingInset() || !bv_->theLockingInset()->nodraw()) {
603                 LyXText::text_status st = bv_->text->status();
604                 screen().update(bv_->text, bv_);
605                 bool fitc = false;
606                 while (bv_->text->status() == LyXText::CHANGED_IN_DRAW) {
607                         bv_->text->fullRebreak(bv_);
608                         st = LyXText::NEED_MORE_REFRESH;
609                         bv_->text->setCursor(bv_, bv_->text->cursor.par(),
610                                              bv_->text->cursor.pos());
611                         if (bv_->text->selection.set()) {
612                                 bv_->text->setCursor(bv_, bv_->text->selection.start,
613                                                      bv_->text->selection.start.par(),
614                                                      bv_->text->selection.start.pos());
615                                 bv_->text->setCursor(bv_, bv_->text->selection.end,
616                                                      bv_->text->selection.end.par(),
617                                                      bv_->text->selection.end.pos());
618                         }
619                         fitc = true;
620                         bv_->text->status(bv_, st);
621                         screen().update(bv_->text, bv_);
622                 }
623                 // do this here instead of in the screen::update because of
624                 // the above loop!
625                 bv_->text->status(bv_, LyXText::UNCHANGED);
626                 if (fitc)
627                         fitCursor();
628         }
629 }
630
631 // Values used when calling update:
632 // -3 - update
633 // -2 - update, move sel_cursor if selection, fitcursor
634 // -1 - update, move sel_cursor if selection, fitcursor, mark dirty
635 //  0 - update, move sel_cursor if selection, fitcursor
636 //  1 - update, move sel_cursor if selection, fitcursor, mark dirty
637 //  3 - update, move sel_cursor if selection
638 //
639 // update -
640 // a simple redraw of the parts that need refresh
641 //
642 // move sel_cursor if selection -
643 // the text's sel_cursor is moved if there is selection is progress
644 //
645 // fitcursor -
646 // fitCursor() is called and the scrollbar updated
647 //
648 // mark dirty -
649 // the buffer is marked dirty.
650 //
651 // enum {
652 //       UPDATE = 0,
653 //       SELECT = 1,
654 //       FITCUR = 2,
655 //       CHANGE = 4
656 // };
657 //
658 // UPDATE_ONLY = UPDATE;
659 // UPDATE_SELECT = UPDATE | SELECT;
660 // UPDATE_SELECT_MOVE = UPDATE | SELECT | FITCUR;
661 // UPDATE_SELECT_MOVE_AFTER_CHANGE = UPDATE | SELECT | FITCUR | CHANGE;
662 //
663 // update(-3) -> update(0)         -> update(0) -> update(UPDATE)
664 // update(-2) -> update(1 + 2)     -> update(3) -> update(SELECT|FITCUR)
665 // update(-1) -> update(1 + 2 + 4) -> update(7) -> update(SELECT|FITCUR|CHANGE)
666 // update(1)  -> update(1 + 2 + 4) -> update(7) -> update(SELECT|FITCUR|CHANGE)
667 // update(3)  -> update(1)         -> update(1) -> update(SELECT)
668
669 void BufferView::Pimpl::update(LyXText * text, BufferView::UpdateCodes f)
670 {
671         owner_->updateLayoutChoice();
672
673         if (!text->selection.set() && (f & SELECT)) {
674                 text->selection.cursor = text->cursor;
675         }
676
677         text->fullRebreak(bv_);
678
679         if (text->inset_owner) {
680                 text->inset_owner->setUpdateStatus(bv_, InsetText::NONE);
681                 updateInset(text->inset_owner, false);
682         } else {
683                 update();
684         }
685
686         if ((f & FITCUR)) {
687                 fitCursor();
688         }
689
690         if ((f & CHANGE)) {
691                 buffer_->markDirty();
692         }
693 }
694
695
696 // Callback for cursor timer
697 void BufferView::Pimpl::cursorToggle()
698 {
699         if (!buffer_) {
700                 cursor_timeout.restart();
701                 return;
702         }
703
704         /* FIXME */
705         extern void reapSpellchecker(void);
706         reapSpellchecker();
707
708         if (!bv_->theLockingInset()) {
709                 screen().cursorToggle(bv_);
710         } else {
711                 bv_->theLockingInset()->toggleInsetCursor(bv_);
712         }
713
714         cursor_timeout.restart();
715 }
716
717
718 bool BufferView::Pimpl::available() const
719 {
720         if (buffer_ && bv_->text)
721                 return true;
722         return false;
723 }
724
725
726 void BufferView::Pimpl::beforeChange(LyXText * text)
727 {
728         toggleSelection();
729         text->clearSelection();
730 }
731
732
733 void BufferView::Pimpl::savePosition(unsigned int i)
734 {
735         if (i >= saved_positions_num)
736                 return;
737         saved_positions[i] = Position(buffer_->fileName(),
738                                       bv_->text->cursor.par()->id(),
739                                       bv_->text->cursor.pos());
740         if (i > 0) {
741                 ostringstream str;
742                 str << _("Saved bookmark") << ' ' << i;
743                 owner_->message(str.str().c_str());
744         }
745 }
746
747
748 void BufferView::Pimpl::restorePosition(unsigned int i)
749 {
750         if (i >= saved_positions_num)
751                 return;
752
753         string const fname = saved_positions[i].filename;
754
755         beforeChange(bv_->text);
756
757         if (fname != buffer_->fileName()) {
758                 Buffer * b = bufferlist.exists(fname) ?
759                         bufferlist.getBuffer(fname) :
760                         bufferlist.loadLyXFile(fname); // don't ask, just load it
761                 if (b != 0) buffer(b);
762         }
763
764         Paragraph * par = buffer_->getParFromID(saved_positions[i].par_id);
765         if (!par)
766                 return;
767
768         bv_->text->setCursor(bv_, par,
769                              min(par->size(), saved_positions[i].par_pos));
770
771         update(bv_->text, BufferView::SELECT | BufferView::FITCUR);
772         if (i > 0) {
773                 ostringstream str;
774                 str << _("Moved to bookmark") << ' ' << i;
775                 owner_->message(str.str().c_str());
776         }
777 }
778
779
780 bool BufferView::Pimpl::isSavedPosition(unsigned int i)
781 {
782         if (i >= saved_positions_num)
783                 return false;
784
785         return !saved_positions[i].filename.empty();
786 }
787
788
789 void BufferView::Pimpl::switchKeyMap()
790 {
791         if (!lyxrc.rtl_support)
792                 return;
793
794         LyXText * text = bv_->getLyXText();
795         if (text->real_current_font.isRightToLeft()
796             && !(bv_->theLockingInset()
797                  && bv_->theLockingInset()->lyxCode() == Inset::ERT_CODE))
798         {
799                 if (owner_->getIntl().keymap == Intl::PRIMARY)
800                         owner_->getIntl().KeyMapSec();
801         } else {
802                 if (owner_->getIntl().keymap == Intl::SECONDARY)
803                         owner_->getIntl().KeyMapPrim();
804         }
805 }
806
807
808 void BufferView::Pimpl::insetUnlock()
809 {
810         if (bv_->theLockingInset()) {
811                 bv_->theLockingInset()->insetUnlock(bv_);
812                 bv_->theLockingInset(0);
813                 finishUndo();
814         }
815 }
816
817
818 void BufferView::Pimpl::showCursor()
819 {
820         if (bv_->theLockingInset())
821                 bv_->theLockingInset()->showInsetCursor(bv_);
822         else
823                 screen().showCursor(bv_->text, bv_);
824 }
825
826
827 void BufferView::Pimpl::hideCursor()
828 {
829         if (!bv_->theLockingInset())
830                 screen().hideCursor();
831 }
832
833
834 void BufferView::Pimpl::toggleSelection(bool b)
835 {
836         if (bv_->theLockingInset())
837                 bv_->theLockingInset()->toggleSelection(bv_, b);
838         screen().toggleSelection(bv_->text, bv_, b);
839 }
840
841
842 void BufferView::Pimpl::toggleToggle()
843 {
844         screen().toggleToggle(bv_->text, bv_);
845 }
846
847
848 void BufferView::Pimpl::center()
849 {
850         LyXText * t = bv_->text;
851
852         beforeChange(t);
853         int const half_height = workarea().workHeight() / 2;
854         int new_y = 0;
855
856         if (t->cursor.y() > half_height) {
857                 new_y = t->cursor.y() - half_height;
858         }
859
860         // FIXME: can we do this w/o calling screen directly ?
861         // This updates first_y but means the fitCursor() call
862         // from the update(FITCUR) doesn't realise that we might
863         // have moved (e.g. from GOTOPARAGRAPH), so doesn't cause
864         // the scrollbar to be updated as it should, so we have
865         // to do it manually. Any operation that does a center()
866         // and also might have moved first_y must make sure to call
867         // updateScrollbar() currently. Never mind that this is a
868         // pretty obfuscated way of updating t->first_y
869         screen().draw(t, bv_, new_y);
870
871         update(t, BufferView::SELECT | BufferView::FITCUR);
872 }
873
874
875 void BufferView::Pimpl::stuffClipboard(string const & stuff) const
876 {
877         workarea().putClipboard(stuff);
878 }
879
880
881 /*
882  * Dispatch functions for actions which can be valid for BufferView->text
883  * and/or InsetText->text!!!
884  */
885
886
887 Inset * BufferView::Pimpl::getInsetByCode(Inset::Code code)
888 {
889 #if 0
890         LyXCursor cursor = bv_->getLyXText()->cursor;
891         Buffer::inset_iterator it =
892                 find_if(Buffer::inset_iterator(
893                         cursor.par(), cursor.pos()),
894                         buffer_->inset_iterator_end(),
895                         lyx::compare_memfun(&Inset::lyxCode, code));
896         return it != buffer_->inset_iterator_end() ? (*it) : 0;
897 #else
898         // Ok, this is a little bit too brute force but it
899         // should work for now. Better infrastructure is comming. (Lgb)
900
901         Buffer * b = bv_->buffer();
902         LyXCursor cursor = bv_->getLyXText()->cursor;
903
904         Buffer::inset_iterator beg = b->inset_iterator_begin();
905         Buffer::inset_iterator end = b->inset_iterator_end();
906
907         bool cursor_par_seen = false;
908
909         for (; beg != end; ++beg) {
910                 if (beg.getPar() == cursor.par()) {
911                         cursor_par_seen = true;
912                 }
913                 if (cursor_par_seen) {
914                         if (beg.getPar() == cursor.par()
915                             && beg.getPos() >= cursor.pos()) {
916                                 break;
917                         } else if (beg.getPar() != cursor.par()) {
918                                 break;
919                         }
920                 }
921
922         }
923         if (beg != end) {
924                 // Now find the first inset that matches code.
925                 for (; beg != end; ++beg) {
926                         if (beg->lyxCode() == code) {
927                                 return &(*beg);
928                         }
929                 }
930         }
931         return 0;
932 #endif
933 }
934
935
936 void BufferView::Pimpl::MenuInsertLyXFile(string const & filen)
937 {
938         string filename = filen;
939
940         if (filename.empty()) {
941                 // Launch a file browser
942                 string initpath = lyxrc.document_path;
943
944                 if (available()) {
945                         string const trypath = owner_->buffer()->filePath();
946                         // If directory is writeable, use this as default.
947                         if (IsDirWriteable(trypath))
948                                 initpath = trypath;
949                 }
950
951                 FileDialog fileDlg(bv_->owner(),
952                                    _("Select LyX document to insert"),
953                         LFUN_FILE_INSERT,
954                         make_pair(string(_("Documents|#o#O")),
955                                   string(lyxrc.document_path)),
956                         make_pair(string(_("Examples|#E#e")),
957                                   string(AddPath(system_lyxdir, "examples"))));
958
959                 FileDialog::Result result =
960                         fileDlg.Select(initpath,
961                                        _("*.lyx| LyX Documents (*.lyx)"));
962
963                 if (result.first == FileDialog::Later)
964                         return;
965
966                 filename = result.second;
967
968                 // check selected filename
969                 if (filename.empty()) {
970                         owner_->message(_("Canceled."));
971                         return;
972                 }
973         }
974
975         // get absolute path of file and add ".lyx" to the filename if
976         // necessary
977         filename = FileSearch(string(), filename, "lyx");
978
979         string const disp_fn(MakeDisplayPath(filename));
980
981         ostringstream s1;
982         s1 << _("Inserting document") << ' '
983            << disp_fn << " ...";
984         owner_->message(s1.str().c_str());
985         bool const res = bv_->insertLyXFile(filename);
986         if (res) {
987                 ostringstream str;
988                 str << _("Document") << ' ' << disp_fn
989                     << ' ' << _("inserted.");
990                 owner_->message(str.str().c_str());
991         } else {
992                 ostringstream str;
993                 str << _("Could not insert document") << ' '
994                     << disp_fn;
995                 owner_->message(str.str().c_str());
996         }
997 }
998
999
1000 bool BufferView::Pimpl::dispatch(FuncRequest const & ev)
1001 {
1002         lyxerr[Debug::ACTION] << "BufferView::Pimpl::Dispatch:"
1003                 << " action[" << ev.action <<"]"
1004                 << " arg[" << ev.argument << "]"
1005                 << " x[" << ev.x << "]"
1006                 << " y[" << ev.y << "]"
1007                 << " button[" << ev.button() << "]"
1008                 << endl;
1009
1010         LyXTextClass const & tclass = buffer_->params.getLyXTextClass();
1011
1012         switch (ev.action) {
1013
1014         case LFUN_TOC_INSERT:
1015         {
1016                 InsetCommandParams p;
1017                 p.setCmdName("tableofcontents");
1018                 Inset * inset = new InsetTOC(p);
1019                 if (!insertInset(inset, tclass.defaultLayoutName()))
1020                         delete inset;
1021                 break;
1022         }
1023
1024         case LFUN_SCROLL_INSET:
1025                 // this is not handled here as this function is only active
1026                 // if we have a locking_inset and that one is (or contains)
1027                 // a tabular-inset
1028                 break;
1029
1030         case LFUN_INSET_GRAPHICS:
1031         {
1032                 Inset * new_inset = new InsetGraphics;
1033                 if (!insertInset(new_inset)) {
1034                         delete new_inset;
1035                 } else {
1036                         // this is need because you don't use a inset->Edit()
1037                         updateInset(new_inset, true);
1038                         new_inset->edit(bv_);
1039                 }
1040                 break;
1041         }
1042
1043         case LFUN_LAYOUT_COPY:
1044                 bv_->copyEnvironment();
1045                 break;
1046
1047         case LFUN_LAYOUT_PASTE:
1048                 bv_->pasteEnvironment();
1049                 switchKeyMap();
1050                 break;
1051
1052         case LFUN_DEPTH_MIN:
1053                 changeDepth(bv_, bv_->getLyXText(), -1);
1054                 break;
1055
1056         case LFUN_DEPTH_PLUS:
1057                 changeDepth(bv_, bv_->getLyXText(), 1);
1058                 break;
1059
1060         case LFUN_FREE:
1061                 owner_->getDialogs().setUserFreeFont();
1062                 break;
1063
1064         case LFUN_FILE_INSERT:
1065                 MenuInsertLyXFile(ev.argument);
1066                 break;
1067
1068         case LFUN_FILE_INSERT_ASCII_PARA:
1069                 InsertAsciiFile(bv_, ev.argument, true);
1070                 break;
1071
1072         case LFUN_FILE_INSERT_ASCII:
1073                 InsertAsciiFile(bv_, ev.argument, false);
1074                 break;
1075
1076         case LFUN_LANGUAGE:
1077                 lang(bv_, ev.argument);
1078                 switchKeyMap();
1079                 owner_->view_state_changed();
1080                 break;
1081
1082         case LFUN_EMPH:
1083                 emph(bv_);
1084                 owner_->view_state_changed();
1085                 break;
1086
1087         case LFUN_BOLD:
1088                 bold(bv_);
1089                 owner_->view_state_changed();
1090                 break;
1091
1092         case LFUN_NOUN:
1093                 noun(bv_);
1094                 owner_->view_state_changed();
1095                 break;
1096
1097         case LFUN_CODE:
1098                 code(bv_);
1099                 owner_->view_state_changed();
1100                 break;
1101
1102         case LFUN_SANS:
1103                 sans(bv_);
1104                 owner_->view_state_changed();
1105                 break;
1106
1107         case LFUN_ROMAN:
1108                 roman(bv_);
1109                 owner_->view_state_changed();
1110                 break;
1111
1112         case LFUN_DEFAULT:
1113                 styleReset(bv_);
1114                 owner_->view_state_changed();
1115                 break;
1116
1117         case LFUN_UNDERLINE:
1118                 underline(bv_);
1119                 owner_->view_state_changed();
1120                 break;
1121
1122         case LFUN_FONT_SIZE:
1123                 fontSize(bv_, ev.argument);
1124                 owner_->view_state_changed();
1125                 break;
1126
1127         case LFUN_FONT_STATE:
1128                 owner_->getLyXFunc().setMessage(currentState(bv_));
1129                 break;
1130
1131         case LFUN_INSERT_LABEL:
1132                 MenuInsertLabel(bv_, ev.argument);
1133                 break;
1134
1135         case LFUN_REF_INSERT:
1136                 if (ev.argument.empty()) {
1137                         InsetCommandParams p("ref");
1138                         owner_->getDialogs().createRef(p.getAsString());
1139                 } else {
1140                         InsetCommandParams p;
1141                         p.setFromString(ev.argument);
1142
1143                         InsetRef * inset = new InsetRef(p, *buffer_);
1144                         if (!insertInset(inset))
1145                                 delete inset;
1146                         else
1147                                 updateInset(inset, true);
1148                 }
1149                 break;
1150
1151         case LFUN_BOOKMARK_SAVE:
1152                 savePosition(strToUnsignedInt(ev.argument));
1153                 break;
1154
1155         case LFUN_BOOKMARK_GOTO:
1156                 restorePosition(strToUnsignedInt(ev.argument));
1157                 break;
1158
1159         case LFUN_REF_GOTO:
1160         {
1161                 string label = ev.argument;
1162                 if (label.empty()) {
1163                         InsetRef * inset =
1164                                 static_cast<InsetRef*>(getInsetByCode(Inset::REF_CODE));
1165                         if (inset) {
1166                                 label = inset->getContents();
1167                                 savePosition(0);
1168                         }
1169                 }
1170
1171                 if (!label.empty()) {
1172                         //bv_->savePosition(0);
1173                         if (!bv_->gotoLabel(label))
1174                                 Alert::alert(_("Error"),
1175                                            _("Couldn't find this label"),
1176                                            _("in current document."));
1177                 }
1178         }
1179         break;
1180
1181         case LFUN_HTMLURL:
1182         case LFUN_URL:
1183         {
1184                 InsetCommandParams p;
1185                 if (ev.action == LFUN_HTMLURL)
1186                         p.setCmdName("htmlurl");
1187                 else
1188                         p.setCmdName("url");
1189                 owner_->getDialogs().createUrl(p.getAsString());
1190         }
1191         break;
1192
1193         case LFUN_INSERT_URL:
1194         {
1195                 InsetCommandParams p;
1196                 p.setFromString(ev.argument);
1197
1198                 InsetUrl * inset = new InsetUrl(p);
1199                 if (!insertInset(inset))
1200                         delete inset;
1201                 else
1202                         updateInset(inset, true);
1203         }
1204         break;
1205
1206         case LFUN_INSET_CAPTION:
1207         {
1208                 // Do we have a locking inset...
1209                 if (bv_->theLockingInset()) {
1210                         lyxerr << "Locking inset code: "
1211                                << static_cast<int>(bv_->theLockingInset()->lyxCode());
1212                         InsetCaption * new_inset =
1213                                 new InsetCaption(buffer_->params);
1214                         new_inset->setOwner(bv_->theLockingInset());
1215                         new_inset->setAutoBreakRows(true);
1216                         new_inset->setDrawFrame(0, InsetText::LOCKED);
1217                         new_inset->setFrameColor(0, LColor::captionframe);
1218                         if (insertInset(new_inset))
1219                                 new_inset->edit(bv_);
1220                         else
1221                                 delete new_inset;
1222                 }
1223         }
1224         break;
1225
1226
1227         // --- accented characters ---------------------------
1228
1229         case LFUN_UMLAUT:
1230         case LFUN_CIRCUMFLEX:
1231         case LFUN_GRAVE:
1232         case LFUN_ACUTE:
1233         case LFUN_TILDE:
1234         case LFUN_CEDILLA:
1235         case LFUN_MACRON:
1236         case LFUN_DOT:
1237         case LFUN_UNDERDOT:
1238         case LFUN_UNDERBAR:
1239         case LFUN_CARON:
1240         case LFUN_SPECIAL_CARON:
1241         case LFUN_BREVE:
1242         case LFUN_TIE:
1243         case LFUN_HUNG_UMLAUT:
1244         case LFUN_CIRCLE:
1245         case LFUN_OGONEK:
1246                 if (ev.argument.empty()) {
1247                         // As always...
1248                         owner_->getLyXFunc().handleKeyFunc(ev.action);
1249                 } else {
1250                         owner_->getLyXFunc().handleKeyFunc(ev.action);
1251                         owner_->getIntl().getTransManager()
1252                                 .TranslateAndInsert(ev.argument[0], bv_->getLyXText());
1253                         update(bv_->getLyXText(),
1254                                BufferView::SELECT
1255                                | BufferView::FITCUR
1256                                | BufferView::CHANGE);
1257                 }
1258                 break;
1259
1260         case LFUN_MATH_MACRO:
1261         case LFUN_MATH_DELIM:
1262         case LFUN_INSERT_MATRIX:
1263         case LFUN_INSERT_MATH:
1264         case LFUN_MATH_IMPORT_SELECTION: // Imports LaTeX from the X selection
1265         case LFUN_MATH_DISPLAY:          // Open or create a displayed math inset
1266         case LFUN_MATH_MODE:             // Open or create an inlined math inset
1267         case LFUN_GREEK:                 // Insert a single greek letter
1268                 mathDispatch(FuncRequest(bv_, ev.action, ev.argument));
1269                 break;
1270
1271         case LFUN_CITATION_INSERT:
1272         {
1273                 InsetCommandParams p;
1274                 p.setFromString(ev.argument);
1275
1276                 InsetCitation * inset = new InsetCitation(p);
1277                 if (!insertInset(inset))
1278                         delete inset;
1279                 else
1280                         updateInset(inset, true);
1281         }
1282         break;
1283
1284         case LFUN_INSERT_BIBTEX:
1285         {
1286                 // ale970405+lasgoutt970425
1287                 // The argument can be up to two tokens separated
1288                 // by a space. The first one is the bibstyle.
1289                 string const db = token(ev.argument, ' ', 0);
1290                 string bibstyle = token(ev.argument, ' ', 1);
1291                 if (bibstyle.empty())
1292                         bibstyle = "plain";
1293
1294                 InsetCommandParams p("BibTeX", db, bibstyle);
1295                 InsetBibtex * inset = new InsetBibtex(p);
1296
1297                 if (insertInset(inset)) {
1298                         if (ev.argument.empty())
1299                                 inset->edit(bv_);
1300                 } else
1301                         delete inset;
1302         }
1303         break;
1304
1305         // BibTeX data bases
1306         case LFUN_BIBDB_ADD:
1307         {
1308                 InsetBibtex * inset =
1309                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
1310                 if (inset) {
1311                         inset->addDatabase(ev.argument);
1312                 }
1313         }
1314         break;
1315
1316         case LFUN_BIBDB_DEL:
1317         {
1318                 InsetBibtex * inset =
1319                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
1320                 if (inset) {
1321                         inset->delDatabase(ev.argument);
1322                 }
1323         }
1324         break;
1325
1326         case LFUN_BIBTEX_STYLE:
1327         {
1328                 InsetBibtex * inset =
1329                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
1330                 if (inset) {
1331                         inset->setOptions(ev.argument);
1332                 }
1333         }
1334         break;
1335
1336         case LFUN_INDEX_INSERT:
1337         {
1338                 string entry = ev.argument;
1339                 if (entry.empty())
1340                         entry = bv_->getLyXText()->getStringToIndex(bv_);
1341
1342                 if (entry.empty()) {
1343                         owner_->getDialogs().createIndex();
1344                         break;
1345                 }
1346
1347                 InsetIndex * inset = new InsetIndex(InsetCommandParams("index", entry));
1348
1349                 if (!insertInset(inset)) {
1350                         delete inset;
1351                 } else {
1352                         updateInset(inset, true);
1353                 }
1354         }
1355         break;
1356
1357         case LFUN_INDEX_PRINT:
1358         {
1359                 InsetCommandParams p("printindex");
1360                 Inset * inset = new InsetPrintIndex(p);
1361                 if (!insertInset(inset, tclass.defaultLayoutName()))
1362                         delete inset;
1363         }
1364         break;
1365
1366         case LFUN_PARENTINSERT:
1367         {
1368                 InsetCommandParams p("lyxparent", ev.argument);
1369                 Inset * inset = new InsetParent(p, *buffer_);
1370                 if (!insertInset(inset, tclass.defaultLayoutName()))
1371                         delete inset;
1372         }
1373
1374         break;
1375
1376         case LFUN_CHILD_INSERT:
1377         {
1378                 InsetInclude::Params p;
1379                 p.cparams.setFromString(ev.argument);
1380                 p.masterFilename_ = buffer_->fileName();
1381
1382                 InsetInclude * inset = new InsetInclude(p);
1383                 if (!insertInset(inset))
1384                         delete inset;
1385                 else {
1386                         updateInset(inset, true);
1387                         bv_->owner()->getDialogs().showInclude(inset);
1388                 }
1389         }
1390         break;
1391
1392         case LFUN_FLOAT_LIST:
1393                 if (tclass.floats().typeExist(ev.argument)) {
1394                         Inset * inset = new InsetFloatList(ev.argument);
1395                         if (!insertInset(inset, tclass.defaultLayoutName()))
1396                                 delete inset;
1397                 } else {
1398                         lyxerr << "Non-existent float type: "
1399                                << ev.argument << endl;
1400                 }
1401                 break;
1402
1403         case LFUN_THESAURUS_ENTRY:
1404         {
1405                 string arg = ev.argument;
1406
1407                 if (arg.empty()) {
1408                         arg = bv_->getLyXText()->selectionAsString(buffer_,
1409                                                                    false);
1410
1411                         // FIXME
1412                         if (arg.size() > 100 || arg.empty()) {
1413                                 // Get word or selection
1414                                 bv_->getLyXText()->selectWordWhenUnderCursor(bv_, LyXText::WHOLE_WORD);
1415                                 arg = bv_->getLyXText()->selectionAsString(buffer_, false);
1416                                 // FIXME: where is getLyXText()->unselect(bv_) ?
1417                         }
1418                 }
1419
1420                 bv_->owner()->getDialogs().showThesaurus(arg);
1421         }
1422                 break;
1423
1424         case LFUN_UNKNOWN_ACTION:
1425                 ev.errorMessage(N_("Unknown function!"));
1426                 break;
1427
1428         case LFUN_MOUSE_TRIPLE:
1429         {
1430                 if (!buffer_)
1431                         return false;
1432
1433                 LyXText * text = bv_->getLyXText();
1434
1435                 if (text->bv_owner && bv_->theLockingInset())
1436                         return false;
1437
1438                 if (ev.button() == mouse_button::button1) {
1439                         if (text->bv_owner) {
1440                                 screen().hideCursor();
1441                                 screen().toggleSelection(text, bv_);
1442                         }
1443                         text->cursorHome(bv_);
1444                         text->selection.cursor = text->cursor;
1445                         text->cursorEnd(bv_);
1446                         text->setSelection(bv_);
1447                         if (text->bv_owner)
1448                                 screen().toggleSelection(text, bv_, false);
1449                         // This will fit the cursor on the screen if necessary
1450                         update(text, BufferView::SELECT|BufferView::FITCUR);
1451                         workarea().haveSelection(bv_->getLyXText()->selection.set());
1452                 }
1453                 break;
1454         }
1455
1456         case LFUN_MOUSE_DOUBLE:
1457         {
1458                 if (!buffer_)
1459                         return false;
1460
1461                 LyXText * text = bv_->getLyXText();
1462
1463                 if (text->bv_owner && bv_->theLockingInset())
1464                         return false;
1465
1466                 if (ev.button() == mouse_button::button1) {
1467                         if (text->bv_owner) {
1468                                 screen().hideCursor();
1469                                 screen().toggleSelection(text, bv_);
1470                                 text->selectWord(bv_, LyXText::WHOLE_WORD_STRICT);
1471                                 screen().toggleSelection(text, bv_, false);
1472                         } else {
1473                                 text->selectWord(bv_, LyXText::WHOLE_WORD_STRICT);
1474                         }
1475                         // This will fit the cursor on the screen if necessary
1476                         update(text, BufferView::SELECT|BufferView::FITCUR);
1477                         workarea().haveSelection(bv_->getLyXText()->selection.set());
1478                 }
1479                 break;
1480         }
1481
1482         case LFUN_MOUSE_MOTION:
1483         {
1484                 // Only use motion with button 1
1485                 //if (ev.button() != mouse_button::button1)
1486                 //      return false;
1487
1488                 if (!buffer_)
1489                         return false;
1490
1491                 // Check for inset locking
1492                 if (bv_->theLockingInset()) {
1493                         LyXCursor cursor = bv_->text->cursor;
1494                         LyXFont font = bv_->text->getFont(buffer_,
1495                                                                 cursor.par(), cursor.pos());
1496                         int width = bv_->theLockingInset()->width(bv_, font);
1497                         int inset_x = font.isVisibleRightToLeft()
1498                                 ? cursor.ix() - width : cursor.ix();
1499                         int start_x = inset_x + bv_->theLockingInset()->scroll();
1500
1501                         FuncRequest cmd(bv_, LFUN_MOUSE_MOTION,
1502                           ev.x - start_x, ev.y - cursor.iy() + bv_->text->first_y, ev.button());
1503                         bv_->theLockingInset()->localDispatch(cmd);
1504                         return false;
1505                 }
1506
1507                 // The test for not selection possible is needed, that only motion
1508                 // events are used, where the bottom press event was on
1509                 //  the drawing area too
1510                 if (!selection_possible) {
1511                         lyxerr[Debug::ACTION]
1512                                 << "BufferView::Pimpl::Dispatch: no selection possible\n";
1513                         return false;
1514                 }
1515
1516                 screen().hideCursor();
1517
1518                 Row * cursorrow = bv_->text->cursor.row();
1519                 bv_->text->setCursorFromCoordinates(bv_, ev.x, ev.y + bv_->text->first_y);
1520         #if 0
1521                 // sorry for this but I have a strange error that the y value jumps at
1522                 // a certain point. This seems like an error in my xforms library or
1523                 // in some other local environment, but I would like to leave this here
1524                 // for the moment until I can remove this (Jug 20020418)
1525                 if (y_before < bv_->text->cursor.y())
1526                         lyxerr << y_before << ":" << bv_->text->cursor.y() << endl;
1527         #endif
1528                 // This is to allow jumping over large insets
1529                 if (cursorrow == bv_->text->cursor.row()) {
1530                         if (ev.y >= int(workarea().workHeight())) {
1531                                 bv_->text->cursorDown(bv_, false);
1532                         } else if (ev.y < 0) {
1533                                 bv_->text->cursorUp(bv_, false);
1534                         }
1535                 }
1536
1537                 if (!bv_->text->selection.set())
1538                         update(bv_->text, BufferView::UPDATE); // Maybe an empty line was deleted
1539
1540                 bv_->text->setSelection(bv_);
1541                 screen().toggleToggle(bv_->text, bv_);
1542                 fitCursor();
1543                 showCursor();
1544                 break;
1545         }
1546
1547         // Single-click on work area
1548         case LFUN_MOUSE_PRESS:
1549         {
1550                 if (!buffer_)
1551                         return false;
1552
1553                 // ok ok, this is a hack (for xforms)
1554                 if (ev.button() == mouse_button::button4) {
1555                         scroll(-lyxrc.wheel_jump);
1556                         // We shouldn't go further down as we really should only do the
1557                         // scrolling and be done with this. Otherwise we may open some
1558                         // dialogs (Jug 20020424).
1559                         return false;
1560                 }
1561                 if (ev.button() == mouse_button::button5) {
1562                         scroll(lyxrc.wheel_jump);
1563                         // We shouldn't go further down as we really should only do the
1564                         // scrolling and be done with this. Otherwise we may open some
1565                         // dialogs (Jug 20020424).
1566                         return false;
1567                 }
1568
1569                 int x = ev.x;
1570                 int y = ev.y;
1571                 Inset * inset_hit = checkInsetHit(bv_->text, x, y);
1572
1573                 // Middle button press pastes if we have a selection
1574                 // We do this here as if the selection was inside an inset
1575                 // it could get cleared on the unlocking of the inset so
1576                 // we have to check this first
1577                 bool paste_internally = false;
1578                 if (ev.button() == mouse_button::button2
1579                                 && bv_->getLyXText()->selection.set())
1580                 {
1581                         owner_->dispatch(FuncRequest(LFUN_COPY));
1582                         paste_internally = true;
1583                 }
1584
1585                 int const screen_first = bv_->text->first_y;
1586
1587                 if (bv_->theLockingInset()) {
1588                         // We are in inset locking mode
1589
1590                         // Check whether the inset was hit. If not reset mode,
1591                         // otherwise give the event to the inset
1592                         if (inset_hit == bv_->theLockingInset()) {
1593                                 FuncRequest cmd(bv_, LFUN_MOUSE_PRESS, x, y, ev.button());
1594                                 bv_->theLockingInset()->localDispatch(cmd);
1595                                 return false;
1596                         }
1597                         bv_->unlockInset(bv_->theLockingInset());
1598                 }
1599
1600                 if (!inset_hit)
1601                         selection_possible = true;
1602                 screen().hideCursor();
1603
1604                 // Clear the selection
1605                 screen().toggleSelection(bv_->text, bv_);
1606                 bv_->text->clearSelection();
1607                 bv_->text->fullRebreak(bv_);
1608                 update();
1609                 updateScrollbar();
1610
1611                 // Single left click in math inset?
1612                 if (isHighlyEditableInset(inset_hit)) {
1613                         // Highly editable inset, like math
1614                         UpdatableInset * inset = static_cast<UpdatableInset *>(inset_hit);
1615                         selection_possible = false;
1616                         owner_->updateLayoutChoice();
1617                         owner_->message(inset->editMessage());
1618                         //inset->edit(bv_, x, y, ev.button());
1619                         // We just have to lock the inset before calling a PressEvent on it!
1620                         // we don't need the edit() call here! (Jug20020329)
1621                         if (!bv_->lockInset(inset)) {
1622                                 lyxerr[Debug::INSETS] << "Cannot lock inset" << endl;
1623                         }
1624                         FuncRequest cmd(bv_, LFUN_MOUSE_PRESS, x, y, ev.button());
1625                         inset->localDispatch(cmd);
1626                         return false;
1627                 }
1628                 // I'm not sure we should continue here if we hit an inset (Jug20020403)
1629
1630                 // Right click on a footnote flag opens float menu
1631                 if (ev.button() == mouse_button::button3) {
1632                         selection_possible = false;
1633                         return false;
1634                 }
1635
1636                 if (!inset_hit) // otherwise it was already set in checkInsetHit(...)
1637                         bv_->text->setCursorFromCoordinates(bv_, x, y + screen_first);
1638                 finishUndo();
1639                 bv_->text->selection.cursor = bv_->text->cursor;
1640                 bv_->text->cursor.x_fix(bv_->text->cursor.x());
1641
1642                 owner_->updateLayoutChoice();
1643                 if (fitCursor()) {
1644                         selection_possible = false;
1645                 }
1646
1647                 // Insert primary selection with middle mouse
1648                 // if there is a local selection in the current buffer,
1649                 // insert this
1650                 if (ev.button() == mouse_button::button2) {
1651                         if (paste_internally)
1652                                 owner_->dispatch(FuncRequest(LFUN_PASTE));
1653                         else
1654                                 owner_->dispatch(FuncRequest(LFUN_PASTESELECTION, "paragraph"));
1655                         selection_possible = false;
1656                         return false;
1657                 }
1658                 break;
1659         }
1660
1661         case LFUN_MOUSE_RELEASE:
1662         {
1663                 // do nothing if we used the mouse wheel
1664                 if (!buffer_
1665                                 || ev.button() == mouse_button::button4
1666                                 || ev.button() == mouse_button::button5)
1667                         return false;
1668
1669                 // If we hit an inset, we have the inset coordinates in these
1670                 // and inset_hit points to the inset.  If we do not hit an
1671                 // inset, inset_hit is 0, and inset_x == x, inset_y == y.
1672                 int x = ev.x;
1673                 int y = ev.y;
1674                 Inset * inset_hit = checkInsetHit(bv_->text, x, y);
1675
1676                 if (bv_->theLockingInset()) {
1677                         // We are in inset locking mode.
1678
1679                         // LyX does a kind of work-area grabbing for insets.
1680                         // Only a ButtonPress FuncRequest outside the inset will
1681                         // force a insetUnlock.
1682                         FuncRequest cmd(bv_, LFUN_MOUSE_RELEASE, x, y, ev.button());
1683                         bv_->theLockingInset()->localDispatch(cmd);
1684                         return false;
1685                 }
1686
1687                 selection_possible = false;
1688
1689                 if (ev.button() == mouse_button::button2)
1690                         return false;
1691
1692                 // finish selection
1693                 if (ev.button() == mouse_button::button1) {
1694                         workarea().haveSelection(bv_->getLyXText()->selection.set());
1695                 }
1696
1697                 switchKeyMap();
1698                 owner_->view_state_changed();
1699                 owner_->updateMenubar();
1700                 owner_->updateToolbar();
1701
1702                 // Did we hit an editable inset?
1703                 if (inset_hit) {
1704                         selection_possible = false;
1705
1706                         // if we reach this point with a selection, it
1707                         // must mean we are currently selecting.
1708                         // But we don't want to open the inset
1709                         // because that is annoying for the user.
1710                         // So just pretend we didn't hit it.
1711                         // this is OK because a "kosher" ButtonRelease
1712                         // will follow a ButtonPress that clears
1713                         // the selection.
1714                         // Note this also fixes selection drawing
1715                         // problems if we end up opening an inset
1716                         if (bv_->getLyXText()->selection.set())
1717                                 return false;
1718
1719                         // CHECK fix this proper in 0.13
1720                         // well, maybe 13.0 !!!!!!!!!
1721
1722                         // Following a ref shouldn't issue
1723                         // a push on the undo-stack
1724                         // anylonger, now that we have
1725                         // keybindings for following
1726                         // references and returning from
1727                         // references.  IMHO though, it
1728                         // should be the inset's own business
1729                         // to push or not push on the undo
1730                         // stack. They don't *have* to
1731                         // alter the document...
1732                         // (Joacim)
1733                         // ...or maybe the SetCursorParUndo()
1734                         // below isn't necessary at all anylonger?
1735                         if (inset_hit->lyxCode() == Inset::REF_CODE) {
1736                                 setCursorParUndo(bv_);
1737                         }
1738
1739                         owner_->message(inset_hit->editMessage());
1740
1741                         if (isHighlyEditableInset(inset_hit)) {
1742                                 // Highly editable inset, like math
1743                                 UpdatableInset *inset = (UpdatableInset *)inset_hit;
1744                                 FuncRequest cmd(bv_, LFUN_MOUSE_RELEASE, x, y, ev.button());
1745                                 inset->localDispatch(cmd);
1746                         } else {
1747                                 FuncRequest cmd(bv_, LFUN_MOUSE_RELEASE, x, y, ev.button());
1748                                 inset_hit->localDispatch(cmd);
1749                                 // IMO this is a grosshack! Inset's should be changed so that
1750                                 // they call the actions they have to do with the insetButtonRel.
1751                                 // function and not in the edit(). This should be changed
1752                                 // (Jug 20020329)
1753         #ifdef WITH_WARNINGS
1754         #warning Please remove donot call inset->edit() here (Jug 20020812)
1755         #endif
1756                                 inset_hit->edit(bv_, x, y, ev.button());
1757                         }
1758                         return false;
1759                 }
1760
1761                 // Maybe we want to edit a bibitem ale970302
1762                 if (bv_->text->cursor.par()->bibkey) {
1763                         bool const is_rtl = bv_->text->cursor.par()->isRightToLeftPar(buffer_->params);
1764                         int const width = bibitemMaxWidth(bv_, buffer_->params.getLyXTextClass().defaultfont());
1765                         if ((is_rtl && x > bv_->text->workWidth(bv_)-20-width) ||
1766                                         (!is_rtl && x < 20+width)) {
1767                                 bv_->text->cursor.par()->bibkey->edit(bv_, 0, 0, mouse_button::none);
1768                         }
1769                 }
1770                 return false;
1771         }
1772
1773         default:
1774                 return bv_->getLyXText()->dispatch(FuncRequest(ev, bv_));
1775         } // end of switch
1776
1777         return true;
1778 }
1779
1780
1781 bool BufferView::Pimpl::insertInset(Inset * inset, string const & lout)
1782 {
1783         // if we are in a locking inset we should try to insert the
1784         // inset there otherwise this is a illegal function now
1785         if (bv_->theLockingInset()) {
1786                 if (bv_->theLockingInset()->insetAllowed(inset))
1787                     return bv_->theLockingInset()->insertInset(bv_, inset);
1788                 return false;
1789         }
1790
1791         // not quite sure if we want this...
1792         setCursorParUndo(bv_);
1793         freezeUndo();
1794
1795         beforeChange(bv_->text);
1796         if (!lout.empty()) {
1797                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
1798                 bv_->text->breakParagraph(bv_);
1799                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
1800
1801                 if (!bv_->text->cursor.par()->empty()) {
1802                         bv_->text->cursorLeft(bv_);
1803
1804                         bv_->text->breakParagraph(bv_);
1805                         update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
1806                 }
1807
1808                 string lres = lout;
1809                 LyXTextClass const & tclass =
1810                         buffer_->params.getLyXTextClass();
1811                 bool hasLayout = tclass.hasLayout(lres);
1812                 string lay = tclass.defaultLayoutName();
1813
1814                 if (hasLayout != false) {
1815                         // layout found
1816                         lay = lres;
1817                 } else {
1818                         // layout not fount using default
1819                         lay = tclass.defaultLayoutName();
1820                 }
1821
1822                 bv_->text->setLayout(bv_, lay);
1823
1824                 bv_->text->setParagraph(bv_, 0, 0,
1825                                    0, 0,
1826                                    VSpace(VSpace::NONE), VSpace(VSpace::NONE),
1827                                    Spacing(),
1828                                    LYX_ALIGN_LAYOUT,
1829                                    string(),
1830                                    0);
1831                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
1832         }
1833
1834         bv_->text->insertInset(bv_, inset);
1835         update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
1836
1837         unFreezeUndo();
1838         return true;
1839 }
1840
1841
1842 void BufferView::Pimpl::updateInset(Inset * inset, bool mark_dirty)
1843 {
1844         if (!inset || !available())
1845                 return;
1846
1847         // first check for locking insets
1848         if (bv_->theLockingInset()) {
1849                 if (bv_->theLockingInset() == inset) {
1850                         if (bv_->text->updateInset(bv_, inset)) {
1851                                 update();
1852                                 if (mark_dirty) {
1853                                         buffer_->markDirty();
1854                                 }
1855                                 updateScrollbar();
1856                                 return;
1857                         }
1858                 } else if (bv_->theLockingInset()->updateInsetInInset(bv_, inset)) {
1859                         if (bv_->text->updateInset(bv_,  bv_->theLockingInset())) {
1860                                 update();
1861                                 if (mark_dirty) {
1862                                         buffer_->markDirty();
1863                                 }
1864                                 updateScrollbar();
1865                                 return;
1866                         }
1867                 }
1868         }
1869
1870         // then check if the inset is a top_level inset (has no owner)
1871         // if yes do the update as always otherwise we have to update the
1872         // toplevel inset where this inset is inside
1873         Inset * tl_inset = inset;
1874         while (tl_inset->owner())
1875                 tl_inset = tl_inset->owner();
1876         hideCursor();
1877         if (tl_inset == inset) {
1878                 update(bv_->text, BufferView::UPDATE);
1879                 if (bv_->text->updateInset(bv_, inset)) {
1880                         if (mark_dirty) {
1881                                 update(bv_->text,
1882                                        BufferView::SELECT
1883                                        | BufferView::FITCUR
1884                                        | BufferView::CHANGE);
1885                         } else {
1886                                 update(bv_->text, SELECT);
1887                         }
1888                         return;
1889                 }
1890         } else if (static_cast<UpdatableInset *>(tl_inset)
1891                            ->updateInsetInInset(bv_, inset))
1892         {
1893                 if (bv_->text->updateInset(bv_, tl_inset)) {
1894                         update();
1895                         updateScrollbar();
1896                 }
1897         }
1898 }