]> git.lyx.org Git - features.git/blob - src/BufferView.C
removed some dead code from BufferView, added a check for kpsewhich in configure...
[features.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-1999 The LyX Team.
9  *
10  * ======================================================*/
11
12 #include <config.h>
13
14 #include <cstdlib>
15 #include <csignal>
16
17 #include <unistd.h>
18 #include <sys/wait.h>
19
20 #include "support/lstrings.h"
21
22 #ifdef __GNUG__
23 #pragma implementation
24 #endif
25
26 #include "commandtags.h"
27 #include "BufferView.h"
28 #include "bufferlist.h"
29 #include "LyXView.h"
30 #include "lyxfunc.h"
31 #include "insets/lyxinset.h"
32 #include "minibuffer.h"
33 #include "lyxscreen.h"
34 #include "up.xpm"
35 #include "down.xpm"
36 #include "error.h"
37 #include "lyxdraw.h"
38 #include "lyx_gui_misc.h"
39 #include "BackStack.h"
40 #include "lyxtext.h"
41 #include "lyx_cb.h"
42 #include "gettext.h"
43
44 extern BufferList bufferlist;
45 void sigchldhandler(pid_t pid, int *status);
46
47 extern void SetXtermCursor(Window win);
48 extern bool input_prohibited;
49 extern bool selection_possible;
50 extern void BeforeChange();
51 extern char ascii_type;
52 extern int UnlockInset(UpdatableInset* inset);
53 extern void ToggleFloat();
54 extern void MenuPasteSelection(char at);
55 extern InsetUpdateStruct *InsetUpdateList;
56 extern void UpdateInsetUpdateList();
57
58 // This is _very_ temporary
59 FL_OBJECT *figinset_canvas;
60
61 BufferView::BufferView(LyXView *o, int xpos, int ypos,
62                        int width, int height)
63         : _owner(o)
64 {
65         _buffer = 0;
66         
67         screen = 0;
68         work_area = 0;
69         figinset_canvas = 0;
70         scrollbar = 0;
71         button_down = 0;
72         button_up = 0;
73         timer_cursor = 0;
74         current_scrollbar_value = 0;
75         create_view(xpos, ypos, width, height);
76         // Activate the timer for the cursor 
77         fl_set_timer(timer_cursor, 0.4);
78         fl_set_focus_object(_owner->getForm(), work_area);
79         work_area_focus = true;
80         lyx_focus = false;
81         backstack = new BackStack(16);
82 }
83
84
85 BufferView::~BufferView()
86 {
87         delete backstack;
88 }   
89
90
91 void BufferView::setBuffer(Buffer *b)
92 {
93         lyxerr.debug("Setting buffer in BufferView");
94         if (_buffer) {
95                 _buffer->InsetSleep();
96                 _buffer->delUser(this);
97         }
98
99         // Set current buffer
100         _buffer = b;
101
102         if (bufferlist.getState() == BufferList::CLOSING) return;
103         
104         // Nuke old image
105         if (screen)
106                 delete screen;
107         screen = 0;
108
109         // If we are closing the buffer, use the first buffer as current
110         if (!_buffer) {
111                 _buffer = bufferlist.first();
112         }
113
114         if (_buffer) {
115                 lyxerr.debug(string("  Buffer addr: ") + tostr(_buffer));
116                 _buffer->addUser(this);
117                 _owner->getMenus()->showMenus();
118                 // If we don't have a text object for this, we make one
119                 if (_buffer->text == 0)
120                         resizeCurrentBuffer();
121                 else {
122                         updateScreen();
123                         updateScrollbar();
124                 }
125                 screen->first = screen->TopCursorVisible();
126                 redraw();
127                 updateAllVisibleBufferRelatedPopups();
128                 _buffer->InsetWakeup();
129         } else {
130                 lyxerr.debug("  No Buffer!");
131                 _owner->getMenus()->hideMenus();
132                 //workAreaExpose();
133                 updateScrollbar();
134                 fl_redraw_object(work_area);
135         }
136         // should update layoutchoice even if we don't have a buffer.
137         _owner->updateLayoutChoice();
138         _owner->getMiniBuffer()->Init();
139         _owner->updateWindowTitle();
140 }
141
142
143 void BufferView::updateScreen()
144 {
145         // Regenerate the screen.
146         if (screen)
147                 delete screen;
148         screen = new LyXScreen(FL_ObjWin(work_area),
149                                work_area->w,
150                                work_area->h,
151                                work_area->x,
152                                work_area->y,
153                                _buffer->text);
154 }
155
156
157 void BufferView::resize()
158 {
159         // This will resize the buffer. (Asger)
160         if (_buffer)
161                 resizeCurrentBuffer();
162 }
163
164
165 static bool lgb_hack = false;
166
167 void BufferView::redraw()
168 {
169         lyxerr.debug("BufferView::redraw()");
170         lgb_hack = true;
171         fl_redraw_object(work_area);
172         fl_redraw_object(scrollbar);
173         fl_redraw_object(button_down);
174         fl_redraw_object(button_up);
175         lgb_hack = false;
176 }
177
178
179 void BufferView::fitCursor()
180 {
181         if (screen) screen->FitCursor();
182 }
183
184
185 void BufferView::update()
186 {
187         if (screen) screen->Update();
188 }
189
190
191 void BufferView::updateScrollbar()
192 {
193         /* If the text is smaller than the working area, the scrollbar
194          * maximum must be the working area height. No scrolling will 
195          * be possible */
196
197         if (!_buffer) {
198                 fl_set_slider_value(scrollbar, 0);
199                 fl_set_slider_size(scrollbar, scrollbar->h);
200                 return;
201         }
202         
203         static long max2 = 0;
204         static long height2 = 0;
205
206         long cbth = 0;
207         long cbsf = 0;
208
209         if (_buffer->text)
210                 cbth = _buffer->text->height;
211         if (screen)
212                 cbsf = screen->first;
213
214         // check if anything has changed.
215         if (max2 == cbth &&
216             height2 == work_area->h &&
217             current_scrollbar_value == cbsf)
218                 return;       // no
219         
220         max2 = cbth;
221         height2 = work_area->h;
222         current_scrollbar_value = cbsf;
223
224         if (cbth <= height2) { // text is smaller than screen
225                 fl_set_slider_size(scrollbar, scrollbar->h);
226                 return;
227         }
228         
229         long maximum_height = work_area->h * 3/4 + cbth;
230         long value = cbsf;
231
232         /* set the scrollbar */
233         double hfloat = work_area->h;
234         double maxfloat = maximum_height;
235    
236         fl_set_slider_value(scrollbar, value);
237         fl_set_slider_bounds(scrollbar, 0,
238                              maximum_height - work_area->h);
239 #if FL_REVISION > 85
240         double lineh = _buffer->text->DefaultHeight();
241         fl_set_slider_increment(scrollbar,work_area->h-lineh,lineh);
242 #endif
243         if (maxfloat>0){
244                 if ((hfloat/maxfloat) * (float) height2 < 3)
245                         fl_set_slider_size(scrollbar,
246                                            3/(float)height2);
247                 else
248                         fl_set_slider_size(scrollbar,
249                                            hfloat/maxfloat);
250         } else
251                 fl_set_slider_size(scrollbar, hfloat);
252         fl_set_slider_precision(scrollbar, 0);
253 }
254
255
256 void BufferView::redoCurrentBuffer()
257 {
258         lyxerr.debug("BufferView::redoCurrentBuffer");
259         if (_buffer && _buffer->text) {
260                 resize();
261                 _owner->updateLayoutChoice();
262         }
263 }
264
265
266 int BufferView::resizeCurrentBuffer()
267 {
268         lyxerr.debug("resizeCurrentBuffer");
269         
270         LyXParagraph *par = 0;
271         LyXParagraph *selstartpar = 0;
272         LyXParagraph *selendpar = 0;
273         int pos = 0;
274         int selstartpos = 0;
275         int selendpos = 0;
276         int selection = 0;
277         int mark_set = 0;
278
279         ProhibitInput();
280
281         _owner->getMiniBuffer()->Set(_("Formatting document..."));   
282
283         if (_buffer->text) {
284                 par = _buffer->text->cursor.par;
285                 pos = _buffer->text->cursor.pos;
286                 selstartpar = _buffer->text->sel_start_cursor.par;
287                 selstartpos = _buffer->text->sel_start_cursor.pos;
288                 selendpar = _buffer->text->sel_end_cursor.par;
289                 selendpos = _buffer->text->sel_end_cursor.pos;
290                 selection = _buffer->text->selection;
291                 mark_set = _buffer->text->mark_set;
292                 delete _buffer->text;
293         }
294         _buffer->text = new LyXText(work_area->w, _buffer);
295
296         updateScreen();
297    
298         if (par) {
299                 _buffer->text->selection = true;
300                 /* at this point just
301                  * to avoid the Delete-
302                  * Empty-Paragraph
303                  * Mechanism when
304                  * setting the cursor */
305                 _buffer->text->mark_set = mark_set;
306                 if (selection) {
307                         _buffer->text->SetCursor(selstartpar, selstartpos);
308                         _buffer->text->sel_cursor = _buffer->text->cursor;
309                         _buffer->text->SetCursor(selendpar, selendpos);
310                         _buffer->text->SetSelection();
311                         _buffer->text->SetCursor(par, pos);
312                 } else {
313                         _buffer->text->SetCursor(par, pos);
314                         _buffer->text->sel_cursor = _buffer->text->cursor;
315                         _buffer->text->selection = false;
316                 }
317         }
318         screen->first = screen->TopCursorVisible(); /* this will scroll the
319                                                      * screen such that the
320                                                      * cursor becomes
321                                                      * visible */ 
322         updateScrollbar();
323         redraw();
324         _owner->getMiniBuffer()->Init();
325         AllowInput();
326
327         // Now if the title form still exist kill it
328         TimerCB(0,0);
329
330         return 0;
331 }
332
333
334 void BufferView::gotoError()
335 {
336         if (!screen)
337                 return;
338    
339         screen->HideCursor();
340         BeforeChange();
341         _buffer->update(-2);
342         LyXCursor tmp;
343    
344         if (!_buffer->text->GotoNextError()) {
345                 if (_buffer->text->cursor.pos 
346                     || _buffer->text->cursor.par !=
347                     _buffer->text->FirstParagraph()) {
348                         tmp = _buffer->text->cursor;
349                         _buffer->text->cursor.par =
350                                 _buffer->text->FirstParagraph();
351                         _buffer->text->cursor.pos = 0;
352                         if (!_buffer->text->GotoNextError()) {
353                                 _buffer->text->cursor = tmp;
354                                 _owner->getMiniBuffer()->Set(_("No more errors"));
355                                 LyXBell();
356                         }
357                 } else {
358                         _owner->getMiniBuffer()->Set(_("No more errors"));
359                         LyXBell();
360                 }
361         }
362         _buffer->update(0);
363         _buffer->text->sel_cursor =
364                 _buffer->text->cursor;
365 }
366
367
368 void BufferView::create_view(int xpos, int ypos, int width, int height)
369 {
370         FL_OBJECT *obj;
371         const int bw = abs(fl_get_border_width());
372
373         // a hack for the figinsets (Matthias)
374         // This one first, then it will probably be invisible. (Lgb)
375         ::figinset_canvas = figinset_canvas = obj =
376                 fl_add_canvas(FL_NORMAL_CANVAS,
377                               xpos + 1,
378                               ypos + 1,1,1,"");
379         fl_set_object_boxtype(obj,FL_NO_BOX);
380         fl_set_object_resize(obj, FL_RESIZE_ALL);
381         fl_set_object_gravity(obj, NorthWestGravity, NorthWestGravity);
382
383         // a box
384         obj = fl_add_box(FL_BORDER_BOX, xpos, ypos,
385                          width - 15,
386                          height,"");
387         fl_set_object_resize(obj, FL_RESIZE_ALL);
388         fl_set_object_gravity(obj, NorthWestGravity, SouthEastGravity);
389
390         // the free object
391         work_area = obj = fl_add_free(FL_INPUT_FREE,
392                                       xpos +bw, ypos+bw,
393                                       width-15-2*bw /* scrollbarwidth */,
394                                       height-2*bw,"",
395                                       work_area_handler);
396         obj->wantkey = FL_KEY_TAB;
397         obj->u_vdata = this; /* This is how we pass the BufferView
398                                        to the work_area_handler. */
399         fl_set_object_boxtype(obj,FL_DOWN_BOX);
400         fl_set_object_resize(obj, FL_RESIZE_ALL);
401         fl_set_object_gravity(obj, NorthWestGravity, SouthEastGravity);
402
403         //
404         // THE SCROLLBAR
405         //
406
407         // up - scrollbar button
408 #if FL_REVISION > 85
409         fl_set_border_width(-1);
410 #else
411         fl_set_border_width(-2); // to get visible feedback
412 #endif
413         button_up = obj = fl_add_pixmapbutton(FL_TOUCH_BUTTON,
414                                               width-15+4*bw,
415                                               ypos,
416                                               15,15,"");
417         fl_set_object_boxtype(obj,FL_UP_BOX);
418         fl_set_object_color(obj,FL_MCOL,FL_BLUE);
419         fl_set_object_resize(obj, FL_RESIZE_ALL);
420         fl_set_object_gravity(obj,NorthEastGravity, NorthEastGravity);
421         fl_set_object_callback(obj,UpCB,(long)this);
422         fl_set_pixmapbutton_data(obj, up_xpm);
423
424 #if FL_REVISION >85
425         // Remove the blue feedback rectangle
426         fl_set_pixmapbutton_focus_outline(obj,0);
427 #endif  
428
429         // the scrollbar slider
430         fl_set_border_width(-bw);
431         scrollbar = obj = fl_add_slider(FL_VERT_SLIDER,
432                                         width-15+4*bw,
433                                         ypos + 15,
434                                         15,height-30,"");
435         fl_set_object_color(obj,FL_COL1,FL_MCOL);
436         fl_set_object_boxtype(obj, FL_UP_BOX);
437         fl_set_object_resize(obj, FL_RESIZE_ALL);
438         fl_set_object_gravity(obj, NorthEastGravity, SouthEastGravity);
439         fl_set_object_callback(obj,ScrollCB,(long)this);
440         
441         // down - scrollbar button
442 #if FL_REVISION > 85
443         fl_set_border_width(-1);
444 #else
445         fl_set_border_width(-2); // to get visible feedback
446 #endif
447         button_down = obj = fl_add_pixmapbutton(FL_TOUCH_BUTTON,
448                                                       width-15+4*bw,
449                                                       ypos + height-15,
450                                                       15,15,"");
451         fl_set_object_boxtype(obj,FL_UP_BOX);
452         fl_set_object_color(obj,FL_MCOL,FL_BLUE);
453         fl_set_object_resize(obj, FL_RESIZE_ALL);
454         fl_set_object_gravity(obj, SouthEastGravity, SouthEastGravity);
455         fl_set_object_callback(obj,DownCB,(long)this);
456         fl_set_pixmapbutton_data(obj, down_xpm);
457         fl_set_border_width(-bw);
458
459 #if FL_REVISION >85
460         // Remove the blue feedback rectangle
461         fl_set_pixmapbutton_focus_outline(obj,0);
462 #endif  
463
464         //
465         // TIMERS
466         //
467         
468         // timer_cursor
469         timer_cursor = obj = fl_add_timer(FL_HIDDEN_TIMER,
470                                           0,0,0,0,"Timer");
471         fl_set_object_callback(obj,CursorToggleCB,0);
472         obj->u_vdata = this;
473 }
474
475
476 // Callback for scrollbar up button
477 void BufferView::UpCB(FL_OBJECT *ob, long buf)
478 {
479         BufferView *view = (BufferView*) buf;
480         
481         if (view->_buffer == 0) return;
482
483         const XEvent*ev2;
484         static long time = 0;
485         ev2 = fl_last_event();
486         if (ev2->type == ButtonPress || ev2->type == ButtonRelease) 
487                 time = 0;
488         int button = fl_get_button_numb(ob);
489         switch (button) {
490         case 3:
491                 view->ScrollUpOnePage(time++); break;
492         case 2:
493                 view->ScrollDownOnePage(time++); break;
494         default:
495                 view->ScrollUp(time++); break;
496         }
497 }
498
499
500 static
501 void waitForX()
502 {
503         static Window w = 0;
504         static Atom a = 0;
505         if (!a)
506                 a = XInternAtom(fl_display, "WAIT_FOR_X", False);
507         if (w == 0) {
508                 int mask;
509                 XSetWindowAttributes attr;
510                 mask = CWOverrideRedirect;
511                 attr.override_redirect = 1;
512                 w = XCreateWindow(fl_display, fl_root,
513                                   0, 0, 1, 1, 0, CopyFromParent,
514                                   InputOnly, CopyFromParent, mask, &attr);
515                 XSelectInput(fl_display, w, PropertyChangeMask);
516                 XMapWindow(fl_display, w);
517         }
518         static XEvent ev;
519         XChangeProperty(fl_display, w, a, a, 8,
520                         PropModeAppend, (unsigned char *)"", 0);
521         XWindowEvent(fl_display, w, PropertyChangeMask, &ev);
522 }
523
524
525 // Callback for scrollbar slider
526 void BufferView::ScrollCB(FL_OBJECT *ob, long buf)
527 {
528         BufferView *view = (BufferView*) buf;
529         extern bool cursor_follows_scrollbar;
530         
531         if (view->_buffer == 0) return;
532
533         view->current_scrollbar_value = (long)fl_get_slider_value(ob);
534         if (view->current_scrollbar_value < 0)
535                 view->current_scrollbar_value = 0;
536    
537         if (!view->screen)
538                 return;
539
540         view->screen->Draw(view->current_scrollbar_value);
541
542         if (cursor_follows_scrollbar) {
543                 LyXText * vbt = view->_buffer->text;
544                 int height = vbt->DefaultHeight();
545                 
546                 if (vbt->cursor.y < view->screen->first + height) {
547                         vbt->SetCursorFromCoordinates(0,
548                                                       view->screen->first +
549                                                       height);
550                 }
551                 else if (vbt->cursor.y >
552                          view->screen->first + view->work_area->h - height) {
553                         vbt->SetCursorFromCoordinates(0,
554                                                       view->screen->first +
555                                                       view->work_area->h  -
556                                                       height);
557                 }
558         }
559         waitForX();
560 }
561
562
563 // Callback for scrollbar down button
564 void BufferView::DownCB(FL_OBJECT *ob, long buf)
565 {
566         BufferView *view = (BufferView*) buf;
567
568         if (view->_buffer == 0) return;
569         
570         const XEvent*ev2;
571         static long time = 0;
572         ev2 = fl_last_event();
573         if (ev2->type == ButtonPress || ev2->type == ButtonRelease) 
574                 time = 0;
575         int button = fl_get_button_numb(ob);
576         switch (button) {
577         case 2:
578                 view->ScrollUpOnePage(time++); break;
579         case 3:
580                 view->ScrollDownOnePage(time++); break;
581         default:
582                 view->ScrollDown(time++); break;
583         }
584 }
585
586
587 int BufferView::ScrollUp(long time)
588 {
589         if (_buffer == 0) return 0;
590         if (!screen)
591                 return 0;
592    
593         double value= fl_get_slider_value(scrollbar);
594    
595         if (value == 0)
596                 return 0;
597    
598         float add_value =  (_buffer->text->DefaultHeight()
599                             + (float)(time) * (float)(time) * 0.125);
600    
601         if (add_value > work_area->h)
602                 add_value = (float) (work_area->h -
603                                      _buffer->text->DefaultHeight());
604    
605         value -= add_value;
606
607         if (value < 0)
608                 value = 0;
609    
610         fl_set_slider_value(scrollbar, value);
611    
612         ScrollCB(scrollbar,(long)this); 
613         return 0;
614 }
615
616
617 int BufferView::ScrollDown(long time)
618 {
619         if (_buffer == 0) return 0;
620         if (!screen)
621                 return 0;
622    
623         double value= fl_get_slider_value(scrollbar);
624         double min, max;
625         fl_get_slider_bounds(scrollbar, &min, &max);
626
627         if (value == max)
628                 return 0;
629    
630         float add_value =  (_buffer->text->DefaultHeight()
631                             + (float)(time) * (float)(time) * 0.125);
632    
633         if (add_value > work_area->h)
634                 add_value = (float) (work_area->h -
635                                      _buffer->text->DefaultHeight());
636    
637         value += add_value;
638    
639         if (value > max)
640                 value = max;
641    
642         fl_set_slider_value(scrollbar, value);
643    
644         ScrollCB(scrollbar,(long)this); 
645         return 0;
646 }
647
648
649 void BufferView::ScrollUpOnePage(long /*time*/)
650 {
651         if (_buffer == 0) return;
652         if (!screen)
653                 return;
654    
655         long y = screen->first;
656
657         if (!y) return;
658    
659         Row* row = _buffer->text->GetRowNearY(y);
660         y = y - work_area->h + row->height;
661         
662         fl_set_slider_value(scrollbar, y);
663    
664         ScrollCB(scrollbar,(long)this); 
665 }
666
667
668 void BufferView::ScrollDownOnePage(long /*time*/)
669 {
670         if (_buffer == 0) return;
671         if (!screen)
672                 return;
673    
674         double min, max;
675         fl_get_slider_bounds(scrollbar, &min, &max);
676         long y = screen->first;
677    
678         if (y > _buffer->text->height - work_area->h)
679                 return;
680    
681         y += work_area->h;
682         _buffer->text->GetRowNearY(y);
683         
684         fl_set_slider_value(scrollbar, y);
685    
686         ScrollCB(scrollbar,(long)this); 
687 }
688
689
690 int BufferView::work_area_handler(FL_OBJECT * ob, int event,
691                                   FL_Coord, FL_Coord ,
692                                   int /*key*/, void *xev)
693 {
694         static int x_old = -1;
695         static int y_old = -1;
696         static long scrollbar_value_old = -1;
697         
698         XEvent* ev = (XEvent*) xev;
699         BufferView *view = (BufferView*) ob->u_vdata;
700
701         // If we don't have a view yet; return
702         if (!view || quitting) return 0;
703
704         switch (event){   
705         case FL_DRAW:
706                 view->workAreaExpose(); 
707                 break;
708         case FL_PUSH:
709                 view->WorkAreaButtonPress(ob, 0,0,0,ev,0);
710                 break; 
711         case FL_RELEASE:
712                 view->WorkAreaButtonRelease(ob, 0,0,0,ev,0);
713                 break;
714         case FL_MOUSE:
715                 if (ev->xmotion.x != x_old || 
716                     ev->xmotion.y != y_old ||
717                     view->current_scrollbar_value != scrollbar_value_old) {
718                         x_old = ev->xmotion.x;
719                         y_old = ev->xmotion.y;
720                         scrollbar_value_old = view->current_scrollbar_value;
721                         view->WorkAreaMotionNotify(ob, 0,0,0,ev,0);
722                 }
723                 break;
724         // Done by the raw callback:
725         //  case FL_KEYBOARD: WorkAreaKeyPress(ob, 0,0,0,ev,0); break;
726         case FL_FOCUS:
727                 if (!view->_owner->getMiniBuffer()->shows_no_match)
728                         view->_owner->getMiniBuffer()->Init();
729                 view->_owner->getMiniBuffer()->shows_no_match = false;
730                 view->work_area_focus = true;
731                 fl_set_timer(view->timer_cursor, 0.4);
732                 break;
733         case FL_UNFOCUS:
734                 view->_owner->getMiniBuffer()->ExecCommand();
735                 view->work_area_focus = false;
736                 break;
737         case FL_ENTER:
738                 SetXtermCursor(view->_owner->getForm()->window);
739                 // reset the timer
740                 view->lyx_focus = true;
741                 fl_set_timer(view->timer_cursor, 0.4);
742                 break;
743         case FL_LEAVE: 
744                 if (!input_prohibited)
745                         XUndefineCursor(fl_display,
746                                         view->_owner->getForm()->window);
747                 view->lyx_focus = false; // This is not an absolute truth
748                 // but if it is not true, it will be changed within a blink
749                 // of an eye. ... Not good enough... use regulare timeperiod
750                 //fl_set_timer(view->timer_cursor, 0.01); // 0.1 sec blink
751                 fl_set_timer(view->timer_cursor, 0.4); // 0.4 sec blink
752                 break;
753         case FL_DBLCLICK: 
754                 // select a word 
755                 if (view->_buffer && !view->_buffer->the_locking_inset) {
756                         if (view->screen && ev->xbutton.button == 1) {
757                                 view->screen->HideCursor();
758                                 view->screen->ToggleSelection(); 
759                                 view->_buffer->text->SelectWord();
760                                 view->screen->ToggleSelection(false);
761                                 /* This will fit the cursor on the screen
762                                  * if necessary */ 
763                                 view->_buffer->update(0); 
764                         }
765                 }
766                 break;
767         case FL_TRPLCLICK:
768                 // select a line
769                 if (view->_buffer && view->screen && ev->xbutton.button == 1) {
770                         view->screen->HideCursor(); 
771                         view->screen->ToggleSelection(); 
772                         view->_buffer->text->CursorHome();
773                         view->_buffer->text->sel_cursor =
774                                 view->_buffer->text->cursor;
775                         view->_buffer->text->CursorEnd();
776                         view->_buffer->text->SetSelection();
777                         view->screen->ToggleSelection(false); 
778                         /* This will fit the cursor on the screen
779                          * if necessary */ 
780                         view->_buffer->update(0); 
781                 }
782                 break;
783         case FL_OTHER:
784                 view->WorkAreaSelectionNotify(ob,
785                                               view->_owner->getForm()->window,
786                                               0,0,ev,0); 
787                 break;
788         }
789         return 1;
790 }
791
792 int BufferView::WorkAreaMotionNotify(FL_OBJECT *ob, Window,
793                                      int /*w*/, int /*h*/,
794                                      XEvent *ev, void */*d*/)
795 {
796
797         if (_buffer == 0) return 0;
798         if (!screen) return 0;
799
800         // Check for inset locking
801         if (_buffer->the_locking_inset) {
802                 LyXCursor cursor = _buffer->text->cursor;
803                 _buffer->the_locking_inset->
804                         InsetMotionNotify(ev->xbutton.x - ob->x - cursor.x,
805                                           ev->xbutton.y - ob->y -
806                                           (cursor.y),
807                                           ev->xbutton.state);
808                 return 0;
809         }
810    
811         // Only use motion with button 1
812         if (!ev->xmotion.state & Button1MotionMask)
813                 return 0; 
814    
815         /* The selection possible is needed, that only motion events are 
816          * used, where the bottom press event was on the drawing area too */
817         if (selection_possible) {
818                 screen->HideCursor();
819
820                 _buffer->text->
821                         SetCursorFromCoordinates(ev->xbutton.x - ob->x,
822                                                  ev->xbutton.y - ob->y +
823                                                  screen->first);
824       
825                 if (!_buffer->text->selection)
826                     _buffer->update(-3); // Maybe an empty line was deleted
827       
828                 _buffer->text->SetSelection();
829                 screen->ToggleToggle();
830                 if (screen->FitCursor())
831                         updateScrollbar(); 
832                 screen->ShowCursor();
833         }
834         return 0;
835 }
836
837
838 extern int bibitemMaxWidth(const class LyXFont &);
839
840 // Single-click on work area
841 int BufferView::WorkAreaButtonPress(FL_OBJECT *ob, Window,
842                         int /*w*/, int /*h*/, XEvent *ev, void */*d*/)
843 {
844         last_click_x = -1;
845         last_click_y = -1;
846
847         if (_buffer == 0) return 0;
848         if (!screen) return 0;
849
850         int const x = ev->xbutton.x - ob->x;
851         int const y = ev->xbutton.y - ob->y;
852         // If we hit an inset, we have the inset coordinates in these
853         // and inset_hit points to the inset.  If we do not hit an
854         // inset, inset_hit is 0, and inset_x == x, inset_y == y.
855         int inset_x = x;
856         int inset_y = y;
857         Inset * inset_hit = checkInsetHit(inset_x, inset_y);
858
859         // ok ok, this is a hack.
860         int button = ev->xbutton.button;
861         if (button == 4 || button == 5) goto wheel;
862
863         {
864                 
865         if (_buffer->the_locking_inset) {
866                 // We are in inset locking mode
867                 
868                 /* Check whether the inset was hit. If not reset mode,
869                    otherwise give the event to the inset */
870                 if (inset_hit != 0) {
871                         _buffer->the_locking_inset->
872                                 InsetButtonPress(inset_x, inset_y, button);
873                         return 0;
874                 } else {
875                         UnlockInset(_buffer->the_locking_inset);
876                 }
877         }
878         
879         selection_possible = true;
880         screen->HideCursor();
881         
882         // Right button mouse click on a table
883         if (button == 3 &&
884             (_buffer->text->cursor.par->table ||
885              _buffer->text->MouseHitInTable(x, y+screen->first))) {
886                 // Set the cursor to the press-position
887                 _buffer->text->SetCursorFromCoordinates(x, y + screen->first);
888                 bool doit = true;
889                 
890                 // Only show the table popup if the hit is in the table, too
891                 if (!_buffer->text->HitInTable(_buffer->text->cursor.row, x))
892                         doit = false;
893                 
894                 // Hit above or below the table?
895                 if (doit) {
896                         if (!_buffer->text->selection) {
897                                 screen->ToggleSelection();
898                                 _buffer->text->ClearSelection();
899                                 _buffer->text->FullRebreak();
900                                 screen->Update();
901                                 updateScrollbar();
902                         }
903                         // Popup table popup when on a table.
904                         // This is obviously temporary, since we should be
905                         // able to 
906                         // popup various context-sensitive-menus with the
907                         // the right mouse. So this should be done more
908                         // general in the future. Matthias.
909                         selection_possible = false;
910                         _owner->getLyXFunc()->Dispatch(LFUN_LAYOUT_TABLE,
911                                                        "true");
912                         return 0;
913                 }
914         }
915         
916         int screen_first = screen->first;
917         
918         // Middle button press pastes if we have a selection
919         bool paste_internally = false;
920         if (button == 2  // && !_buffer->the_locking_inset
921             && _buffer->text->selection) {
922                 _owner->getLyXFunc()->Dispatch(LFUN_COPY);
923                 paste_internally = true;
924         }
925         
926         // Clear the selection
927         screen->ToggleSelection();
928         _buffer->text->ClearSelection();
929         _buffer->text->FullRebreak();
930         screen->Update();
931         updateScrollbar();
932                 
933         // Single left click in math inset?
934         if (inset_hit != 0 && inset_hit->Editable() == 2) {
935                 // Highly editable inset, like math
936                 selection_possible = false;
937                 _owner->updateLayoutChoice();
938                 _owner->getMiniBuffer()->Set(inset_hit->EditMessage());
939                 inset_hit->Edit(inset_x, inset_y);
940                 return 0;
941         } 
942
943         // Right click on a footnote flag opens float menu
944         if (button == 3) { 
945                 selection_possible = false;
946                 return 0;
947         }
948         
949         _buffer->text->SetCursorFromCoordinates(x, y + screen_first);
950         _buffer->text->FinishUndo();
951         _buffer->text->sel_cursor = _buffer->text->cursor;
952         _buffer->text->cursor.x_fix = _buffer->text->cursor.x;
953         
954         _owner->updateLayoutChoice();
955         if (screen->FitCursor()){
956                 updateScrollbar();
957                 selection_possible = false;
958         }
959
960         // Insert primary selection with middle mouse
961         // if there is a local selection in the current buffer, insert this
962         if (button == 2) { //  && !_buffer->the_locking_inset){
963                 if (paste_internally)
964                         _owner->getLyXFunc()->Dispatch(LFUN_PASTE);
965                 else
966                         _owner->getLyXFunc()->Dispatch(LFUN_PASTESELECTION,
967                                                        "paragraph");
968                 selection_possible = false;
969                 return 0;
970         }
971         }
972         goto out;
973  wheel: {
974         // I am not quite sure if this is the correct place to put this,
975         // but it will not cause any harm.
976         // Patch from Mark Huang (markman@mit.edu) to make LyX recognise
977         // button 4 and 5. This enables LyX use use the scrollwhell on
978         // certain mice for something useful. (Lgb)
979         // Added wheel acceleration detection code. (Rvdk)
980         static Time lastTime = 0;
981         int diff = ev->xbutton.time - lastTime;
982         int scroll = int(1.0 + (4.0/(abs(diff)+1.0))*200.0);
983         switch (button) {
984         case 4:
985                 ScrollUp(scroll);
986                 break;
987         case 5:
988                 ScrollDown(scroll);
989                 break;
990         }
991         lastTime = ev->xbutton.time;
992         return 0;
993         }
994  out:
995         last_click_x = x;
996         last_click_y = y;
997         
998         return 0;
999 }
1000
1001
1002 int BufferView::WorkAreaButtonRelease(FL_OBJECT *ob, Window ,
1003                           int /*w*/, int /*h*/, XEvent *ev, void */*d*/)
1004 {
1005         if (_buffer == 0 || screen == 0) return 0;
1006
1007         int const x = ev->xbutton.x - ob->x;
1008         int const y = ev->xbutton.y - ob->y;
1009
1010         // If we hit an inset, we have the inset coordinates in these
1011         // and inset_hit points to the inset.  If we do not hit an
1012         // inset, inset_hit is 0, and inset_x == x, inset_y == y.
1013         int inset_x = x;
1014         int inset_y = y;
1015         Inset * inset_hit = checkInsetHit(inset_x, inset_y);
1016
1017         if (_buffer->the_locking_inset) {
1018                 // We are in inset locking mode.
1019
1020                 /* LyX does a kind of work-area grabbing for insets.
1021                    Only a ButtonPress Event outside the inset will 
1022                    force a InsetUnlock. */
1023                 _buffer->the_locking_inset->
1024                         InsetButtonRelease(inset_x, inset_y, 
1025                                            ev->xbutton.button);
1026                 return 0;
1027         }
1028   
1029         selection_possible = false;
1030         if (_buffer->text->cursor.par->table) {
1031                 int cell = _buffer->text->
1032                         NumberOfCell(_buffer->text->cursor.par,
1033                                      _buffer->text->cursor.pos);
1034                 if (_buffer->text->cursor.par->table->IsContRow(cell) &&
1035                     _buffer->text->cursor.par->table->
1036                     CellHasContRow(_buffer->text->cursor.par->table->
1037                                    GetCellAbove(cell))<0) {
1038                         _buffer->text->CursorUp();
1039                 }
1040         }
1041         
1042         if (ev->xbutton.button >= 2)
1043                 return 0;
1044
1045         // Make sure that the press was not far from the release
1046         if ((abs(last_click_x - x) >= 5) ||
1047             (abs(last_click_y - y) >= 5)) {
1048                 return 0;
1049         }
1050
1051         // Did we hit an editable inset?
1052         if (inset_hit != 0) {
1053                 // Inset like error, notes and figures
1054                 selection_possible = false;
1055 #ifdef WITH_WARNINGS
1056 #warning fix this proper in 0.13
1057 #endif
1058                 // Following a ref shouldn't issue
1059                 // a push on the undo-stack
1060                 // anylonger, now that we have
1061                 // keybindings for following
1062                 // references and returning from
1063                 // references.  IMHO though, it
1064                 // should be the inset's own business
1065                 // to push or not push on the undo
1066                 // stack. They don't *have* to
1067                 // alter the document...
1068                 // (Joacim)
1069                 // ...or maybe the SetCursorParUndo()
1070                 // below isn't necessary at all anylonger?
1071                 if (inset_hit->LyxCode() == Inset::REF_CODE) {
1072                         _buffer->text->SetCursorParUndo();
1073                 }
1074
1075                 _owner->getMiniBuffer()->Set(inset_hit->EditMessage());
1076                 inset_hit->Edit(inset_x, inset_y);
1077                 return 0;
1078         }
1079
1080         // check whether we want to open a float
1081         if (_buffer->text) {
1082                 bool hit = false;
1083                 char c = ' ';
1084                 if (_buffer->text->cursor.pos <
1085                     _buffer->text->cursor.par->Last()) {
1086                         c = _buffer->text->cursor.par->
1087                                 GetChar(_buffer->text->cursor.pos);
1088                 }
1089                 if (c == LYX_META_FOOTNOTE || c == LYX_META_MARGIN
1090                     || c == LYX_META_FIG || c == LYX_META_TAB
1091                     || c == LYX_META_WIDE_FIG || c == LYX_META_WIDE_TAB
1092                     || c == LYX_META_ALGORITHM){
1093                         hit = true;
1094                 } else if (_buffer->text->cursor.pos - 1 >= 0) {
1095                         c = _buffer->text->cursor.par->
1096                                 GetChar(_buffer->text->cursor.pos - 1);
1097                         if (c == LYX_META_FOOTNOTE || c == LYX_META_MARGIN
1098                             || c == LYX_META_FIG || c == LYX_META_TAB
1099                             || c == LYX_META_WIDE_FIG 
1100                             || c == LYX_META_WIDE_TAB
1101                             || c == LYX_META_ALGORITHM){
1102                                 // We are one step too far to the right
1103                                 _buffer->text->CursorLeft();
1104                                 hit = true;
1105                         }
1106                 }
1107                 if (hit == true) {
1108                         ToggleFloat();
1109                         selection_possible = false;
1110                         return 0;
1111                 }
1112         }
1113
1114         // Do we want to close a float? (click on the float-label)
1115         if (_buffer->text->cursor.row->par->footnoteflag ==
1116             LyXParagraph::OPEN_FOOTNOTE
1117             && _buffer->text->cursor.pos == 0
1118             && _buffer->text->cursor.row->previous &&
1119             _buffer->text->cursor.row->previous->par->
1120             footnoteflag != LyXParagraph::OPEN_FOOTNOTE){
1121                 LyXFont font (LyXFont::ALL_SANE);
1122                 font.setSize(LyXFont::SIZE_SMALL);
1123
1124                 int box_x = 20; // LYX_PAPER_MARGIN;
1125                 box_x += font.textWidth("Mwide-figM", 10);
1126
1127                 int screen_first = screen->first;
1128
1129                 if (x < box_x
1130                     && y + screen_first > _buffer->text->cursor.y -
1131                     _buffer->text->cursor.row->baseline
1132                     && y + screen_first < _buffer->text->cursor.y -
1133                     _buffer->text->cursor.row->baseline
1134                     + font.maxAscent()*1.2 + font.maxDescent()*1.2) {
1135                         ToggleFloat();
1136                         selection_possible = false;
1137                         return 0;
1138                 }
1139         }
1140
1141         // Maybe we want to edit a bibitem ale970302
1142         if (_buffer->text->cursor.par->bibkey && x < 20 + 
1143             bibitemMaxWidth(lyxstyle.TextClass(_buffer->
1144                                         params.textclass)->defaultfont)) {
1145                 _buffer->text->cursor.par->bibkey->Edit(0, 0);
1146         }
1147
1148         return 0;
1149 }
1150
1151
1152 /* 
1153  * Returns an inset if inset was hit. 0 otherwise.
1154  * If hit, the coordinates are changed relative to the inset. 
1155  * Otherwise coordinates are not changed, and false is returned.
1156  */
1157 Inset * BufferView::checkInsetHit(int &x, int &y)
1158 {
1159         if (!getScreen())
1160                 return 0;
1161   
1162         int y_tmp = y + getScreen()->first;
1163   
1164         LyXCursor cursor = _buffer->text->cursor;
1165         if (cursor.pos < cursor.par->Last() 
1166             && cursor.par->GetChar(cursor.pos) == LYX_META_INSET
1167             && cursor.par->GetInset(cursor.pos)
1168             && cursor.par->GetInset(cursor.pos)->Editable()) {
1169
1170                 // Check whether the inset really was hit
1171                 Inset* tmpinset = cursor.par->GetInset(cursor.pos);
1172                 LyXFont font = _buffer->text->GetFont(cursor.par, cursor.pos);
1173                 if (x > cursor.x
1174                     && x < cursor.x + tmpinset->Width(font) 
1175                     && y_tmp > cursor.y - tmpinset->Ascent(font)
1176                     && y_tmp < cursor.y + tmpinset->Descent(font)) {
1177                         x = x - cursor.x;
1178                         // The origin of an inset is on the baseline
1179                         y = y_tmp - (cursor.y); 
1180                         return tmpinset;
1181                 }
1182         } else if (cursor.pos - 1 >= 0 
1183                    && cursor.par->GetChar(cursor.pos - 1) == LYX_META_INSET
1184                    && cursor.par->GetInset(cursor.pos - 1)
1185                    && cursor.par->GetInset(cursor.pos - 1)->Editable()) {
1186                 _buffer->text->CursorLeft();
1187                 Inset * result = checkInsetHit(x, y);
1188                 if (result == 0) {
1189                         _buffer->text->CursorRight();
1190                         return 0;
1191                 } else {
1192                         return result;
1193                 }
1194         }
1195         return 0;
1196 }
1197
1198
1199 int BufferView::workAreaExpose()
1200 {
1201         if (!work_area || !work_area->form->visible) 
1202                 return 1;
1203
1204         // this is a hack to ensure that we only call this through
1205         // BufferView::redraw().
1206         if (!lgb_hack) {
1207                 redraw();
1208         }
1209         
1210         static int work_area_width = work_area->w;
1211         static int work_area_height = work_area->h;
1212
1213         bool widthChange = work_area->w != work_area_width;
1214         bool heightChange = work_area->h != work_area_height;
1215
1216         // update from work area
1217         work_area_width = work_area->w;
1218         work_area_height = work_area->h;
1219         if (_buffer != 0) {
1220                 if (widthChange) {
1221                         // All buffers need a resize
1222                         bufferlist.resize();
1223                 } else if (heightChange) {
1224                         // Rebuild image of current screen
1225                         updateScreen();
1226                         // fitCursor() ensures we don't jump back
1227                         // to the start of the document on vertical
1228                         // resize
1229                         fitCursor();
1230
1231                         // The main window size has changed, repaint most stuff
1232                         redraw();
1233                         // ...including the minibuffer
1234                         _owner->getMiniBuffer()->Init();
1235
1236                 } else if (screen) screen->Redraw();
1237         } else {
1238                 // Grey box when we don't have a buffer
1239                 fl_winset(FL_ObjWin(work_area));
1240                 fl_rectangle(1, work_area->x, work_area->y,
1241                              work_area->w, work_area->h, FL_GRAY63);
1242         }
1243
1244         // always make sure that the scrollbar is sane.
1245         updateScrollbar();
1246         _owner->updateLayoutChoice();
1247         return 1;
1248 }
1249
1250
1251 // Callback for cursor timer
1252 void BufferView::CursorToggleCB(FL_OBJECT *ob, long)
1253 {
1254         BufferView *view = (BufferView*) ob->u_vdata;
1255         
1256         /* quite a nice place for asyncron Inset updating, isn't it? */
1257         // actually no! This is run even if no buffer exist... so (Lgb)
1258         if (view && !view->_buffer) {
1259                 goto set_timer_and_return;
1260         }
1261 #ifdef WITH_WARNINGS
1262 #warning NOTE!
1263 #endif
1264
1265         // On my quest to solve the gs rendre hangups I am now
1266         // disabling the SIGHUP completely, and will do a wait
1267         // now and then instead. If the guess that xforms somehow
1268         // destroys something is true, this is likely (hopefully)
1269         // to solve the problem...at least I hope so. Lgb
1270
1271         // ...Ok this seems to work...at least it does not make things
1272         // worse so far. However I still see gs processes that hangs.
1273         // I would really like to know _why_ they are hanging. Anyway
1274         // the solution without the SIGCHLD handler seems to be easier
1275         // to debug.
1276
1277         // When attaching gdb to a a running gs that hangs it shows
1278         // that it is waiting for input(?) Is it possible for us to
1279         // provide that input somehow? Or figure what it is expecing
1280         // to read?
1281
1282         // One solution is to, after some time, look if there are some
1283         // old gs processes still running and if there are: kill them
1284         // and re render.
1285
1286         // Another solution is to provide the user an option to rerender
1287         // a picture. This would, for the picture in question, check if
1288         // there is a gs running for it, if so kill it, and start a new
1289         // rendering process.
1290
1291         // these comments posted to lyx@via
1292 //#if 0
1293         {
1294         int status = 1;
1295         int pid = waitpid((pid_t)0, &status, WNOHANG);
1296         if (pid == -1) // error find out what is wrong
1297                 ; // ignore it for now.
1298         else if (pid > 0)
1299                 sigchldhandler(pid, &status);
1300         }
1301 //#endif
1302         if (InsetUpdateList) 
1303                 UpdateInsetUpdateList();
1304
1305         if (view && !view->screen){
1306                 goto set_timer_and_return;
1307         }
1308
1309         if (view->lyx_focus && view->work_area_focus) {
1310                 if (!view->_buffer->the_locking_inset){
1311                         view->screen->CursorToggle();
1312                 } else {
1313                         view->_buffer->the_locking_inset->
1314                                 ToggleInsetCursor();
1315                 }
1316                 goto set_timer_and_return;
1317         } else {
1318                 // Make sure that the cursor is visible.
1319                 if (!view->_buffer->the_locking_inset){
1320                         view->screen->ShowCursor();
1321                 } else {
1322                         if (!view->_buffer->the_locking_inset->isCursorVisible())
1323                                 view->_buffer->the_locking_inset->
1324                                         ToggleInsetCursor();
1325                 }
1326
1327                 // This is only run when work_area_focus or lyx_focus is false.
1328                 Window tmpwin;
1329                 int tmp;
1330                 XGetInputFocus(fl_display, &tmpwin, &tmp);
1331                 lyxerr.debug(string("tmpwin: ") + tostr(tmpwin));
1332                 lyxerr.debug(string("window: ")
1333                              + tostr(view->_owner->getForm()->window));
1334                 lyxerr.debug(string("work_area_focus: ")
1335                              + tostr(view->work_area_focus));
1336                 lyxerr.debug(string("lyx_focus      : ")
1337                              + tostr(view->lyx_focus));
1338                 if (tmpwin != view->_owner->getForm()->window) {
1339                         view->lyx_focus = false;
1340                         goto skip_timer;
1341                 } else {
1342                         view->lyx_focus = true;
1343                         if (!view->work_area_focus)
1344                                 goto skip_timer;
1345                         else
1346                                 goto set_timer_and_return;
1347                 }
1348         }
1349
1350   set_timer_and_return:
1351         fl_set_timer(ob, 0.4);
1352   skip_timer:
1353         return;
1354 }
1355
1356
1357 int BufferView::WorkAreaSelectionNotify(FL_OBJECT *, Window win,
1358                             int /*w*/, int /*h*/, XEvent *event, void */*d*/)
1359 {
1360         if (_buffer == 0) return 0;
1361         if (event->type != SelectionNotify)
1362                 return 0;
1363
1364         Atom tmpatom;
1365         unsigned long ul1;
1366         unsigned long ul2;
1367         unsigned char* uc = 0;
1368         int tmpint;
1369         screen->HideCursor();
1370         BeforeChange();
1371         if (event->xselection.type == XA_STRING
1372             && event->xselection.property) {
1373     
1374                 if (XGetWindowProperty(
1375                         fl_display            /* display */,
1376                         win /* w */,
1377                         event->xselection.property        /* property */,
1378                         0                /* long_offset */,
1379                         0                /* long_length */,
1380                         false                /* delete */,
1381                         XA_STRING                /* req_type */,
1382                         &tmpatom               /* actual_type_return */,
1383                         &tmpint                /* actual_format_return */,
1384                         &ul1      /* nitems_return */,
1385                         &ul2      /* bytes_after_return */,
1386                         &uc     /* prop_return */
1387                         ) != Success) {
1388                         return 0;
1389                 }
1390                 XFlush(fl_display);
1391
1392                 if (uc){
1393                         free(uc);
1394                         uc = 0;
1395                 }
1396
1397                 if (XGetWindowProperty(
1398                         fl_display           /* display */,
1399                         win              /* w */,
1400                         event->xselection.property           /* property */,
1401                         0                /* long_offset */,
1402                         ul2/4+1                /* long_length */,
1403                         True                /* delete */,
1404                         XA_STRING                /* req_type */,
1405                         &tmpatom               /* actual_type_return */,
1406                         &tmpint                /* actual_format_return */,
1407                         &ul1      /* nitems_return */,
1408                         &ul2      /* bytes_after_return */,
1409                         &uc     /* prop_return */
1410                         ) != Success) {
1411                         return 0;
1412                 }
1413                 XFlush(fl_display);
1414         
1415                 if (uc){
1416                         if (!ascii_type)
1417                                 _buffer->text->
1418                                         InsertStringA((char*)uc);
1419                         else
1420                                 _buffer->text->
1421                                         InsertStringB((char*)uc);
1422                         free(uc);
1423                         uc = 0;
1424                 }
1425     
1426                 _buffer->update(1);
1427         }
1428         return 0;
1429 }
1430
1431
1432 void BufferView::cursorPrevious()
1433 {
1434         if (!currentBuffer()->text->cursor.row->previous) return;
1435         
1436         long y = getScreen()->first;
1437         Row* cursorrow = currentBuffer()->text->cursor.row;
1438         currentBuffer()->text->
1439           SetCursorFromCoordinates(currentBuffer()->text->
1440                                    cursor.x_fix,
1441                                    y);
1442         currentBuffer()->text->FinishUndo();
1443         /* this is to allow jumping over large insets */
1444         if ((cursorrow == currentBuffer()->text->cursor.row))
1445           currentBuffer()->text->CursorUp();
1446         
1447         if (currentBuffer()->text->cursor.row->height < work_area->h)
1448           getScreen()->Draw(currentBuffer()->text->cursor.y
1449                             - currentBuffer()->text->cursor.row->baseline
1450                             + currentBuffer()->text->cursor.row->height
1451                             - work_area->h +1 );
1452 }
1453
1454
1455 void BufferView::cursorNext()
1456 {
1457         if (!currentBuffer()->text->cursor.row->next) return;
1458         
1459         long y = getScreen()->first;
1460         currentBuffer()->text->GetRowNearY(y);
1461         Row* cursorrow = currentBuffer()->text->cursor.row;
1462         currentBuffer()->text->
1463                 SetCursorFromCoordinates(currentBuffer()->text->
1464                                          cursor.x_fix, 
1465                                          y + work_area->h);
1466         currentBuffer()->text->FinishUndo();
1467         /* this is to allow jumping over large insets */
1468         if ((cursorrow == currentBuffer()->text->cursor.row))
1469           currentBuffer()->text->CursorDown();
1470         
1471         if (currentBuffer()->text->cursor.row->height < work_area->h)
1472           getScreen()->Draw(currentBuffer()->text->cursor.y
1473                             - currentBuffer()->text->cursor.row->baseline);
1474 }
1475
1476
1477 bool BufferView::available() const
1478 {
1479         if (_buffer && _buffer->text) return true;
1480         return false;
1481 }
1482
1483
1484 void BufferView::savePosition()
1485 {
1486         backstack->push(currentBuffer()->getFileName(),
1487                         currentBuffer()->text->cursor.x,
1488                         currentBuffer()->text->cursor.y);
1489 }
1490
1491
1492 void BufferView::restorePosition()
1493 {
1494         int  x, y;
1495         string fname = backstack->pop(&x, &y);
1496         
1497         BeforeChange();
1498         Buffer *b = (bufferlist.exists(fname)) ? bufferlist.getBuffer(fname):
1499                 bufferlist.loadLyXFile(fname); // don't ask, just load it
1500         setBuffer(b);
1501         currentBuffer()->text->SetCursorFromCoordinates(x, y);
1502         currentBuffer()->update(0);
1503