]> git.lyx.org Git - features.git/blob - src/BufferView.C
212a1831bb014cc3524daf5c438b3570a7986f9c
[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 = (void*) 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 = (void*) 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 0
897                         long y_tmp = y + screen->first;
898
899                         Row*  row =  _buffer->text->GetRowNearY(y_tmp);
900
901                         // Isn't this empty code anyway? (Lgb)
902                         if (row->par != _buffer->text->cursor.par)
903                                 doit = true;
904                 }
905                 
906                 if (doit) {
907 #endif
908                         if (!_buffer->text->selection) {
909                                 screen->ToggleSelection();
910                                 _buffer->text->ClearSelection();
911                                 _buffer->text->FullRebreak();
912                                 screen->Update();
913                                 updateScrollbar();
914                         }
915                         // Popup table popup when on a table.
916                         // This is obviously temporary, since we should be
917                         // able to 
918                         // popup various context-sensitive-menus with the
919                         // the right mouse. So this should be done more
920                         // general in the future. Matthias.
921                         selection_possible = false;
922                         _owner->getLyXFunc()->Dispatch(LFUN_LAYOUT_TABLE,
923                                                        "true");
924                         return 0;
925                 }
926         }
927         
928         int screen_first = screen->first;
929         
930         // Middle button press pastes if we have a selection
931         bool paste_internally = false;
932         if (button == 2  // && !_buffer->the_locking_inset
933             && _buffer->text->selection) {
934                 _owner->getLyXFunc()->Dispatch(LFUN_COPY);
935                 paste_internally = true;
936         }
937         
938         // Clear the selection
939         screen->ToggleSelection();
940         _buffer->text->ClearSelection();
941         _buffer->text->FullRebreak();
942         screen->Update();
943         updateScrollbar();
944                 
945         // Single left click in math inset?
946         if (inset_hit != 0 && inset_hit->Editable() == 2) {
947                 // Highly editable inset, like math
948                 selection_possible = false;
949                 _owner->updateLayoutChoice();
950                 _owner->getMiniBuffer()->Set(inset_hit->EditMessage());
951                 inset_hit->Edit(inset_x, inset_y);
952                 return 0;
953         } 
954
955         // Right click on a footnote flag opens float menu
956         if (button == 3) { 
957 #if 0
958                 // Isn't this empty code anyway? (Lgb)
959                 if (_buffer->text->cursor.par->footnoteflag == 
960                     LyXParagraph::OPEN_FOOTNOTE) {
961                         selection_possible = false;
962                         lyxerr.debug("LyX: Sorry not implemented yet.");
963                         return 0;
964                 }
965         }
966         
967         if (button == 3) {
968 #endif
969                 selection_possible = false;
970                 return 0;
971         }
972         
973         _buffer->text->SetCursorFromCoordinates(x, y + screen_first);
974         _buffer->text->FinishUndo();
975         _buffer->text->sel_cursor = _buffer->text->cursor;
976         _buffer->text->cursor.x_fix = _buffer->text->cursor.x;
977         
978         _owner->updateLayoutChoice();
979         if (screen->FitCursor()){
980                 updateScrollbar();
981                 selection_possible = false;
982         }
983
984         // Insert primary selection with middle mouse
985         // if there is a local selection in the current buffer, insert this
986         if (button == 2) { //  && !_buffer->the_locking_inset){
987                 if (paste_internally)
988                         _owner->getLyXFunc()->Dispatch(LFUN_PASTE);
989                 else
990                         _owner->getLyXFunc()->Dispatch(LFUN_PASTESELECTION,
991                                                        "paragraph");
992                 selection_possible = false;
993                 return 0;
994         }
995         }
996         goto out;
997  wheel: {
998         // I am not quite sure if this is the correct place to put this,
999         // but it will not cause any harm.
1000         // Patch from Mark Huang (markman@mit.edu) to make LyX recognise
1001         // button 4 and 5. This enables LyX use use the scrollwhell on
1002         // certain mice for something useful. (Lgb)
1003         // Added wheel acceleration detection code. (Rvdk)
1004         static Time lastTime = 0;
1005         int diff = ev->xbutton.time - lastTime;
1006         int scroll = int(1.0 + (4.0/(abs(diff)+1.0))*200.0);
1007         switch (button) {
1008         case 4:
1009                 ScrollUp(scroll);
1010                 break;
1011         case 5:
1012                 ScrollDown(scroll);
1013                 break;
1014         }
1015         lastTime = ev->xbutton.time;
1016         return 0;
1017         }
1018  out:
1019         last_click_x = x;
1020         last_click_y = y;
1021         
1022         return 0;
1023 }
1024
1025
1026 int BufferView::WorkAreaButtonRelease(FL_OBJECT *ob, Window ,
1027                           int /*w*/, int /*h*/, XEvent *ev, void */*d*/)
1028 {
1029         if (_buffer == 0 || screen == 0) return 0;
1030
1031         int const x = ev->xbutton.x - ob->x;
1032         int const y = ev->xbutton.y - ob->y;
1033
1034         // If we hit an inset, we have the inset coordinates in these
1035         // and inset_hit points to the inset.  If we do not hit an
1036         // inset, inset_hit is 0, and inset_x == x, inset_y == y.
1037         int inset_x = x;
1038         int inset_y = y;
1039         Inset * inset_hit = checkInsetHit(inset_x, inset_y);
1040
1041         if (_buffer->the_locking_inset) {
1042                 // We are in inset locking mode.
1043
1044                 /* LyX does a kind of work-area grabbing for insets.
1045                    Only a ButtonPress Event outside the inset will 
1046                    force a InsetUnlock. */
1047                 _buffer->the_locking_inset->
1048                         InsetButtonRelease(inset_x, inset_y, 
1049                                            ev->xbutton.button);
1050                 return 0;
1051         }
1052   
1053         selection_possible = false;
1054         if (_buffer->text->cursor.par->table) {
1055                 int cell = _buffer->text->
1056                         NumberOfCell(_buffer->text->cursor.par,
1057                                      _buffer->text->cursor.pos);
1058                 if (_buffer->text->cursor.par->table->IsContRow(cell) &&
1059                     _buffer->text->cursor.par->table->
1060                     CellHasContRow(_buffer->text->cursor.par->table->
1061                                    GetCellAbove(cell))<0) {
1062                         _buffer->text->CursorUp();
1063                 }
1064         }
1065         
1066         if (ev->xbutton.button >= 2)
1067                 return 0;
1068
1069         // Make sure that the press was not far from the release
1070         if ((abs(last_click_x - x) >= 5) ||
1071             (abs(last_click_y - y) >= 5)) {
1072                 return 0;
1073         }
1074
1075         // Did we hit an editable inset?
1076         if (inset_hit != 0) {
1077                 // Inset like error, notes and figures
1078                 selection_possible = false;
1079 #ifdef WITH_WARNINGS
1080 #warning fix this proper in 0.13
1081 #endif
1082                 // Following a ref shouldn't issue
1083                 // a push on the undo-stack
1084                 // anylonger, now that we have
1085                 // keybindings for following
1086                 // references and returning from
1087                 // references.  IMHO though, it
1088                 // should be the inset's own business
1089                 // to push or not push on the undo
1090                 // stack. They don't *have* to
1091                 // alter the document...
1092                 // (Joacim)
1093                 // ...or maybe the SetCursorParUndo()
1094                 // below isn't necessary at all anylonger?
1095                 if (inset_hit->LyxCode() == Inset::REF_CODE) {
1096                         _buffer->text->SetCursorParUndo();
1097                 }
1098
1099                 _owner->getMiniBuffer()->Set(inset_hit->EditMessage());
1100                 inset_hit->Edit(inset_x, inset_y);
1101                 return 0;
1102         }
1103
1104         // check whether we want to open a float
1105         if (_buffer->text) {
1106                 bool hit = false;
1107                 char c = ' ';
1108                 if (_buffer->text->cursor.pos <
1109                     _buffer->text->cursor.par->Last()) {
1110                         c = _buffer->text->cursor.par->
1111                                 GetChar(_buffer->text->cursor.pos);
1112                 }
1113                 if (c == LYX_META_FOOTNOTE || c == LYX_META_MARGIN
1114                     || c == LYX_META_FIG || c == LYX_META_TAB
1115                     || c == LYX_META_WIDE_FIG || c == LYX_META_WIDE_TAB
1116                     || c == LYX_META_ALGORITHM){
1117                         hit = true;
1118                 } else if (_buffer->text->cursor.pos - 1 >= 0) {
1119                         c = _buffer->text->cursor.par->
1120                                 GetChar(_buffer->text->cursor.pos - 1);
1121                         if (c == LYX_META_FOOTNOTE || c == LYX_META_MARGIN
1122                             || c == LYX_META_FIG || c == LYX_META_TAB
1123                             || c == LYX_META_WIDE_FIG 
1124                             || c == LYX_META_WIDE_TAB
1125                             || c == LYX_META_ALGORITHM){
1126                                 // We are one step too far to the right
1127                                 _buffer->text->CursorLeft();
1128                                 hit = true;
1129                         }
1130                 }
1131                 if (hit == true) {
1132                         ToggleFloat();
1133                         selection_possible = false;
1134                         return 0;
1135                 }
1136         }
1137
1138         // Do we want to close a float? (click on the float-label)
1139         if (_buffer->text->cursor.row->par->footnoteflag ==
1140             LyXParagraph::OPEN_FOOTNOTE
1141             && _buffer->text->cursor.pos == 0
1142             && _buffer->text->cursor.row->previous &&
1143             _buffer->text->cursor.row->previous->par->
1144             footnoteflag != LyXParagraph::OPEN_FOOTNOTE){
1145                 LyXFont font (LyXFont::ALL_SANE);
1146                 font.setSize(LyXFont::SIZE_SMALL);
1147
1148                 int box_x = 20; // LYX_PAPER_MARGIN;
1149                 box_x += font.textWidth("Mwide-figM", 10);
1150
1151                 int screen_first = screen->first;
1152
1153                 if (x < box_x
1154                     && y + screen_first > _buffer->text->cursor.y -
1155                     _buffer->text->cursor.row->baseline
1156                     && y + screen_first < _buffer->text->cursor.y -
1157                     _buffer->text->cursor.row->baseline
1158                     + font.maxAscent()*1.2 + font.maxDescent()*1.2) {
1159                         ToggleFloat();
1160                         selection_possible = false;
1161                         return 0;
1162                 }
1163         }
1164
1165         // Maybe we want to edit a bibitem ale970302
1166         if (_buffer->text->cursor.par->bibkey && x < 20 + 
1167             bibitemMaxWidth(lyxstyle.TextClass(_buffer->
1168                                         params.textclass)->defaultfont)) {
1169                 _buffer->text->cursor.par->bibkey->Edit(0, 0);
1170         }
1171
1172         return 0;
1173 }
1174
1175
1176 /* 
1177  * Returns an inset if inset was hit. 0 otherwise.
1178  * If hit, the coordinates are changed relative to the inset. 
1179  * Otherwise coordinates are not changed, and false is returned.
1180  */
1181 Inset * BufferView::checkInsetHit(int &x, int &y)
1182 {
1183         if (!getScreen())
1184                 return 0;
1185   
1186         int y_tmp = y + getScreen()->first;
1187   
1188         LyXCursor cursor = _buffer->text->cursor;
1189         if (cursor.pos < cursor.par->Last() 
1190             && cursor.par->GetChar(cursor.pos) == LYX_META_INSET
1191             && cursor.par->GetInset(cursor.pos)
1192             && cursor.par->GetInset(cursor.pos)->Editable()) {
1193
1194                 // Check whether the inset really was hit
1195                 Inset* tmpinset = cursor.par->GetInset(cursor.pos);
1196                 LyXFont font = _buffer->text->GetFont(cursor.par, cursor.pos);
1197                 if (x > cursor.x
1198                     && x < cursor.x + tmpinset->Width(font) 
1199                     && y_tmp > cursor.y - tmpinset->Ascent(font)
1200                     && y_tmp < cursor.y + tmpinset->Descent(font)) {
1201                         x = x - cursor.x;
1202                         // The origin of an inset is on the baseline
1203                         y = y_tmp - (cursor.y); 
1204                         return tmpinset;
1205                 }
1206         } else if (cursor.pos - 1 >= 0 
1207                    && cursor.par->GetChar(cursor.pos - 1) == LYX_META_INSET
1208                    && cursor.par->GetInset(cursor.pos - 1)
1209                    && cursor.par->GetInset(cursor.pos - 1)->Editable()) {
1210                 _buffer->text->CursorLeft();
1211                 Inset * result = checkInsetHit(x, y);
1212                 if (result == 0) {
1213                         _buffer->text->CursorRight();
1214                         return 0;
1215                 } else {
1216                         return result;
1217                 }
1218         }
1219         return 0;
1220 }
1221
1222
1223 int BufferView::workAreaExpose()
1224 {
1225         if (!work_area || !work_area->form->visible) 
1226                 return 1;
1227
1228         // this is a hack to ensure that we only call this through
1229         // BufferView::redraw().
1230         if (!lgb_hack) {
1231                 redraw();
1232         }
1233         
1234         static int work_area_width = work_area->w;
1235         static int work_area_height = work_area->h;
1236
1237         bool widthChange = work_area->w != work_area_width;
1238         bool heightChange = work_area->h != work_area_height;
1239
1240         // update from work area
1241         work_area_width = work_area->w;
1242         work_area_height = work_area->h;
1243         if (_buffer != 0) {
1244                 if (widthChange) {
1245                         // All buffers need a resize
1246                         bufferlist.resize();
1247                 } else if (heightChange) {
1248                         // Rebuild image of current screen
1249                         updateScreen();
1250                         // fitCursor() ensures we don't jump back
1251                         // to the start of the document on vertical
1252                         // resize
1253                         fitCursor();
1254
1255                         // The main window size has changed, repaint most stuff
1256                         redraw();
1257                         // ...including the minibuffer
1258                         _owner->getMiniBuffer()->Init();
1259
1260                 } else if (screen) screen->Redraw();
1261         } else {
1262                 // Grey box when we don't have a buffer
1263                 fl_winset(FL_ObjWin(work_area));
1264                 fl_rectangle(1, work_area->x, work_area->y,
1265                              work_area->w, work_area->h, FL_GRAY63);
1266         }
1267
1268         // always make sure that the scrollbar is sane.
1269         updateScrollbar();
1270         _owner->updateLayoutChoice();
1271         return 1;
1272 }
1273
1274
1275 // Callback for cursor timer
1276 void BufferView::CursorToggleCB(FL_OBJECT *ob, long)
1277 {
1278         BufferView *view = (BufferView*) ob->u_vdata;
1279         
1280         /* quite a nice place for asyncron Inset updating, isn't it? */
1281         // actually no! This is run even if no buffer exist... so (Lgb)
1282         if (view && !view->_buffer) {
1283                 goto set_timer_and_return;
1284         }
1285 #ifdef WITH_WARNINGS
1286 #warning NOTE!
1287 #endif
1288
1289         // On my quest to solve the gs rendre hangups I am now
1290         // disabling the SIGHUP completely, and will do a wait
1291         // now and then instead. If the guess that xforms somehow
1292         // destroys something is true, this is likely (hopefully)
1293         // to solve the problem...at least I hope so. Lgb
1294
1295         // ...Ok this seems to work...at least it does not make things
1296         // worse so far. However I still see gs processes that hangs.
1297         // I would really like to know _why_ they are hanging. Anyway
1298         // the solution without the SIGCHLD handler seems to be easier
1299         // to debug.
1300
1301         // When attaching gdb to a a running gs that hangs it shows
1302         // that it is waiting for input(?) Is it possible for us to
1303         // provide that input somehow? Or figure what it is expecing
1304         // to read?
1305
1306         // One solution is to, after some time, look if there are some
1307         // old gs processes still running and if there are: kill them
1308         // and re render.
1309
1310         // Another solution is to provide the user an option to rerender
1311         // a picture. This would, for the picture in question, check if
1312         // there is a gs running for it, if so kill it, and start a new
1313         // rendering process.
1314
1315         // these comments posted to lyx@via
1316 //#if 0
1317         {
1318         int status = 1;
1319         int pid = waitpid((pid_t)0, &status, WNOHANG);
1320         if (pid == -1) // error find out what is wrong
1321                 ; // ignore it for now.
1322         else if (pid > 0)
1323                 sigchldhandler(pid, &status);
1324         }
1325 //#endif
1326         if (InsetUpdateList) 
1327                 UpdateInsetUpdateList();
1328
1329         if (view && !view->screen){
1330                 goto set_timer_and_return;
1331         }
1332
1333         if (view->lyx_focus && view->work_area_focus) {
1334                 if (!view->_buffer->the_locking_inset){
1335                         view->screen->CursorToggle();
1336                 } else {
1337                         view->_buffer->the_locking_inset->
1338                                 ToggleInsetCursor();
1339                 }
1340                 goto set_timer_and_return;
1341         } else {
1342                 // Make sure that the cursor is visible.
1343                 if (!view->_buffer->the_locking_inset){
1344                         view->screen->ShowCursor();
1345                 } else {
1346                         if (!view->_buffer->the_locking_inset->isCursorVisible())
1347                                 view->_buffer->the_locking_inset->
1348                                         ToggleInsetCursor();
1349                 }
1350
1351                 // This is only run when work_area_focus or lyx_focus is false.
1352                 Window tmpwin;
1353                 int tmp;
1354                 XGetInputFocus(fl_display, &tmpwin, &tmp);
1355                 lyxerr.debug(string("tmpwin: ") + tostr(tmpwin));
1356                 lyxerr.debug(string("window: ")
1357                              + tostr(view->_owner->getForm()->window));
1358                 lyxerr.debug(string("work_area_focus: ")
1359                              + tostr(view->work_area_focus));
1360                 lyxerr.debug(string("lyx_focus      : ")
1361                              + tostr(view->lyx_focus));
1362                 if (tmpwin != view->_owner->getForm()->window) {
1363                         view->lyx_focus = false;
1364                         goto skip_timer;
1365                 } else {
1366                         view->lyx_focus = true;
1367                         if (!view->work_area_focus)
1368                                 goto skip_timer;
1369                         else
1370                                 goto set_timer_and_return;
1371                 }
1372         }
1373
1374   set_timer_and_return:
1375         fl_set_timer(ob, 0.4);
1376   skip_timer:
1377         return;
1378 }
1379
1380
1381 int BufferView::WorkAreaSelectionNotify(FL_OBJECT *, Window win,
1382                             int /*w*/, int /*h*/, XEvent *event, void */*d*/)
1383 {
1384         if (_buffer == 0) return 0;
1385         if (event->type != SelectionNotify)
1386                 return 0;
1387
1388         Atom tmpatom;
1389         unsigned long ul1;
1390         unsigned long ul2;
1391         unsigned char* uc = 0;
1392         int tmpint;
1393         screen->HideCursor();
1394         BeforeChange();
1395         if (event->xselection.type == XA_STRING
1396             && event->xselection.property) {
1397     
1398                 if (XGetWindowProperty(
1399                         fl_display            /* display */,
1400                         win /* w */,
1401                         event->xselection.property        /* property */,
1402                         0                /* long_offset */,
1403                         0                /* long_length */,
1404                         false                /* delete */,
1405                         XA_STRING                /* req_type */,
1406                         &tmpatom               /* actual_type_return */,
1407                         &tmpint                /* actual_format_return */,
1408                         &ul1      /* nitems_return */,
1409                         &ul2      /* bytes_after_return */,
1410                         &uc     /* prop_return */
1411                         ) != Success) {
1412                         return 0;
1413                 }
1414                 XFlush(fl_display);
1415
1416                 if (uc){
1417                         free(uc);
1418                         uc = 0;
1419                 }
1420
1421                 if (XGetWindowProperty(
1422                         fl_display           /* display */,
1423                         win              /* w */,
1424                         event->xselection.property           /* property */,
1425                         0                /* long_offset */,
1426                         ul2/4+1                /* long_length */,
1427                         True                /* delete */,
1428                         XA_STRING                /* req_type */,
1429                         &tmpatom               /* actual_type_return */,
1430                         &tmpint                /* actual_format_return */,
1431                         &ul1      /* nitems_return */,
1432                         &ul2      /* bytes_after_return */,
1433                         &uc     /* prop_return */
1434                         ) != Success) {
1435                         return 0;
1436                 }
1437                 XFlush(fl_display);
1438         
1439                 if (uc){
1440                         if (!ascii_type)
1441                                 _buffer->text->
1442                                         InsertStringA((char*)uc);
1443                         else
1444                                 _buffer->text->
1445                                         InsertStringB((char*)uc);
1446                         free(uc);
1447                         uc = 0;
1448                 }
1449     
1450                 _buffer->update(1);
1451         }
1452         return 0;
1453 }
1454
1455
1456 void BufferView::cursorPrevious()
1457 {
1458         if (!currentBuffer()->text->cursor.row->previous) return;
1459         
1460         long y = getScreen()->first;
1461         Row* cursorrow = currentBuffer()->text->cursor.row;
1462         currentBuffer()->text->
1463           SetCursorFromCoordinates(currentBuffer()->text->
1464                                    cursor.x_fix,
1465                                    y);
1466         currentBuffer()->text->FinishUndo();
1467         /* this is to allow jumping over large insets */
1468         if ((cursorrow == currentBuffer()->text->cursor.row))
1469           currentBuffer()->text->CursorUp();
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                             + currentBuffer()->text->cursor.row->height
1475                             - work_area->h +1 );
1476 }
1477
1478
1479 void BufferView::cursorNext()
1480 {
1481         if (!currentBuffer()->text->cursor.row->next) return;
1482         
1483         long y = getScreen()->first;
1484         currentBuffer()->text->GetRowNearY(y);
1485         Row* cursorrow = currentBuffer()->text->cursor.row;
1486         currentBuffer()->text->
1487                 SetCursorFromCoordinates(currentBuffer()->text->
1488                                          cursor.x_fix, 
1489                                          y + work_area->h);
1490         currentBuffer()->text->FinishUndo();
1491         /* this is to allow jumping over large insets */
1492         if ((cursorrow == currentBuffer()->text->cursor.row))
1493           currentBuffer()->text->CursorDown();
1494         
1495         if (currentBuffer()->text->cursor.row->height < work_area->h)
1496           getScreen()->Draw(currentBuffer()->text->cursor.y
1497                             - currentBuffer()->text->cursor.row->baseline);
1498 }
1499
1500
1501 bool BufferView::available() const
1502 {
1503         if (_buffer && _buffer->text) return true;
1504         return false;
1505 }
1506
1507
1508 void BufferView::savePosition()
1509 {
1510         backstack->push(currentBuffer()->getFileName(),
1511                         currentBuffer()->text->cursor.x,
1512                         currentBuffer()->text->cursor.y);
1513 }
1514
1515
1516 void BufferView::restorePosition()
1517 {
1518         int  x, y;
1519         string fname = backstack->pop(&x, &y);
1520         
1521         BeforeChange();
1522         Buffer *b = (bufferlist.exists(fname)) ? bufferlist.getBuffer(fname):
1523                 bufferlist.loadLyXFile(fname); // don't ask, just load it
1524         setBuffer(b);
1525         currentBuffer()->text->SetCursorFromCoordinates(x, y);
1526         currentBuffer()->update(0);
1527
1528