]> git.lyx.org Git - lyx.git/blob - src/BufferView_pimpl.C
fix compilation bug
[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 #include "BufferView_pimpl.h"
12 #include "frontends/WorkArea.h"
13 #include "frontends/screen.h"
14 #include "frontends/LyXScreenFactory.h"
15 #include "frontends/WorkAreaFactory.h"
16 #include "frontends/Dialogs.h"
17 #include "frontends/Alert.h"
18 #include "frontends/FileDialog.h"
19 #include "frontends/mouse_state.h"
20 #include "lyxtext.h"
21 #include "lyxrow.h"
22 #include "paragraph.h"
23 #include "frontends/LyXView.h"
24 #include "commandtags.h"
25 #include "lyxfunc.h"
26 #include "debug.h"
27 #include "bufferview_funcs.h"
28 #include "TextCache.h"
29 #include "bufferlist.h"
30 #include "lyxrc.h"
31 #include "intl.h"
32 // added for Dispatch functions
33 #include "lyx_cb.h"
34 #include "lyx_main.h"
35 #include "FloatList.h"
36 #include "gettext.h"
37 #include "ParagraphParameters.h"
38 #include "undo_funcs.h"
39 #include "funcrequest.h"
40 #include "iterators.h"
41 #include "lyxfind.h"
42
43 #include "insets/insetbib.h"
44 #include "insets/insettext.h"
45 #include "insets/insetlatexaccent.h"
46 #include "insets/insettoc.h"
47 #include "insets/insetindex.h"
48 #include "insets/insetref.h"
49 #include "insets/insetinclude.h"
50 #include "insets/insetcite.h"
51 #include "insets/insetgraphics.h"
52 #include "insets/insetmarginal.h"
53 #include "insets/insetfloatlist.h"
54
55 #include "mathed/formulabase.h"
56
57 #include "graphics/Previews.h"
58
59 #include "support/LAssert.h"
60 #include "support/lstrings.h"
61 #include "support/filetools.h"
62
63 #include <boost/bind.hpp>
64 #include <boost/signals/connection.hpp>
65 #include "BoostFormat.h"
66
67 #include <unistd.h>
68 #include <sys/wait.h>
69
70
71 using std::vector;
72 using std::find_if;
73 using std::find;
74 using std::pair;
75 using std::endl;
76 using std::make_pair;
77 using std::min;
78
79 using lyx::pos_type;
80
81 extern BufferList bufferlist;
82
83
84 namespace {
85
86 unsigned int const saved_positions_num = 20;
87
88 // All the below connection objects are needed because of a bug in some
89 // versions of GCC (<=2.96 are on the suspects list.) By having and assigning
90 // to these connections we avoid a segfault upon startup, and also at exit.
91 // (Lgb)
92
93 boost::signals::connection dispatchcon;
94 boost::signals::connection timecon;
95 boost::signals::connection doccon;
96 boost::signals::connection resizecon;
97 boost::signals::connection kpresscon;
98 boost::signals::connection selectioncon;
99 boost::signals::connection lostcon;
100
101
102 } // anon namespace
103
104
105 BufferView::Pimpl::Pimpl(BufferView * bv, LyXView * owner,
106              int xpos, int ypos, int width, int height)
107         : bv_(bv), owner_(owner), buffer_(0), cursor_timeout(400),
108           using_xterm_cursor(false)
109 {
110         workarea_.reset(WorkAreaFactory::create(xpos, ypos, width, height));
111         screen_.reset(LyXScreenFactory::create(workarea()));
112
113         // Setup the signals
114         doccon = workarea().scrollDocView
115                 .connect(boost::bind(&BufferView::Pimpl::scrollDocView, this, _1));
116         resizecon = workarea().workAreaResize
117                 .connect(boost::bind(&BufferView::Pimpl::workAreaResize, this));
118         dispatchcon = workarea().dispatch
119                 .connect(boost::bind(&BufferView::Pimpl::dispatch, this, _1));
120         kpresscon = workarea().workAreaKeyPress
121                 .connect(boost::bind(&BufferView::Pimpl::workAreaKeyPress, this, _1, _2));
122         selectioncon = workarea().selectionRequested
123                 .connect(boost::bind(&BufferView::Pimpl::selectionRequested, this));
124         lostcon = workarea().selectionLost
125                 .connect(boost::bind(&BufferView::Pimpl::selectionLost, this));
126
127         timecon = cursor_timeout.timeout
128                 .connect(boost::bind(&BufferView::Pimpl::cursorToggle, this));
129         cursor_timeout.start();
130         saved_positions.resize(saved_positions_num);
131 }
132
133
134 WorkArea & BufferView::Pimpl::workarea() const
135 {
136         return *workarea_.get();
137 }
138
139
140 LyXScreen & BufferView::Pimpl::screen() const
141 {
142         return *screen_.get();
143 }
144
145
146 Painter & BufferView::Pimpl::painter() const
147 {
148         return workarea().getPainter();
149 }
150
151
152 void BufferView::Pimpl::buffer(Buffer * b)
153 {
154         lyxerr[Debug::INFO] << "Setting buffer in BufferView ("
155                             << b << ')' << endl;
156         if (buffer_) {
157                 buffer_->delUser(bv_);
158
159                 // Put the old text into the TextCache, but
160                 // only if the buffer is still loaded.
161                 // Also set the owner of the test to 0
162                 //              bv_->text->owner(0);
163                 textcache.add(buffer_, workarea().workWidth(), bv_->text);
164                 if (lyxerr.debugging())
165                         textcache.show(lyxerr, "BufferView::buffer");
166
167                 bv_->text = 0;
168         }
169
170         // set current buffer
171         buffer_ = b;
172
173         // if we're quitting lyx, don't bother updating stuff 
174         if (quitting)
175                 return; 
176
177         // if we are closing the buffer, use the first buffer as current
178         if (!buffer_) {
179                 buffer_ = bufferlist.first();
180         }
181
182         if (buffer_) {
183                 lyxerr[Debug::INFO] << "Buffer addr: " << buffer_ << endl;
184                 buffer_->addUser(bv_);
185
186                 // If we don't have a text object for this, we make one
187                 if (bv_->text == 0) {
188                         resizeCurrentBuffer();
189                 }
190
191                 // FIXME: needed when ?
192                 bv_->text->first_y =
193                         screen().topCursorVisible(bv_->text->cursor, bv_->text->first_y);
194
195                 // Similarly, buffer-dependent dialogs should be updated or
196                 // hidden. This should go here because some dialogs (eg ToC)
197                 // require bv_->text.
198                 owner_->getDialogs().updateBufferDependent(true);
199         } else {
200                 lyxerr[Debug::INFO] << "  No Buffer!" << endl;
201                 owner_->getDialogs().hideBufferDependent();
202
203                 // Also remove all remaining text's from the testcache.
204                 // (there should not be any!) (if there is any it is a
205                 // bug!)
206                 if (lyxerr.debugging())
207                         textcache.show(lyxerr, "buffer delete all");
208                 textcache.clear();
209         }
210
211         repaint();
212         updateScrollbar();
213         owner_->updateMenubar();
214         owner_->updateToolbar();
215         owner_->updateLayoutChoice();
216         owner_->updateWindowTitle();
217
218         if (grfx::Previews::activated() && buffer_)
219                 grfx::Previews::get().generateBufferPreviews(*buffer_);
220 }
221
222
223 bool BufferView::Pimpl::fitCursor()
224 {
225         bool ret;
226
227         if (bv_->theLockingInset()) {
228                 bv_->theLockingInset()->fitInsetCursor(bv_);
229                 ret = true;
230         } else {
231                 ret = screen().fitCursor(bv_->text, bv_);
232         }
233
234         bv_->owner()->getDialogs().updateParagraph();
235         if (ret)
236                 updateScrollbar();
237         return ret;
238 }
239
240
241 void BufferView::Pimpl::redoCurrentBuffer()
242 {
243         lyxerr[Debug::INFO] << "BufferView::redoCurrentBuffer" << endl;
244         if (buffer_ && bv_->text) {
245                 resizeCurrentBuffer();
246                 updateScrollbar();
247                 owner_->updateLayoutChoice();
248                 repaint();
249         }
250 }
251
252
253 int BufferView::Pimpl::resizeCurrentBuffer()
254 {
255         lyxerr[Debug::INFO] << "resizeCurrentBuffer" << endl;
256
257         Paragraph * par = 0;
258         Paragraph * selstartpar = 0;
259         Paragraph * selendpar = 0;
260         UpdatableInset * the_locking_inset = 0;
261
262         pos_type pos = 0;
263         pos_type selstartpos = 0;
264         pos_type selendpos = 0;
265         bool selection = false;
266         bool mark_set  = false;
267
268         owner_->busy(true);
269  
270         owner_->message(_("Formatting document..."));
271
272         if (bv_->text) {
273                 par = bv_->text->cursor.par();
274                 pos = bv_->text->cursor.pos();
275                 selstartpar = bv_->text->selection.start.par();
276                 selstartpos = bv_->text->selection.start.pos();
277                 selendpar = bv_->text->selection.end.par();
278                 selendpos = bv_->text->selection.end.pos();
279                 selection = bv_->text->selection.set();
280                 mark_set = bv_->text->selection.mark();
281                 the_locking_inset = bv_->theLockingInset();
282                 buffer_->resizeInsets(bv_);
283                 // I don't think the delete and new are necessary here we just could
284                 // call only init! (Jug 20020419)
285                 delete bv_->text;
286                 bv_->text = new LyXText(bv_);
287                 bv_->text->init(bv_);
288         } else {
289                 // See if we have a text in TextCache that fits
290                 // the new buffer_ with the correct width.
291                 bv_->text = textcache.findFit(buffer_, workarea().workWidth());
292                 if (bv_->text) {
293                         if (lyxerr.debugging()) {
294                                 lyxerr << "Found a LyXText that fits:\n";
295                                 textcache.show(lyxerr, make_pair(buffer_, make_pair(workarea().workWidth(), bv_->text)));
296                         }
297                         // Set the owner of the newly found text
298                         //      bv_->text->owner(bv_);
299                         if (lyxerr.debugging())
300                                 textcache.show(lyxerr, "resizeCurrentBuffer");
301
302                         buffer_->resizeInsets(bv_);
303                 } else {
304                         bv_->text = new LyXText(bv_);
305                         bv_->text->init(bv_);
306                         //buffer_->resizeInsets(bv_);
307                 }
308         }
309
310         if (par) {
311                 bv_->text->selection.set(true);
312                 // At this point just to avoid the Delete-Empty-Paragraph-
313                 // Mechanism when setting the cursor.
314                 bv_->text->selection.mark(mark_set);
315                 if (selection) {
316                         bv_->text->setCursor(bv_, selstartpar, selstartpos);
317                         bv_->text->selection.cursor = bv_->text->cursor;
318                         bv_->text->setCursor(bv_, selendpar, selendpos);
319                         bv_->text->setSelection(bv_);
320                         bv_->text->setCursor(bv_, par, pos);
321                 } else {
322                         bv_->text->setCursor(bv_, par, pos);
323                         bv_->text->selection.cursor = bv_->text->cursor;
324                         bv_->text->selection.set(false);
325                 }
326                 // remake the inset locking
327                 bv_->theLockingInset(the_locking_inset);
328         }
329
330         bv_->text->first_y = screen().topCursorVisible(bv_->text->cursor, bv_->text->first_y);
331
332         switchKeyMap();
333         owner_->busy(false);
334
335         updateScrollbar();
336
337         return 0;
338 }
339
340
341 void BufferView::Pimpl::repaint()
342 {
343         // Regenerate the screen.
344         screen().redraw(bv_->text, bv_);
345 }
346
347
348 void BufferView::Pimpl::updateScrollbar()
349 {
350         if (!bv_->text) {
351                 lyxerr[Debug::GUI] << "no text in updateScrollbar" << endl;
352                 workarea().setScrollbarParams(0, 0, 0);
353                 return;
354         }
355
356         LyXText const & t = *bv_->text;
357
358         lyxerr[Debug::GUI] << "Updating scrollbar: h " << t.height << ", first_y "
359                 << t.first_y << ", default height " << defaultRowHeight() << endl;
360
361         workarea().setScrollbarParams(t.height, t.first_y, defaultRowHeight());
362 }
363
364
365 void BufferView::Pimpl::scrollDocView(int value)
366 {
367         lyxerr[Debug::GUI] << "scrollDocView of " << value << endl;
368
369         if (!buffer_)
370                 return;
371
372         screen().draw(bv_->text, bv_, value);
373
374         if (!lyxrc.cursor_follows_scrollbar)
375                 return;
376
377         LyXText * vbt = bv_->text;
378
379         int const height = defaultRowHeight();
380         int const first = static_cast<int>((bv_->text->first_y + height));
381         int const last = static_cast<int>((bv_->text->first_y + workarea().workHeight() - height));
382
383         if (vbt->cursor.y() < first)
384                 vbt->setCursorFromCoordinates(bv_, 0, first);
385         else if (vbt->cursor.y() > last)
386                 vbt->setCursorFromCoordinates(bv_, 0, last);
387 }
388
389
390 void BufferView::Pimpl::scroll(int lines)
391 {
392         if (!buffer_) {
393                 return;
394         }
395
396         LyXText const * t = bv_->text;
397         int const line_height = defaultRowHeight();
398
399         // The new absolute coordinate
400         int new_first_y = t->first_y + lines * line_height;
401
402         // Restrict to a valid value
403         new_first_y = std::min(t->height - 4 * line_height, new_first_y);
404         new_first_y = std::max(0, new_first_y);
405
406         scrollDocView(new_first_y);
407
408         // Update the scrollbar.
409         workarea().setScrollbarParams(t->height, t->first_y, defaultRowHeight());
410 }
411
412
413 void BufferView::Pimpl::workAreaKeyPress(LyXKeySymPtr key,
414                                          key_modifier::state state)
415 {
416         bv_->owner()->getLyXFunc().processKeySym(key, state);
417 }
418
419
420 void BufferView::Pimpl::selectionRequested()
421 {
422         static string sel;
423
424         if (!available())
425                 return;
426
427         LyXText * text = bv_->getLyXText();
428
429         if (text->selection.set() &&
430                 (!bv_->text->xsel_cache.set() ||
431                  text->selection.start != bv_->text->xsel_cache.start ||
432                  text->selection.end != bv_->text->xsel_cache.end))
433         {
434                 bv_->text->xsel_cache = text->selection;
435                 sel = text->selectionAsString(bv_->buffer(), false);
436         } else if (!text->selection.set()) {
437                 sel = string();
438                 bv_->text->xsel_cache.set(false);
439         }
440         if (!sel.empty()) {
441                 workarea().putClipboard(sel);
442         }
443 }
444
445
446 void BufferView::Pimpl::selectionLost()
447 {
448         if (available()) {
449                 hideCursor();
450                 toggleSelection();
451                 bv_->getLyXText()->clearSelection();
452                 showCursor();
453                 bv_->text->xsel_cache.set(false);
454         }
455 }
456
457
458 void BufferView::Pimpl::workAreaResize()
459 {
460         static int work_area_width;
461         static int work_area_height;
462
463         bool const widthChange = workarea().workWidth() != work_area_width;
464         bool const heightChange = workarea().workHeight() != work_area_height;
465
466         // update from work area
467         work_area_width = workarea().workWidth();
468         work_area_height = workarea().workHeight();
469
470         if (buffer_ != 0) {
471                 if (widthChange) {
472                         // The visible LyXView need a resize
473                         resizeCurrentBuffer();
474
475                         // Remove all texts from the textcache
476                         // This is not _really_ what we want to do. What
477                         // we really want to do is to delete in textcache
478                         // that does not have a BufferView with matching
479                         // width, but as long as we have only one BufferView
480                         // deleting all gives the same result.
481                         if (lyxerr.debugging())
482                                 textcache.show(lyxerr, "Expose delete all");
483                         textcache.clear();
484                         // FIXME: this is already done in resizeCurrentBuffer() ??
485                         buffer_->resizeInsets(bv_);
486                 } else if (heightChange) {
487                         // fitCursor() ensures we don't jump back
488                         // to the start of the document on vertical
489                         // resize
490                         fitCursor();
491                 }
492         }
493
494         if (widthChange || heightChange) {
495                 repaint();
496         }
497
498         // always make sure that the scrollbar is sane.
499         updateScrollbar();
500         owner_->updateLayoutChoice();
501         return;
502 }
503
504
505 void BufferView::Pimpl::update()
506 {
507         if (!bv_->theLockingInset() || !bv_->theLockingInset()->nodraw()) {
508                 LyXText::text_status st = bv_->text->status();
509                 screen().update(bv_->text, bv_);
510                 bool fitc = false;
511                 while (bv_->text->status() == LyXText::CHANGED_IN_DRAW) {
512                         bv_->text->fullRebreak(bv_);
513                         st = LyXText::NEED_MORE_REFRESH;
514                         bv_->text->setCursor(bv_, bv_->text->cursor.par(),
515                                              bv_->text->cursor.pos());
516                         if (bv_->text->selection.set()) {
517                                 bv_->text->setCursor(bv_, bv_->text->selection.start,
518                                                      bv_->text->selection.start.par(),
519                                                      bv_->text->selection.start.pos());
520                                 bv_->text->setCursor(bv_, bv_->text->selection.end,
521                                                      bv_->text->selection.end.par(),
522                                                      bv_->text->selection.end.pos());
523                         }
524                         fitc = true;
525                         bv_->text->status(bv_, st);
526                         screen().update(bv_->text, bv_);
527                 }
528                 // do this here instead of in the screen::update because of
529                 // the above loop!
530                 bv_->text->status(bv_, LyXText::UNCHANGED);
531                 if (fitc)
532                         fitCursor();
533         }
534 }
535
536 // Values used when calling update:
537 // -3 - update
538 // -2 - update, move sel_cursor if selection, fitcursor
539 // -1 - update, move sel_cursor if selection, fitcursor, mark dirty
540 //  0 - update, move sel_cursor if selection, fitcursor
541 //  1 - update, move sel_cursor if selection, fitcursor, mark dirty
542 //  3 - update, move sel_cursor if selection
543 //
544 // update -
545 // a simple redraw of the parts that need refresh
546 //
547 // move sel_cursor if selection -
548 // the text's sel_cursor is moved if there is selection is progress
549 //
550 // fitcursor -
551 // fitCursor() is called and the scrollbar updated
552 //
553 // mark dirty -
554 // the buffer is marked dirty.
555 //
556 // enum {
557 //       UPDATE = 0,
558 //       SELECT = 1,
559 //       FITCUR = 2,
560 //       CHANGE = 4
561 // };
562 //
563 // UPDATE_ONLY = UPDATE;
564 // UPDATE_SELECT = UPDATE | SELECT;
565 // UPDATE_SELECT_MOVE = UPDATE | SELECT | FITCUR;
566 // UPDATE_SELECT_MOVE_AFTER_CHANGE = UPDATE | SELECT | FITCUR | CHANGE;
567 //
568 // update(-3) -> update(0)         -> update(0) -> update(UPDATE)
569 // update(-2) -> update(1 + 2)     -> update(3) -> update(SELECT|FITCUR)
570 // update(-1) -> update(1 + 2 + 4) -> update(7) -> update(SELECT|FITCUR|CHANGE)
571 // update(1)  -> update(1 + 2 + 4) -> update(7) -> update(SELECT|FITCUR|CHANGE)
572 // update(3)  -> update(1)         -> update(1) -> update(SELECT)
573
574 void BufferView::Pimpl::update(LyXText * text, BufferView::UpdateCodes f)
575 {
576         owner_->updateLayoutChoice();
577
578         if (!text->selection.set() && (f & SELECT)) {
579                 text->selection.cursor = text->cursor;
580         }
581
582         text->fullRebreak(bv_);
583
584         if (text->inset_owner) {
585                 text->inset_owner->setUpdateStatus(bv_, InsetText::NONE);
586                 updateInset(text->inset_owner, false);
587         } else {
588                 update();
589         }
590
591         if ((f & FITCUR)) {
592                 fitCursor();
593         }
594
595         if ((f & CHANGE)) {
596                 buffer_->markDirty();
597         }
598 }
599
600
601 // Callback for cursor timer
602 void BufferView::Pimpl::cursorToggle()
603 {
604         if (!buffer_) {
605                 cursor_timeout.restart();
606                 return;
607         }
608
609         if (!bv_->theLockingInset()) {
610                 screen().cursorToggle(bv_);
611         } else {
612                 bv_->theLockingInset()->toggleInsetCursor(bv_);
613         }
614
615         cursor_timeout.restart();
616 }
617
618
619 bool BufferView::Pimpl::available() const
620 {
621         if (buffer_ && bv_->text)
622                 return true;
623         return false;
624 }
625
626
627 Change const BufferView::Pimpl::getCurrentChange()
628 {
629         if (!bv_->buffer()->params.tracking_changes) 
630                 return Change(Change::UNCHANGED);
631
632         LyXText * t(bv_->getLyXText());
633  
634         if (!t->selection.set())
635                 return Change(Change::UNCHANGED);
636  
637         LyXCursor const & cur(t->selection.start);
638         return cur.par()->lookupChangeFull(cur.pos());
639 }
640
641
642 void BufferView::Pimpl::beforeChange(LyXText * text)
643 {
644         toggleSelection();
645         text->clearSelection();
646 }
647
648
649 void BufferView::Pimpl::savePosition(unsigned int i)
650 {
651         if (i >= saved_positions_num)
652                 return;
653         saved_positions[i] = Position(buffer_->fileName(),
654                                       bv_->text->cursor.par()->id(),
655                                       bv_->text->cursor.pos());
656         if (i > 0) {
657                 ostringstream str;
658 #if USE_BOOST_FORMAT
659                 str << boost::format(_("Saved bookmark %1$d")) % i;
660 #else
661                 str << _("Saved bookmark ") << i;
662 #endif
663                 owner_->message(STRCONV(str.str()));
664         }
665 }
666
667
668 void BufferView::Pimpl::restorePosition(unsigned int i)
669 {
670         if (i >= saved_positions_num)
671                 return;
672
673         string const fname = saved_positions[i].filename;
674
675         beforeChange(bv_->text);
676
677         if (fname != buffer_->fileName()) {
678                 Buffer * b = bufferlist.exists(fname) ?
679                         bufferlist.getBuffer(fname) :
680                         bufferlist.loadLyXFile(fname); // don't ask, just load it
681                 if (b != 0) buffer(b);
682         }
683
684         Paragraph * par = buffer_->getParFromID(saved_positions[i].par_id);
685         if (!par)
686                 return;
687
688         bv_->text->setCursor(bv_, par,
689                              min(par->size(), saved_positions[i].par_pos));
690
691         update(bv_->text, BufferView::SELECT | BufferView::FITCUR);
692         if (i > 0) {
693                 ostringstream str;
694 #if USE_BOOST_FORMAT
695                 str << boost::format(_("Moved to bookmark %1$d")) % i;
696 #else
697                 str << _("Moved to bookmark ") << i;
698 #endif
699                 owner_->message(STRCONV(str.str()));
700         }
701 }
702
703
704 bool BufferView::Pimpl::isSavedPosition(unsigned int i)
705 {
706         if (i >= saved_positions_num)
707                 return false;
708
709         return !saved_positions[i].filename.empty();
710 }
711
712
713 void BufferView::Pimpl::switchKeyMap()
714 {
715         if (!lyxrc.rtl_support)
716                 return;
717
718         LyXText * text = bv_->getLyXText();
719         if (text->real_current_font.isRightToLeft()
720             && !(bv_->theLockingInset()
721                  && bv_->theLockingInset()->lyxCode() == Inset::ERT_CODE))
722         {
723                 if (owner_->getIntl().keymap == Intl::PRIMARY)
724                         owner_->getIntl().KeyMapSec();
725         } else {
726                 if (owner_->getIntl().keymap == Intl::SECONDARY)
727                         owner_->getIntl().KeyMapPrim();
728         }
729 }
730
731
732 void BufferView::Pimpl::insetUnlock()
733 {
734         if (bv_->theLockingInset()) {
735                 bv_->theLockingInset()->insetUnlock(bv_);
736                 bv_->theLockingInset(0);
737                 finishUndo();
738         }
739 }
740
741
742 void BufferView::Pimpl::showCursor()
743 {
744         if (bv_->theLockingInset())
745                 bv_->theLockingInset()->showInsetCursor(bv_);
746         else
747                 screen().showCursor(bv_->text, bv_);
748 }
749
750
751 void BufferView::Pimpl::hideCursor()
752 {
753         if (!bv_->theLockingInset())
754                 screen().hideCursor();
755 }
756
757
758 void BufferView::Pimpl::toggleSelection(bool b)
759 {
760         if (bv_->theLockingInset())
761                 bv_->theLockingInset()->toggleSelection(bv_, b);
762         screen().toggleSelection(bv_->text, bv_, b);
763 }
764
765
766 void BufferView::Pimpl::toggleToggle()
767 {
768         screen().toggleToggle(bv_->text, bv_);
769 }
770
771
772 void BufferView::Pimpl::center()
773 {
774         LyXText * t = bv_->text;
775
776         beforeChange(t);
777         int const half_height = workarea().workHeight() / 2;
778         int new_y = 0;
779
780         if (t->cursor.y() > half_height) {
781                 new_y = t->cursor.y() - half_height;
782         }
783
784         // FIXME: can we do this w/o calling screen directly ?
785         // This updates first_y but means the fitCursor() call
786         // from the update(FITCUR) doesn't realise that we might
787         // have moved (e.g. from GOTOPARAGRAPH), so doesn't cause
788         // the scrollbar to be updated as it should, so we have
789         // to do it manually. Any operation that does a center()
790         // and also might have moved first_y must make sure to call
791         // updateScrollbar() currently. Never mind that this is a
792         // pretty obfuscated way of updating t->first_y
793         screen().draw(t, bv_, new_y);
794
795         update(t, BufferView::SELECT | BufferView::FITCUR);
796 }
797
798
799 void BufferView::Pimpl::stuffClipboard(string const & stuff) const
800 {
801         workarea().putClipboard(stuff);
802 }
803
804
805 /*
806  * Dispatch functions for actions which can be valid for BufferView->text
807  * and/or InsetText->text!!!
808  */
809
810
811 Inset * BufferView::Pimpl::getInsetByCode(Inset::Code code)
812 {
813 #if 0
814         LyXCursor cursor = bv_->getLyXText()->cursor;
815         Buffer::inset_iterator it =
816                 find_if(Buffer::inset_iterator(
817                         cursor.par(), cursor.pos()),
818                         buffer_->inset_iterator_end(),
819                         lyx::compare_memfun(&Inset::lyxCode, code));
820         return it != buffer_->inset_iterator_end() ? (*it) : 0;
821 #else
822         // Ok, this is a little bit too brute force but it
823         // should work for now. Better infrastructure is comming. (Lgb)
824
825         Buffer * b = bv_->buffer();
826         LyXCursor cursor = bv_->getLyXText()->cursor;
827
828         Buffer::inset_iterator beg = b->inset_iterator_begin();
829         Buffer::inset_iterator end = b->inset_iterator_end();
830
831         bool cursor_par_seen = false;
832
833         for (; beg != end; ++beg) {
834                 if (beg.getPar() == cursor.par()) {
835                         cursor_par_seen = true;
836                 }
837                 if (cursor_par_seen) {
838                         if (beg.getPar() == cursor.par()
839                             && beg.getPos() >= cursor.pos()) {
840                                 break;
841                         } else if (beg.getPar() != cursor.par()) {
842                                 break;
843                         }
844                 }
845
846         }
847         if (beg != end) {
848                 // Now find the first inset that matches code.
849                 for (; beg != end; ++beg) {
850                         if (beg->lyxCode() == code) {
851                                 return &(*beg);
852                         }
853                 }
854         }
855         return 0;
856 #endif
857 }
858
859
860 void BufferView::Pimpl::MenuInsertLyXFile(string const & filen)
861 {
862         string filename = filen;
863
864         if (filename.empty()) {
865                 // Launch a file browser
866                 string initpath = lyxrc.document_path;
867
868                 if (available()) {
869                         string const trypath = owner_->buffer()->filePath();
870                         // If directory is writeable, use this as default.
871                         if (IsDirWriteable(trypath))
872                                 initpath = trypath;
873                 }
874
875                 FileDialog fileDlg(bv_->owner(),
876                                    _("Select LyX document to insert"),
877                         LFUN_FILE_INSERT,
878                         make_pair(string(_("Documents|#o#O")),
879                                   string(lyxrc.document_path)),
880                         make_pair(string(_("Examples|#E#e")),
881                                   string(AddPath(system_lyxdir, "examples"))));
882
883                 FileDialog::Result result =
884                         fileDlg.open(initpath,
885                                        _("*.lyx| LyX Documents (*.lyx)"));
886
887                 if (result.first == FileDialog::Later)
888                         return;
889
890                 filename = result.second;
891
892                 // check selected filename
893                 if (filename.empty()) {
894                         owner_->message(_("Canceled."));
895                         return;
896                 }
897         }
898
899         // get absolute path of file and add ".lyx" to the filename if
900         // necessary
901         filename = FileSearch(string(), filename, "lyx");
902
903         string const disp_fn(MakeDisplayPath(filename));
904
905         ostringstream s1;
906 #if USE_BOOST_FORMAT
907         s1 << boost::format(_("Inserting document %1$s...")) % disp_fn;
908 #else
909         s1 << _("Inserting document ") << disp_fn << _("...");
910 #endif
911         owner_->message(STRCONV(s1.str()));
912         bool const res = bv_->insertLyXFile(filename);
913         if (res) {
914                 ostringstream str;
915 #if USE_BOOST_FORMAT
916                 str << boost::format(_("Document %1$s inserted.")) % disp_fn;
917 #else
918                 str << _("Document ") << disp_fn << _(" inserted.");
919 #endif
920                 owner_->message(STRCONV(str.str()));
921         } else {
922                 ostringstream str;
923 #if USE_BOOST_FORMAT
924                 str << boost::format(_("Could not insert document %1$s")) % disp_fn;
925 #else
926                 str << _("Could not insert document ") << disp_fn;
927 #endif
928                 owner_->message(STRCONV(str.str()));
929         }
930 }
931
932
933 void BufferView::Pimpl::trackChanges()
934 {
935         Buffer * buf(bv_->buffer());
936         bool const tracking(buf->params.tracking_changes);
937
938         if (!tracking) {
939                 ParIterator const end = buf->par_iterator_end();
940                 for (ParIterator it = buf->par_iterator_begin(); it != end; ++it) {
941                         (*it)->trackChanges();
942                 }
943                 buf->params.tracking_changes = true;
944
945                 // we cannot allow undos beyond the freeze point
946                 buf->undostack.clear();
947         } else {
948                 bv_->update(bv_->text, BufferView::SELECT | BufferView::FITCUR);
949                 bv_->text->setCursor(bv_, &(*buf->paragraphs.begin()), 0);
950 #warning changes FIXME 
951                 //moveCursorUpdate(false);
952
953                 bool found = lyxfind::findNextChange(bv_);
954                 if (found) {
955                         owner_->getDialogs().showMergeChanges();
956                         return;
957                 }
958
959                 ParIterator const end = buf->par_iterator_end();
960                 for (ParIterator it = buf->par_iterator_begin(); it != end; ++it) {
961                         (*it)->untrackChanges();
962                 }
963                 buf->params.tracking_changes = false;
964         }
965  
966         buf->redostack.clear();
967 }
968
969
970 bool BufferView::Pimpl::dispatch(FuncRequest const & ev)
971 {
972         lyxerr[Debug::ACTION] << "BufferView::Pimpl::Dispatch:"
973                 << " action[" << ev.action << ']'
974                 << " arg[" << ev.argument << ']'
975                 << " x[" << ev.x << ']'
976                 << " y[" << ev.y << ']'
977                 << " button[" << ev.button() << ']'
978                 << endl;
979
980         // e.g. Qt mouse press when no buffer
981         if (!buffer_)
982                 return false;
983
984         LyXTextClass const & tclass = buffer_->params.getLyXTextClass();
985
986         switch (ev.action) {
987
988         case LFUN_SCROLL_INSET:
989                 // this is not handled here as this function is only active
990                 // if we have a locking_inset and that one is (or contains)
991                 // a tabular-inset
992                 break;
993
994         case LFUN_INSET_GRAPHICS:
995         {
996                 Inset * new_inset = new InsetGraphics;
997                 if (!insertInset(new_inset)) {
998                         delete new_inset;
999                 } else {
1000                         // this is need because you don't use a inset->Edit()
1001                         updateInset(new_inset, true);
1002                         new_inset->edit(bv_);
1003                 }
1004                 break;
1005         }
1006
1007         case LFUN_LAYOUT_COPY:
1008                 bv_->copyEnvironment();
1009                 break;
1010
1011         case LFUN_LAYOUT_PASTE:
1012                 bv_->pasteEnvironment();
1013                 switchKeyMap();
1014                 break;
1015
1016         case LFUN_DEPTH_MIN:
1017                 changeDepth(bv_, bv_->getLyXText(), -1);
1018                 break;
1019
1020         case LFUN_DEPTH_PLUS:
1021                 changeDepth(bv_, bv_->getLyXText(), 1);
1022                 break;
1023
1024         case LFUN_FREE:
1025                 owner_->getDialogs().setUserFreeFont();
1026                 break;
1027
1028         case LFUN_FILE_INSERT:
1029                 MenuInsertLyXFile(ev.argument);
1030                 break;
1031
1032         case LFUN_FILE_INSERT_ASCII_PARA:
1033                 InsertAsciiFile(bv_, ev.argument, true);
1034                 break;
1035
1036         case LFUN_FILE_INSERT_ASCII:
1037                 InsertAsciiFile(bv_, ev.argument, false);
1038                 break;
1039
1040         case LFUN_LANGUAGE:
1041                 lang(bv_, ev.argument);
1042                 switchKeyMap();
1043                 owner_->view_state_changed();
1044                 break;
1045
1046         case LFUN_EMPH:
1047                 emph(bv_);
1048                 owner_->view_state_changed();
1049                 break;
1050
1051         case LFUN_BOLD:
1052                 bold(bv_);
1053                 owner_->view_state_changed();
1054                 break;
1055
1056         case LFUN_NOUN:
1057                 noun(bv_);
1058                 owner_->view_state_changed();
1059                 break;
1060
1061         case LFUN_CODE:
1062                 code(bv_);
1063                 owner_->view_state_changed();
1064                 break;
1065
1066         case LFUN_SANS:
1067                 sans(bv_);
1068                 owner_->view_state_changed();
1069                 break;
1070
1071         case LFUN_ROMAN:
1072                 roman(bv_);
1073                 owner_->view_state_changed();
1074                 break;
1075
1076         case LFUN_DEFAULT:
1077                 styleReset(bv_);
1078                 owner_->view_state_changed();
1079                 break;
1080
1081         case LFUN_UNDERLINE:
1082                 underline(bv_);
1083                 owner_->view_state_changed();
1084                 break;
1085
1086         case LFUN_FONT_SIZE:
1087                 fontSize(bv_, ev.argument);
1088                 owner_->view_state_changed();
1089                 break;
1090
1091         case LFUN_FONT_STATE:
1092                 owner_->getLyXFunc().setMessage(currentState(bv_));
1093                 break;
1094
1095         case LFUN_INSERT_LABEL:
1096                 MenuInsertLabel(bv_, ev.argument);
1097                 break;
1098
1099         case LFUN_REF_INSERT:
1100                 if (ev.argument.empty()) {
1101                         InsetCommandParams p("ref");
1102                         owner_->getDialogs().createRef(p.getAsString());
1103                 } else {
1104                         InsetCommandParams p;
1105                         p.setFromString(ev.argument);
1106
1107                         InsetRef * inset = new InsetRef(p, *buffer_);
1108                         if (!insertInset(inset))
1109                                 delete inset;
1110                         else
1111                                 updateInset(inset, true);
1112                 }
1113                 break;
1114
1115         case LFUN_BOOKMARK_SAVE:
1116                 savePosition(strToUnsignedInt(ev.argument));
1117                 break;
1118
1119         case LFUN_BOOKMARK_GOTO:
1120                 restorePosition(strToUnsignedInt(ev.argument));
1121                 break;
1122
1123         case LFUN_REF_GOTO:
1124         {
1125                 string label = ev.argument;
1126                 if (label.empty()) {
1127                         InsetRef * inset =
1128                                 static_cast<InsetRef*>(getInsetByCode(Inset::REF_CODE));
1129                         if (inset) {
1130                                 label = inset->getContents();
1131                                 savePosition(0);
1132                         }
1133                 }
1134
1135                 if (!label.empty()) {
1136                         //bv_->savePosition(0);
1137                         if (!bv_->gotoLabel(label))
1138                                 Alert::alert(_("Error"),
1139                                            _("Couldn't find this label"),
1140                                            _("in current document."));
1141                 }
1142         }
1143         break;
1144
1145         // --- accented characters ---------------------------
1146
1147         case LFUN_UMLAUT:
1148         case LFUN_CIRCUMFLEX:
1149         case LFUN_GRAVE:
1150         case LFUN_ACUTE:
1151         case LFUN_TILDE:
1152         case LFUN_CEDILLA:
1153         case LFUN_MACRON:
1154         case LFUN_DOT:
1155         case LFUN_UNDERDOT:
1156         case LFUN_UNDERBAR:
1157         case LFUN_CARON:
1158         case LFUN_SPECIAL_CARON:
1159         case LFUN_BREVE:
1160         case LFUN_TIE:
1161         case LFUN_HUNG_UMLAUT:
1162         case LFUN_CIRCLE:
1163         case LFUN_OGONEK:
1164                 if (ev.argument.empty()) {
1165                         // As always...
1166                         owner_->getLyXFunc().handleKeyFunc(ev.action);
1167                 } else {
1168                         owner_->getLyXFunc().handleKeyFunc(ev.action);
1169                         owner_->getIntl().getTransManager()
1170                                 .TranslateAndInsert(ev.argument[0], bv_->getLyXText());
1171                         update(bv_->getLyXText(),
1172                                BufferView::SELECT
1173                                | BufferView::FITCUR
1174                                | BufferView::CHANGE);
1175                 }
1176                 break;
1177
1178         case LFUN_MATH_MACRO:
1179         case LFUN_MATH_DELIM:
1180         case LFUN_INSERT_MATRIX:
1181         case LFUN_INSERT_MATH:
1182         case LFUN_MATH_IMPORT_SELECTION: // Imports LaTeX from the X selection
1183         case LFUN_MATH_DISPLAY:          // Open or create a displayed math inset
1184         case LFUN_MATH_MODE:             // Open or create an inlined math inset
1185         case LFUN_GREEK:                 // Insert a single greek letter
1186                 mathDispatch(FuncRequest(bv_, ev.action, ev.argument));
1187                 break;
1188
1189         case LFUN_CITATION_INSERT:
1190         {
1191                 InsetCommandParams p;
1192                 p.setFromString(ev.argument);
1193
1194                 InsetCitation * inset = new InsetCitation(p);
1195                 if (!insertInset(inset))
1196                         delete inset;
1197                 else {
1198                         inset->setLoadingBuffer(bv_->buffer(), false);
1199                         updateInset(inset, true);
1200                 }
1201
1202         }
1203         break;
1204
1205         case LFUN_INSERT_BIBTEX:
1206         {
1207                 // ale970405+lasgoutt970425
1208                 // The argument can be up to two tokens separated
1209                 // by a space. The first one is the bibstyle.
1210                 string const db = token(ev.argument, ' ', 0);
1211                 string bibstyle = token(ev.argument, ' ', 1);
1212                 if (bibstyle.empty())
1213                         bibstyle = "plain";
1214
1215                 InsetCommandParams p("BibTeX", db, bibstyle);
1216                 InsetBibtex * inset = new InsetBibtex(p);
1217
1218                 if (insertInset(inset)) {
1219                         if (ev.argument.empty())
1220                                 inset->edit(bv_);
1221                 } else
1222                         delete inset;
1223         }
1224         break;
1225
1226         // BibTeX data bases
1227         case LFUN_BIBDB_ADD:
1228         {
1229                 InsetBibtex * inset =
1230                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
1231                 if (inset) {
1232                         inset->addDatabase(ev.argument);
1233                 }
1234         }
1235         break;
1236
1237         case LFUN_BIBDB_DEL:
1238         {
1239                 InsetBibtex * inset =
1240                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
1241                 if (inset)
1242                         inset->delDatabase(ev.argument);
1243         }
1244         break;
1245
1246         case LFUN_BIBTEX_STYLE:
1247         {
1248                 InsetBibtex * inset =
1249                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
1250                 if (inset)
1251                         inset->setOptions(ev.argument);
1252         }
1253         break;
1254
1255         case LFUN_CHILD_INSERT:
1256         {
1257                 InsetInclude::Params p;
1258                 if (!ev.argument.empty())
1259                         p.cparams.setFromString(ev.argument);
1260                 p.masterFilename_ = buffer_->fileName();
1261
1262                 InsetInclude * inset = new InsetInclude(p);
1263                 if (!insertInset(inset))
1264                         delete inset;
1265                 else {
1266                         updateInset(inset, true);
1267                         bv_->owner()->getDialogs().showInclude(inset);
1268                 }
1269         }
1270         break;
1271
1272         case LFUN_FLOAT_LIST:
1273                 if (tclass.floats().typeExist(ev.argument)) {
1274                         Inset * inset = new InsetFloatList(ev.argument);
1275                         if (!insertInset(inset, tclass.defaultLayoutName()))
1276                                 delete inset;
1277                 } else {
1278                         lyxerr << "Non-existent float type: "
1279                                << ev.argument << endl;
1280                 }
1281                 break;
1282
1283         case LFUN_THESAURUS_ENTRY:
1284         {
1285                 string arg = ev.argument;
1286
1287                 if (arg.empty()) {
1288                         arg = bv_->getLyXText()->selectionAsString(buffer_,
1289                                                                    false);
1290
1291                         // FIXME
1292                         if (arg.size() > 100 || arg.empty()) {
1293                                 // Get word or selection
1294                                 bv_->getLyXText()->selectWordWhenUnderCursor(bv_, LyXText::WHOLE_WORD);
1295                                 arg = bv_->getLyXText()->selectionAsString(buffer_, false);
1296                                 // FIXME: where is getLyXText()->unselect(bv_) ?
1297                         }
1298                 }
1299
1300                 bv_->owner()->getDialogs().showThesaurus(arg);
1301         }
1302                 break;
1303
1304         case LFUN_TRACK_CHANGES:
1305                 trackChanges();
1306                 break;
1307  
1308         case LFUN_MERGE_CHANGES:
1309                 owner_->getDialogs().showMergeChanges();
1310                 break;
1311  
1312         case LFUN_ACCEPT_ALL_CHANGES: {
1313                 bv_->update(bv_->text, BufferView::SELECT | BufferView::FITCUR);
1314                 bv_->text->setCursor(bv_, &(*bv_->buffer()->paragraphs.begin()), 0);
1315 #warning FIXME changes 
1316                 //moveCursorUpdate(false);
1317
1318                 while (lyxfind::findNextChange(bv_)) {
1319                         bv_->getLyXText()->acceptChange(bv_);
1320                 }
1321                 update(bv_->text,
1322                         BufferView::SELECT | BufferView::FITCUR | BufferView::CHANGE);
1323                 break;
1324         }
1325  
1326         case LFUN_REJECT_ALL_CHANGES: {
1327                 bv_->update(bv_->text, BufferView::SELECT | BufferView::FITCUR);
1328                 bv_->text->setCursor(bv_, &(*bv_->buffer()->paragraphs.begin()), 0);
1329 #warning FIXME changes 
1330                 //moveCursorUpdate(false);
1331
1332                 while (lyxfind::findNextChange(bv_)) {
1333                         bv_->getLyXText()->rejectChange(bv_);
1334                 }
1335                 update(bv_->text,
1336                         BufferView::SELECT | BufferView::FITCUR | BufferView::CHANGE);
1337                 break;
1338         }
1339  
1340         case LFUN_ACCEPT_CHANGE: {
1341                 bv_->getLyXText()->acceptChange(bv_);
1342                 update(bv_->text,
1343                         BufferView::SELECT | BufferView::FITCUR | BufferView::CHANGE);
1344                 break;
1345         }
1346
1347         case LFUN_REJECT_CHANGE: {
1348                 bv_->getLyXText()->rejectChange(bv_);
1349                 update(bv_->text,
1350                         BufferView::SELECT | BufferView::FITCUR | BufferView::CHANGE);
1351                 break;
1352         }
1353  
1354         case LFUN_UNKNOWN_ACTION:
1355                 ev.errorMessage(N_("Unknown function!"));
1356                 break;
1357
1358         default:
1359                 return bv_->getLyXText()->dispatch(FuncRequest(ev, bv_));
1360         } // end of switch
1361
1362         return true;
1363 }
1364
1365
1366 bool BufferView::Pimpl::insertInset(Inset * inset, string const & lout)
1367 {
1368         // if we are in a locking inset we should try to insert the
1369         // inset there otherwise this is a illegal function now
1370         if (bv_->theLockingInset()) {
1371                 if (bv_->theLockingInset()->insetAllowed(inset))
1372                         return bv_->theLockingInset()->insertInset(bv_, inset);
1373                 return false;
1374         }
1375
1376         // not quite sure if we want this...
1377         setCursorParUndo(bv_);
1378         freezeUndo();
1379
1380         beforeChange(bv_->text);
1381         if (!lout.empty()) {
1382                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
1383                 bv_->text->breakParagraph(bv_);
1384                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
1385
1386                 if (!bv_->text->cursor.par()->empty()) {
1387                         bv_->text->cursorLeft(bv_);
1388
1389                         bv_->text->breakParagraph(bv_);
1390                         update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
1391                 }
1392
1393                 string lres = lout;
1394                 LyXTextClass const & tclass =
1395                         buffer_->params.getLyXTextClass();
1396                 bool hasLayout = tclass.hasLayout(lres);
1397                 string lay = tclass.defaultLayoutName();
1398
1399                 if (hasLayout != false) {
1400                         // layout found
1401                         lay = lres;
1402                 } else {
1403                         // layout not fount using default
1404                         lay = tclass.defaultLayoutName();
1405                 }
1406
1407                 bv_->text->setLayout(bv_, lay);
1408
1409                 bv_->text->setParagraph(bv_, 0, 0,
1410                                    0, 0,
1411                                    VSpace(VSpace::NONE), VSpace(VSpace::NONE),
1412                                    Spacing(),
1413                                    LYX_ALIGN_LAYOUT,
1414                                    string(),
1415                                    0);
1416                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
1417         }
1418
1419         bv_->text->insertInset(bv_, inset);
1420         update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
1421
1422         unFreezeUndo();
1423         return true;
1424 }
1425
1426
1427 void BufferView::Pimpl::updateInset(Inset * inset, bool mark_dirty)
1428 {
1429         if (!inset || !available())
1430                 return;
1431
1432         // first check for locking insets
1433         if (bv_->theLockingInset()) {
1434                 if (bv_->theLockingInset() == inset) {
1435                         if (bv_->text->updateInset(bv_, inset)) {
1436                                 update();
1437                                 if (mark_dirty) {
1438                                         buffer_->markDirty();
1439                                 }
1440                                 updateScrollbar();
1441                                 return;
1442                         }
1443                 } else if (bv_->theLockingInset()->updateInsetInInset(bv_, inset)) {
1444                         if (bv_->text->updateInset(bv_,  bv_->theLockingInset())) {
1445                                 update();
1446                                 if (mark_dirty) {
1447                                         buffer_->markDirty();
1448                                 }
1449                                 updateScrollbar();
1450                                 return;
1451                         }
1452                 }
1453         }
1454
1455         // then check if the inset is a top_level inset (has no owner)
1456         // if yes do the update as always otherwise we have to update the
1457         // toplevel inset where this inset is inside
1458         Inset * tl_inset = inset;
1459         while (tl_inset->owner())
1460                 tl_inset = tl_inset->owner();
1461         hideCursor();
1462         if (tl_inset == inset) {
1463                 update(bv_->text, BufferView::UPDATE);
1464                 if (bv_->text->updateInset(bv_, inset)) {
1465                         if (mark_dirty) {
1466                                 update(bv_->text,
1467                                        BufferView::SELECT
1468                                        | BufferView::FITCUR
1469                                        | BufferView::CHANGE);
1470                         } else {
1471                                 update(bv_->text, SELECT);
1472                         }
1473                         return;
1474                 }
1475         } else if (static_cast<UpdatableInset *>(tl_inset)
1476                            ->updateInsetInInset(bv_, inset))
1477         {
1478                 if (bv_->text->updateInset(bv_, tl_inset)) {
1479                         update();
1480                         updateScrollbar();
1481                 }
1482         }
1483 }