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