]> git.lyx.org Git - lyx.git/blob - src/BufferView.C
35a33f578721b423060b0abcd01d291d1d441e66
[lyx.git] / src / BufferView.C
1 // -*- C++ -*-
2 /* This file is part of
3  * ====================================================== 
4  * 
5  *           LyX, The Document Processor
6  *        
7  *           Copyright 1995 Matthias Ettrich
8  *           Copyright 1995-2000 The LyX Team.
9  *
10  * ====================================================== */
11
12 #include <config.h>
13
14 #include <algorithm>
15
16 #include <cstdlib>
17 #include <csignal>
18
19 #include <unistd.h>
20 #include <sys/wait.h>
21
22 #include "support/lstrings.h"
23
24 #ifdef __GNUG__
25 #pragma implementation
26 #endif
27
28 #include "commandtags.h"
29 #include "BufferView.h"
30 #include "bufferlist.h"
31 #include "LyXView.h"
32 #include "lyxfunc.h"
33 #include "insets/lyxinset.h"
34 #include "insets/insetbib.h"
35 #include "minibuffer.h"
36 #include "lyxscreen.h"
37 #include "menus.h"
38
39 #include "debug.h"
40 #include "lyx_gui_misc.h"
41 #include "BackStack.h"
42 #include "lyxtext.h"
43 #include "lyx_cb.h"
44 #include "gettext.h"
45 #include "layout.h"
46 #include "TextCache.h"
47 #include "intl.h"
48 #include "lyxrc.h"
49 #include "lyxrow.h"
50 #include "WorkArea.h"
51 #include "font.h"
52 #include "BufferView_pimpl.h"
53
54 using std::pair;
55 using std::for_each;
56 using std::find_if;
57 using std::endl;
58
59 extern BufferList bufferlist;
60
61 void sigchldhandler(pid_t pid, int * status);
62
63 extern void SetXtermCursor(Window win);
64 extern bool input_prohibited;
65 extern bool selection_possible;
66 extern char ascii_type;
67 extern void MenuPasteSelection(char at);
68 extern void FreeUpdateTimer();
69
70 BufferView::BufferView(LyXView * o, int xpos, int ypos,
71                        int width, int height)
72         : pimpl_(new Pimpl(this, o, xpos, ypos, width, height))
73 {
74         text = 0;
75         the_locking_inset = 0;
76         inset_slept = false;
77 }
78
79
80 BufferView::~BufferView()
81 {
82         delete text;
83         delete pimpl_;
84 }
85
86
87 Buffer * BufferView::buffer() const
88 {
89         return pimpl_->buffer_;
90 }
91
92
93 LyXView * BufferView::owner() const
94 {
95         return pimpl_->owner_;
96 }
97
98
99 void BufferView::pushIntoUpdateList(Inset * i)
100 {
101         pimpl_->updatelist.push(i);
102 }
103
104
105 Painter & BufferView::painter() 
106 {
107         return pimpl_->workarea->getPainter();
108 }
109
110
111 void BufferView::buffer(Buffer * b)
112 {
113         lyxerr[Debug::INFO] << "Setting buffer in BufferView ("
114                             << b << ")" << endl;
115         if (pimpl_->buffer_) {
116                 insetSleep();
117                 pimpl_->buffer_->delUser(this);
118
119                 // Put the old text into the TextCache, but
120                 // only if the buffer is still loaded.
121                 // Also set the owner of the test to 0
122                 text->owner(0);
123                 textcache.add(text);
124                 if (lyxerr.debugging())
125                         textcache.show(lyxerr, "BufferView::buffer");
126                 
127                 text = 0;
128         }
129
130         // Set current buffer
131         pimpl_->buffer_ = b;
132
133         if (bufferlist.getState() == BufferList::CLOSING) return;
134         
135         // Nuke old image
136         // screen is always deleted when the buffer is changed.
137         delete pimpl_->screen;
138         pimpl_->screen = 0;
139
140         // If we are closing the buffer, use the first buffer as current
141         if (!pimpl_->buffer_) {
142                 pimpl_->buffer_ = bufferlist.first();
143         }
144
145         if (pimpl_->buffer_) {
146                 lyxerr[Debug::INFO] << "Buffer addr: " << pimpl_->buffer_ << endl;
147                 pimpl_->buffer_->addUser(this);
148                 pimpl_->owner_->getMenus()->showMenus();
149                 // If we don't have a text object for this, we make one
150                 if (text == 0)
151                         resizeCurrentBuffer();
152                 else {
153                         pimpl_->updateScreen();
154                         updateScrollbar();
155                 }
156                 pimpl_->screen->first = pimpl_->screen->TopCursorVisible();
157                 redraw();
158                 updateAllVisibleBufferRelatedPopups();
159                 insetWakeup();
160         } else {
161                 lyxerr[Debug::INFO] << "  No Buffer!" << endl;
162                 pimpl_->owner_->getMenus()->hideMenus();
163                 updateScrollbar();
164                 pimpl_->workarea->redraw();
165
166                 // Also remove all remaining text's from the testcache.
167                 // (there should not be any!) (if there is any it is a
168                 // bug!)
169                 if (lyxerr.debugging())
170                         textcache.show(lyxerr, "buffer delete all");
171                 textcache.clear();
172         }
173         // should update layoutchoice even if we don't have a buffer.
174         pimpl_->owner_->updateLayoutChoice();
175         pimpl_->owner_->getMiniBuffer()->Init();
176         pimpl_->owner_->updateWindowTitle();
177 }
178
179
180 void BufferView::resize(int xpos, int ypos, int width, int height)
181 {
182         pimpl_->workarea->resize(xpos, ypos, width, height);
183         update(3);
184         redraw();
185 }
186
187
188 void BufferView::resize()
189 {
190         // This will resize the buffer. (Asger)
191         if (pimpl_->buffer_)
192                 resizeCurrentBuffer();
193 }
194
195
196 void BufferView::redraw()
197 {
198         lyxerr[Debug::INFO] << "BufferView::redraw()" << endl;
199         pimpl_->workarea->redraw();
200 }
201
202
203 void BufferView::fitCursor()
204 {
205         if (pimpl_->screen) pimpl_->screen->FitCursor();
206         updateScrollbar();
207 }
208
209
210 void BufferView::update()
211 {
212         if (pimpl_->screen) pimpl_->screen->Update();
213 }
214
215
216 void BufferView::updateScrollbar()
217 {
218         /* If the text is smaller than the working area, the scrollbar
219          * maximum must be the working area height. No scrolling will 
220          * be possible */
221
222         if (!pimpl_->buffer_) {
223                 pimpl_->workarea->setScrollbar(0, 1.0);
224                 return;
225         }
226         
227         static long max2 = 0;
228         static long height2 = 0;
229
230         long cbth = 0;
231         long cbsf = 0;
232
233         if (text)
234                 cbth = text->height;
235         if (pimpl_->screen)
236                 cbsf = pimpl_->screen->first;
237
238         // check if anything has changed.
239         if (max2 == cbth &&
240             height2 == pimpl_->workarea->height() &&
241             pimpl_->current_scrollbar_value == cbsf)
242                 return; // no
243         max2 = cbth;
244         height2 = pimpl_->workarea->height();
245         pimpl_->current_scrollbar_value = cbsf;
246
247         if (cbth <= height2) { // text is smaller than screen
248                 pimpl_->workarea->setScrollbar(0, 1.0); // right?
249                 return;
250         }
251
252         long maximum_height = pimpl_->workarea->height() * 3 / 4 + cbth;
253         long value = cbsf;
254
255         // set the scrollbar
256         double hfloat = pimpl_->workarea->height();
257         double maxfloat = maximum_height;
258
259         float slider_size = 0.0;
260         int slider_value = value;
261
262         pimpl_->workarea->setScrollbarBounds(0, text->height - pimpl_->workarea->height());
263         double lineh = text->DefaultHeight();
264         pimpl_->workarea->setScrollbarIncrements(lineh);
265         if (maxfloat > 0.0) {
266                 if ((hfloat / maxfloat) * float(height2) < 3)
267                         slider_size = 3.0/float(height2);
268                 else
269                         slider_size = hfloat / maxfloat;
270         } else
271                 slider_size = hfloat;
272
273         pimpl_->workarea->setScrollbar(slider_value, slider_size / pimpl_->workarea->height());
274 }
275
276
277 void BufferView::redoCurrentBuffer()
278 {
279         lyxerr[Debug::INFO] << "BufferView::redoCurrentBuffer" << endl;
280         if (pimpl_->buffer_ && text) {
281                 resize();
282                 pimpl_->owner_->updateLayoutChoice();
283         }
284 }
285
286
287 int BufferView::resizeCurrentBuffer()
288 {
289         lyxerr[Debug::INFO] << "resizeCurrentBuffer" << endl;
290         
291         LyXParagraph * par = 0;
292         LyXParagraph * selstartpar = 0;
293         LyXParagraph * selendpar = 0;
294         int pos = 0;
295         int selstartpos = 0;
296         int selendpos = 0;
297         int selection = 0;
298         int mark_set = 0;
299
300         ProhibitInput();
301
302         pimpl_->owner_->getMiniBuffer()->Set(_("Formatting document..."));   
303
304         if (text) {
305                 par = text->cursor.par;
306                 pos = text->cursor.pos;
307                 selstartpar = text->sel_start_cursor.par;
308                 selstartpos = text->sel_start_cursor.pos;
309                 selendpar = text->sel_end_cursor.par;
310                 selendpos = text->sel_end_cursor.pos;
311                 selection = text->selection;
312                 mark_set = text->mark_set;
313                 delete text;
314                 text = new LyXText(this, pimpl_->workarea->workWidth(), pimpl_->buffer_);
315         } else {
316                 // See if we have a text in TextCache that fits
317                 // the new buffer_ with the correct width.
318                 text = textcache.findFit(pimpl_->buffer_, pimpl_->workarea->workWidth());
319                 if (text) {
320                         if (lyxerr.debugging()) {
321                                 lyxerr << "Found a LyXText that fits:\n";
322                                 textcache.show(lyxerr, text);
323                         }
324                         // Set the owner of the newly found text
325                         text->owner(this);
326                         if (lyxerr.debugging())
327                                 textcache.show(lyxerr, "resizeCurrentBuffer");
328                 } else {
329                         text = new LyXText(this, pimpl_->workarea->workWidth(), pimpl_->buffer_);
330                 }
331         }
332         pimpl_->updateScreen();
333
334         if (par) {
335                 text->selection = true;
336                 /* at this point just to avoid the Delete-Empty-Paragraph
337                  * Mechanism when setting the cursor */
338                 text->mark_set = mark_set;
339                 if (selection) {
340                         text->SetCursor(selstartpar, selstartpos);
341                         text->sel_cursor = text->cursor;
342                         text->SetCursor(selendpar, selendpos);
343                         text->SetSelection();
344                         text->SetCursor(par, pos);
345                 } else {
346                         text->SetCursor(par, pos);
347                         text->sel_cursor = text->cursor;
348                         text->selection = false;
349                 }
350         }
351         pimpl_->screen->first = pimpl_->screen->TopCursorVisible(); /* this will scroll the
352                                                      * screen such that the
353                                                      * cursor becomes
354                                                      * visible */ 
355         updateScrollbar();
356         redraw();
357         pimpl_->owner_->getMiniBuffer()->Init();
358         setState();
359         AllowInput();
360
361         // Now if the title form still exist kill it
362         TimerCB(0, 0);
363
364         return 0;
365 }
366
367
368 void BufferView::gotoError()
369 {
370         if (!pimpl_->screen)
371                 return;
372    
373         pimpl_->screen->HideCursor();
374         beforeChange();
375         update(-2);
376         LyXCursor tmp;
377
378         if (!text->GotoNextError()) {
379                 if (text->cursor.pos 
380                     || text->cursor.par != text->FirstParagraph()) {
381                         tmp = text->cursor;
382                         text->cursor.par = text->FirstParagraph();
383                         text->cursor.pos = 0;
384                         if (!text->GotoNextError()) {
385                                 text->cursor = tmp;
386                                 pimpl_->owner_->getMiniBuffer()
387                                         ->Set(_("No more errors"));
388                                 LyXBell();
389                         }
390                 } else {
391                         pimpl_->owner_->getMiniBuffer()->Set(_("No more errors"));
392                         LyXBell();
393                 }
394         }
395         update(0);
396         text->sel_cursor = text->cursor;
397 }
398
399
400 extern "C" {
401         void C_BufferView_CursorToggleCB(FL_OBJECT * ob, long buf)
402         {
403                 BufferView::cursorToggleCB(ob, buf);
404         }
405 }
406
407
408 // Callback for scrollbar up button
409 void BufferView::upCB(long time, int button)
410 {
411         if (pimpl_->buffer_ == 0) return;
412
413         switch (button) {
414         case 3:
415                 pimpl_->scrollUpOnePage();
416                 break;
417         case 2:
418                 pimpl_->scrollDownOnePage();
419                 break;
420         default:
421                 pimpl_->scrollUp(time);
422                 break;
423         }
424 }
425
426
427 static inline
428 void waitForX()
429 {
430         XSync(fl_get_display(), 0);
431 }
432
433
434 // Callback for scrollbar slider
435 void BufferView::scrollCB(double value)
436 {
437         extern bool cursor_follows_scrollbar;
438         
439         if (pimpl_->buffer_ == 0) return;
440
441         pimpl_->current_scrollbar_value = long(value);
442         if (pimpl_->current_scrollbar_value < 0)
443                 pimpl_->current_scrollbar_value = 0;
444    
445         if (!pimpl_->screen)
446                 return;
447
448         pimpl_->screen->Draw(pimpl_->current_scrollbar_value);
449
450         if (cursor_follows_scrollbar) {
451                 LyXText * vbt = text;
452                 int height = vbt->DefaultHeight();
453                 
454                 if (vbt->cursor.y < pimpl_->screen->first + height) {
455                         vbt->SetCursorFromCoordinates(0,
456                                                       pimpl_->screen->first +
457                                                       height);
458                 } else if (vbt->cursor.y >
459                            pimpl_->screen->first + pimpl_->workarea->height() - height) {
460                         vbt->SetCursorFromCoordinates(0,
461                                                       pimpl_->screen->first +
462                                                       pimpl_->workarea->height()  -
463                                                       height);
464                 }
465         }
466         waitForX();
467 }
468
469
470 // Callback for scrollbar down button
471 void BufferView::downCB(long time, int button)
472 {
473         if (pimpl_->buffer_ == 0) return;
474         
475         switch (button) {
476         case 2:
477                 pimpl_->scrollUpOnePage();
478                 break;
479         case 3:
480                 pimpl_->scrollDownOnePage();
481                 break;
482         default:
483                 pimpl_->scrollDown(time);
484                 break;
485         }
486 }
487
488
489 void BufferView::workAreaMotionNotify(int x, int y, unsigned int state)
490 {
491         pimpl_->workAreaMotionNotify(x, y, state);
492 }
493
494
495 extern int bibitemMaxWidth(Painter &, LyXFont const &);
496
497
498 ///  Single-click on work area
499 void BufferView::workAreaButtonPress(int xpos, int ypos, unsigned int button)
500 {
501         pimpl_->workAreaButtonPress(xpos, ypos, button);
502 }
503
504
505 void BufferView::doubleClick(int x, int y, unsigned int button) 
506 {
507         pimpl_->doubleClick(x, y, button);
508 }
509
510
511 void BufferView::tripleClick(int x, int y, unsigned int button)
512 {
513         pimpl_->tripleClick(x, y, button);
514 }
515
516
517 void BufferView::workAreaButtonRelease(int x, int y, unsigned int button)
518 {
519         pimpl_->workAreaButtonRelease(x, y, button);
520 }
521
522
523 void BufferView::workAreaExpose()
524 {
525         pimpl_->workAreaExpose();
526 }
527
528
529 // Callback for cursor timer
530 void BufferView::cursorToggleCB(FL_OBJECT * ob, long)
531 {
532         BufferView * view = static_cast<BufferView*>(ob->u_vdata);
533         
534         // Quite a nice place for asyncron Inset updating, isn't it?
535         // Actually no! This is run even if no buffer exist... so (Lgb)
536         if (view && !view->pimpl_->buffer_) {
537                 goto set_timer_and_return;
538         }
539
540         // NOTE:
541         // On my quest to solve the gs render hangups I am now
542         // disabling the SIGHUP completely, and will do a wait
543         // now and then instead. If the guess that xforms somehow
544         // destroys something is true, this is likely (hopefully)
545         // to solve the problem...at least I hope so. Lgb
546
547         // ...Ok this seems to work...at least it does not make things
548         // worse so far. However I still see gs processes that hangs.
549         // I would really like to know _why_ they are hanging. Anyway
550         // the solution without the SIGCHLD handler seems to be easier
551         // to debug.
552
553         // When attaching gdb to a a running gs that hangs it shows
554         // that it is waiting for input(?) Is it possible for us to
555         // provide that input somehow? Or figure what it is expecing
556         // to read?
557
558         // One solution is to, after some time, look if there are some
559         // old gs processes still running and if there are: kill them
560         // and re render.
561
562         // Another solution is to provide the user an option to rerender
563         // a picture. This would, for the picture in question, check if
564         // there is a gs running for it, if so kill it, and start a new
565         // rendering process.
566
567         // these comments posted to lyx@via
568         {
569                 int status = 1;
570                 int pid = waitpid(static_cast<pid_t>(0), &status, WNOHANG);
571                 if (pid == -1) // error find out what is wrong
572                         ; // ignore it for now.
573                 else if (pid > 0)
574                         sigchldhandler(pid, &status);
575         }
576
577         view->pimpl_->updatelist.update(view);
578         
579         if (view && !view->pimpl_->screen) {
580                 goto set_timer_and_return;
581         }
582
583         if (view->pimpl_->lyx_focus && view->pimpl_->work_area_focus) {
584                 if (!view->the_locking_inset) {
585                         view->pimpl_->screen->CursorToggle();
586                 } else {
587                         view->the_locking_inset->
588                                 ToggleInsetCursor(view);
589                 }
590                 goto set_timer_and_return;
591         } else {
592                 // Make sure that the cursor is visible.
593                 if (!view->the_locking_inset) {
594                         view->pimpl_->screen->ShowCursor();
595                 } else {
596                         if (!view->the_locking_inset->isCursorVisible())
597                                 view->the_locking_inset->
598                                         ToggleInsetCursor(view);
599                 }
600                 // This is only run when work_area_focus or lyx_focus is false.
601                 Window tmpwin;
602                 int tmp;
603                 XGetInputFocus(fl_display, &tmpwin, &tmp);
604                 // Commenting this out, we have not had problems with this
605                 // for a long time. We will probably work on this code later
606                 // and we can reenable this debug code then. Now it only
607                 // anoying when debugging. (Lgb)
608                 //if (lyxerr.debugging(Debug::INFO)) {
609                 //      lyxerr << "tmpwin: " << tmpwin
610                 //             << "\nwindow: " << view->owner_->getForm()->window
611                 //             << "\nwork_area_focus: " << view->work_area_focus
612                 //             << "\nlyx_focus      : " << view->lyx_focus
613                 //             << endl;
614                 //}
615                 if (tmpwin != view->pimpl_->owner_->getForm()->window) {
616                         view->pimpl_->lyx_focus = false;
617                         goto skip_timer;
618                 } else {
619                         view->pimpl_->lyx_focus = true;
620                         if (!view->pimpl_->work_area_focus)
621                                 goto skip_timer;
622                         else
623                                 goto set_timer_and_return;
624                 }
625         }
626
627   set_timer_and_return:
628         fl_set_timer(ob, 0.4);
629   skip_timer:
630         return;
631 }
632
633
634 void BufferView::workAreaSelectionNotify(Window win, XEvent * event)
635 {
636         pimpl_->workAreaSelectionNotify(win, event);
637 }
638
639
640 void BufferView::cursorPrevious()
641 {
642         if (!text->cursor.row->previous) return;
643         
644         long y = pimpl_->screen->first;
645         Row * cursorrow = text->cursor.row;
646         text->SetCursorFromCoordinates(text->cursor.x_fix, y);
647         text->FinishUndo();
648         // This is to allow jumping over large insets
649         if ((cursorrow == text->cursor.row))
650                 text->CursorUp();
651         
652         if (text->cursor.row->height < pimpl_->workarea->height())
653                 pimpl_->screen->Draw(text->cursor.y
654                                   - text->cursor.row->baseline
655                                   + text->cursor.row->height
656                                   - pimpl_->workarea->height() + 1 );
657 }
658
659
660 void BufferView::cursorNext()
661 {
662         if (!text->cursor.row->next) return;
663         
664         long y = pimpl_->screen->first;
665         text->GetRowNearY(y);
666         Row * cursorrow = text->cursor.row;
667         text->SetCursorFromCoordinates(text->cursor.x_fix, y
668                                        + pimpl_->workarea->height());
669         text->FinishUndo();
670         // This is to allow jumping over large insets
671         if ((cursorrow == text->cursor.row))
672                 text->CursorDown();
673         
674         if (text->cursor.row->height < pimpl_->workarea->height())
675                 pimpl_->screen->Draw(text->cursor.y
676                                   - text->cursor.row->baseline);
677 }
678
679
680 bool BufferView::available() const
681 {
682         if (pimpl_->buffer_ && text) return true;
683         return false;
684 }
685
686
687 void BufferView::beforeChange()
688 {
689         toggleSelection();
690         text->ClearSelection();
691         FreeUpdateTimer();
692 }
693
694
695 void BufferView::savePosition()
696 {
697         pimpl_->backstack.push(buffer()->fileName(),
698                        text->cursor.x,
699                        text->cursor.y);
700 }
701
702
703 void BufferView::restorePosition()
704 {
705         if (pimpl_->backstack.empty()) return;
706         
707         int  x, y;
708         string fname = pimpl_->backstack.pop(&x, &y);
709         
710         beforeChange();
711         Buffer * b = bufferlist.exists(fname) ?
712                 bufferlist.getBuffer(fname) :
713                 bufferlist.loadLyXFile(fname); // don't ask, just load it
714         buffer(b);
715         text->SetCursorFromCoordinates(x, y);
716         update(0);
717
718
719
720 void BufferView::update(signed char f)
721 {
722         owner()->updateLayoutChoice();
723
724         if (!text->selection && f > -3)
725                 text->sel_cursor = text->cursor;
726         
727         FreeUpdateTimer();
728         text->FullRebreak();
729
730         update();
731
732         if (f != 3 && f != -3) {
733                 fitCursor();
734                 updateScrollbar();
735         }
736
737         if (f == 1 || f == -1) {
738                 if (buffer()->isLyxClean()) {
739                         buffer()->markDirty();
740                         owner()->getMiniBuffer()->setTimer(4);
741                 } else {
742                         buffer()->markDirty();
743                 }
744         }
745 }
746
747
748 void BufferView::smallUpdate(signed char f)
749 {
750         pimpl_->screen->SmallUpdate();
751         if (pimpl_->screen->TopCursorVisible()
752             != pimpl_->screen->first) {
753                 update(f);
754                 return;
755         }
756
757         fitCursor();
758         updateScrollbar();
759
760         if (!text->selection)
761                 text->sel_cursor = text->cursor;
762
763         if (f == 1 || f == -1) {
764                 if (buffer()->isLyxClean()) {
765                         buffer()->markDirty();
766                         owner()->getMiniBuffer()->setTimer(4);
767                 } else {
768                         buffer()->markDirty();
769                 }
770         }
771 }
772
773
774 void BufferView::setState()
775 {
776         if (!lyxrc.rtl_support)
777                 return;
778
779         if (text->real_current_font.isVisibleRightToLeft()) {
780                 if (pimpl_->owner_->getIntl()->primarykeymap)
781                         pimpl_->owner_->getIntl()->KeyMapSec();
782         } else {
783                 if (!pimpl_->owner_->getIntl()->primarykeymap)
784                         pimpl_->owner_->getIntl()->KeyMapPrim();
785         }
786 }
787
788
789 void BufferView::insetSleep()
790 {
791         if (the_locking_inset && !inset_slept) {
792                 the_locking_inset->GetCursorPos(slx, sly);
793                 the_locking_inset->InsetUnlock(this);
794                 inset_slept = true;
795         }
796 }
797
798
799 void BufferView::insetWakeup()
800 {
801         if (the_locking_inset && inset_slept) {
802                 the_locking_inset->Edit(this, slx, sly, 0);
803                 inset_slept = false;
804         }
805 }
806
807
808 void BufferView::insetUnlock()
809 {
810         if (the_locking_inset) {
811                 if (!inset_slept) the_locking_inset->InsetUnlock(this);
812                 the_locking_inset = 0;
813                 text->FinishUndo();
814                 inset_slept = false;
815         }
816 }
817
818
819 bool BufferView::focus() const
820 {
821         return pimpl_->workarea->hasFocus();
822 }
823
824
825 void BufferView::focus(bool f)
826 {
827         if (f) pimpl_->workarea->setFocus();
828 }
829
830
831 bool BufferView::active() const
832 {
833         return pimpl_->workarea->active();
834 }
835
836
837 Painter & BufferView::getPainter() const
838 {
839     return pimpl_->workarea->getPainter();
840 }
841
842
843 unsigned short BufferView::paperWidth() const
844 {
845     return text->paperWidth();
846 }
847
848
849 bool BufferView::belowMouse() const 
850 {
851         return pimpl_->workarea->belowMouse();
852 }
853
854
855 void BufferView::showCursor()
856 {
857         if (pimpl_->screen)
858                 pimpl_->screen->ShowCursor();
859 }
860
861
862 void BufferView::hideCursor()
863 {
864         if (pimpl_->screen)
865                 pimpl_->screen->HideCursor();
866 }
867
868
869 void BufferView::toggleSelection(bool b)
870 {
871         if (pimpl_->screen)
872                 pimpl_->screen->ToggleSelection(b);
873 }
874
875
876 void BufferView::toggleToggle()
877 {
878         if (pimpl_->screen)
879                 pimpl_->screen->ToggleToggle();
880 }
881
882
883 void BufferView::center() 
884 {
885         beforeChange();
886         if (text->cursor.y > pimpl_->workarea->height() / 2) {
887                 pimpl_->screen->Draw(text->cursor.y - pimpl_->workarea->height() / 2);
888         } else {
889                 pimpl_->screen->Draw(0);
890         }
891         update(0);
892         redraw();
893 }