]> git.lyx.org Git - lyx.git/blob - src/BufferView.C
a9abd9ff9cc6112a7fc83383395c17a3d6888347
[lyx.git] / src / BufferView.C
1 // -*- C++ -*-
2 /* This file is part of
3  * ======================================================
4  * 
5  *           LyX, The Document Processor
6  *        
7  *           Copyright 1995 Matthias Ettrich
8  *           Copyright 1995-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 "debug.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" << endl;
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() << "  Buffer addr: " << _buffer << endl;
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!" << endl;
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()" << endl;
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" << endl;
259         if (_buffer && _buffer->text) {
260                 resize();
261                 _owner->updateLayoutChoice();
262         }
263 }
264
265
266 int BufferView::resizeCurrentBuffer()
267 {
268         lyxerr.debug() << "resizeCurrentBuffer" << endl;
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 // Just a bunch of C wrappers around static members of BufferView
368 extern "C" void C_BufferView_UpCB(FL_OBJECT *ob, long buf) {
369         BufferView::UpCB(ob,buf);
370 }
371
372 extern "C" void C_BufferView_DownCB(FL_OBJECT *ob, long buf) {
373         BufferView::DownCB(ob,buf);
374 }
375
376 extern "C" void C_BufferView_ScrollCB(FL_OBJECT *ob, long buf) {
377         BufferView::ScrollCB(ob,buf);
378 }
379
380 extern "C" void C_BufferView_CursorToggleCB(FL_OBJECT *ob, long buf) {
381         BufferView::CursorToggleCB(ob,buf);
382 }
383
384 extern "C" int C_BufferView_work_area_handler(FL_OBJECT *ob, int event,
385                                               FL_Coord, FL_Coord, 
386                                               int key, void *xev) {
387         return BufferView::work_area_handler(ob, event, 0, 0, key, xev);
388 }
389
390
391 void BufferView::create_view(int xpos, int ypos, int width, int height)
392 {
393         FL_OBJECT *obj;
394         const int bw = abs(fl_get_border_width());
395
396         // a hack for the figinsets (Matthias)
397         // This one first, then it will probably be invisible. (Lgb)
398         ::figinset_canvas = figinset_canvas = obj =
399                 fl_add_canvas(FL_NORMAL_CANVAS,
400                               xpos + 1,
401                               ypos + 1,1,1,"");
402         fl_set_object_boxtype(obj,FL_NO_BOX);
403         fl_set_object_resize(obj, FL_RESIZE_ALL);
404         fl_set_object_gravity(obj, NorthWestGravity, NorthWestGravity);
405
406         // a box
407         obj = fl_add_box(FL_BORDER_BOX, xpos, ypos,
408                          width - 15,
409                          height,"");
410         fl_set_object_resize(obj, FL_RESIZE_ALL);
411         fl_set_object_gravity(obj, NorthWestGravity, SouthEastGravity);
412
413         // the free object
414         work_area = obj = fl_add_free(FL_INPUT_FREE,
415                                       xpos +bw, ypos+bw,
416                                       width-15-2*bw /* scrollbarwidth */,
417                                       height-2*bw,"",
418                                       C_BufferView_work_area_handler);
419         obj->wantkey = FL_KEY_TAB;
420         obj->u_vdata = this; /* This is how we pass the BufferView
421                                        to the work_area_handler. */
422         fl_set_object_boxtype(obj,FL_DOWN_BOX);
423         fl_set_object_resize(obj, FL_RESIZE_ALL);
424         fl_set_object_gravity(obj, NorthWestGravity, SouthEastGravity);
425
426         //
427         // THE SCROLLBAR
428         //
429
430         // up - scrollbar button
431 #if FL_REVISION > 85
432         fl_set_border_width(-1);
433 #else
434         fl_set_border_width(-2); // to get visible feedback
435 #endif
436         button_up = obj = fl_add_pixmapbutton(FL_TOUCH_BUTTON,
437                                               width-15+4*bw,
438                                               ypos,
439                                               15,15,"");
440         fl_set_object_boxtype(obj,FL_UP_BOX);
441         fl_set_object_color(obj,FL_MCOL,FL_BLUE);
442         fl_set_object_resize(obj, FL_RESIZE_ALL);
443         fl_set_object_gravity(obj,NorthEastGravity, NorthEastGravity);
444         fl_set_object_callback(obj,C_BufferView_UpCB,(long)this);
445         fl_set_pixmapbutton_data(obj, up_xpm);
446
447 #if FL_REVISION >85
448         // Remove the blue feedback rectangle
449         fl_set_pixmapbutton_focus_outline(obj,0);
450 #endif  
451
452         // the scrollbar slider
453         fl_set_border_width(-bw);
454         scrollbar = obj = fl_add_slider(FL_VERT_SLIDER,
455                                         width-15+4*bw,
456                                         ypos + 15,
457                                         15,height-30,"");
458         fl_set_object_color(obj,FL_COL1,FL_MCOL);
459         fl_set_object_boxtype(obj, FL_UP_BOX);
460         fl_set_object_resize(obj, FL_RESIZE_ALL);
461         fl_set_object_gravity(obj, NorthEastGravity, SouthEastGravity);
462         fl_set_object_callback(obj,C_BufferView_ScrollCB,(long)this);
463         
464         // down - scrollbar button
465 #if FL_REVISION > 85
466         fl_set_border_width(-1);
467 #else
468         fl_set_border_width(-2); // to get visible feedback
469 #endif
470         button_down = obj = fl_add_pixmapbutton(FL_TOUCH_BUTTON,
471                                                       width-15+4*bw,
472                                                       ypos + height-15,
473                                                       15,15,"");
474         fl_set_object_boxtype(obj,FL_UP_BOX);
475         fl_set_object_color(obj,FL_MCOL,FL_BLUE);
476         fl_set_object_resize(obj, FL_RESIZE_ALL);
477         fl_set_object_gravity(obj, SouthEastGravity, SouthEastGravity);
478         fl_set_object_callback(obj,C_BufferView_DownCB,(long)this);
479         fl_set_pixmapbutton_data(obj, down_xpm);
480         fl_set_border_width(-bw);
481
482 #if FL_REVISION >85
483         // Remove the blue feedback rectangle
484         fl_set_pixmapbutton_focus_outline(obj,0);
485 #endif  
486
487         //
488         // TIMERS
489         //
490         
491         // timer_cursor
492         timer_cursor = obj = fl_add_timer(FL_HIDDEN_TIMER,
493                                           0,0,0,0,"Timer");
494         fl_set_object_callback(obj,C_BufferView_CursorToggleCB,0);
495         obj->u_vdata = this;
496 }
497
498
499 // Callback for scrollbar up button
500 void BufferView::UpCB(FL_OBJECT *ob, long buf)
501 {
502         BufferView *view = (BufferView*) buf;
503         
504         if (view->_buffer == 0) return;
505
506         const XEvent*ev2;
507         static long time = 0;
508         ev2 = fl_last_event();
509         if (ev2->type == ButtonPress || ev2->type == ButtonRelease) 
510                 time = 0;
511         int button = fl_get_button_numb(ob);
512         switch (button) {
513         case 3:
514                 view->ScrollUpOnePage(time++); break;
515         case 2:
516                 view->ScrollDownOnePage(time++); break;
517         default:
518                 view->ScrollUp(time++); break;
519         }
520 }
521
522
523 static
524 void waitForX()
525 {
526         static Window w = 0;
527         static Atom a = 0;
528         if (!a)
529                 a = XInternAtom(fl_display, "WAIT_FOR_X", False);
530         if (w == 0) {
531                 int mask;
532                 XSetWindowAttributes attr;
533                 mask = CWOverrideRedirect;
534                 attr.override_redirect = 1;
535                 w = XCreateWindow(fl_display, fl_root,
536                                   0, 0, 1, 1, 0, CopyFromParent,
537                                   InputOnly, CopyFromParent, mask, &attr);
538                 XSelectInput(fl_display, w, PropertyChangeMask);
539                 XMapWindow(fl_display, w);
540         }
541         static XEvent ev;
542         XChangeProperty(fl_display, w, a, a, 8,
543                         PropModeAppend, (unsigned char *)"", 0);
544         XWindowEvent(fl_display, w, PropertyChangeMask, &ev);
545 }
546
547
548 // Callback for scrollbar slider
549 void BufferView::ScrollCB(FL_OBJECT *ob, long buf)
550 {
551         BufferView *view = (BufferView*) buf;
552         extern bool cursor_follows_scrollbar;
553         
554         if (view->_buffer == 0) return;
555
556         view->current_scrollbar_value = (long)fl_get_slider_value(ob);
557         if (view->current_scrollbar_value < 0)
558                 view->current_scrollbar_value = 0;
559    
560         if (!view->screen)
561                 return;
562
563         view->screen->Draw(view->current_scrollbar_value);
564
565         if (cursor_follows_scrollbar) {
566                 LyXText * vbt = view->_buffer->text;
567                 int height = vbt->DefaultHeight();
568                 
569                 if (vbt->cursor.y < view->screen->first + height) {
570                         vbt->SetCursorFromCoordinates(0,
571                                                       view->screen->first +
572                                                       height);
573                 }
574                 else if (vbt->cursor.y >
575                          view->screen->first + view->work_area->h - height) {
576                         vbt->SetCursorFromCoordinates(0,
577                                                       view->screen->first +
578                                                       view->work_area->h  -
579                                                       height);
580                 }
581         }
582         waitForX();
583 }
584
585
586 // Callback for scrollbar down button
587 void BufferView::DownCB(FL_OBJECT *ob, long buf)
588 {
589         BufferView *view = (BufferView*) buf;
590
591         if (view->_buffer == 0) return;
592         
593         const XEvent*ev2;
594         static long time = 0;
595         ev2 = fl_last_event();
596         if (ev2->type == ButtonPress || ev2->type == ButtonRelease) 
597                 time = 0;
598         int button = fl_get_button_numb(ob);
599         switch (button) {
600         case 2:
601                 view->ScrollUpOnePage(time++); break;
602         case 3:
603                 view->ScrollDownOnePage(time++); break;
604         default:
605                 view->ScrollDown(time++); break;
606         }
607 }
608
609
610 int BufferView::ScrollUp(long time)
611 {
612         if (_buffer == 0) return 0;
613         if (!screen)
614                 return 0;
615    
616         double value= fl_get_slider_value(scrollbar);
617    
618         if (value == 0)
619                 return 0;
620    
621         float add_value =  (_buffer->text->DefaultHeight()
622                             + (float)(time) * (float)(time) * 0.125);
623    
624         if (add_value > work_area->h)
625                 add_value = (float) (work_area->h -
626                                      _buffer->text->DefaultHeight());
627    
628         value -= add_value;
629
630         if (value < 0)
631                 value = 0;
632    
633         fl_set_slider_value(scrollbar, value);
634    
635         ScrollCB(scrollbar,(long)this); 
636         return 0;
637 }
638
639
640 int BufferView::ScrollDown(long time)
641 {
642         if (_buffer == 0) return 0;
643         if (!screen)
644                 return 0;
645    
646         double value= fl_get_slider_value(scrollbar);
647         double min, max;
648         fl_get_slider_bounds(scrollbar, &min, &max);
649
650         if (value == max)
651                 return 0;
652    
653         float add_value =  (_buffer->text->DefaultHeight()
654                             + (float)(time) * (float)(time) * 0.125);
655    
656         if (add_value > work_area->h)
657                 add_value = (float) (work_area->h -
658                                      _buffer->text->DefaultHeight());
659    
660         value += add_value;
661    
662         if (value > max)
663                 value = max;
664    
665         fl_set_slider_value(scrollbar, value);
666    
667         ScrollCB(scrollbar,(long)this); 
668         return 0;
669 }
670
671
672 void BufferView::ScrollUpOnePage(long /*time*/)
673 {
674         if (_buffer == 0) return;
675         if (!screen)
676                 return;
677    
678         long y = screen->first;
679
680         if (!y) return;
681    
682         Row* row = _buffer->text->GetRowNearY(y);
683         y = y - work_area->h + row->height;
684         
685         fl_set_slider_value(scrollbar, y);
686    
687         ScrollCB(scrollbar,(long)this); 
688 }
689
690
691 void BufferView::ScrollDownOnePage(long /*time*/)
692 {
693         if (_buffer == 0) return;
694         if (!screen)
695                 return;
696    
697         double min, max;
698         fl_get_slider_bounds(scrollbar, &min, &max);
699         long y = screen->first;
700    
701         if (y > _buffer->text->height - work_area->h)
702                 return;
703    
704         y += work_area->h;
705         _buffer->text->GetRowNearY(y);
706         
707         fl_set_slider_value(scrollbar, y);
708    
709         ScrollCB(scrollbar,(long)this); 
710 }
711
712
713 int BufferView::work_area_handler(FL_OBJECT * ob, int event,
714                                   FL_Coord, FL_Coord ,
715                                   int /*key*/, void *xev)
716 {
717         static int x_old = -1;
718         static int y_old = -1;
719         static long scrollbar_value_old = -1;
720         
721         XEvent* ev = (XEvent*) xev;
722         BufferView *view = (BufferView*) ob->u_vdata;
723
724         // If we don't have a view yet; return
725         if (!view || quitting) return 0;
726
727         switch (event){   
728         case FL_DRAW:
729                 view->workAreaExpose(); 
730                 break;
731         case FL_PUSH:
732                 view->WorkAreaButtonPress(ob, 0,0,0,ev,0);
733                 break; 
734         case FL_RELEASE:
735                 view->WorkAreaButtonRelease(ob, 0,0,0,ev,0);
736                 break;
737         case FL_MOUSE:
738                 if (ev->xmotion.x != x_old || 
739                     ev->xmotion.y != y_old ||
740                     view->current_scrollbar_value != scrollbar_value_old) {
741                         x_old = ev->xmotion.x;
742                         y_old = ev->xmotion.y;
743                         scrollbar_value_old = view->current_scrollbar_value;
744                         view->WorkAreaMotionNotify(ob, 0,0,0,ev,0);
745                 }
746                 break;
747         // Done by the raw callback:
748         //  case FL_KEYBOARD: WorkAreaKeyPress(ob, 0,0,0,ev,0); break;
749         case FL_FOCUS:
750                 if (!view->_owner->getMiniBuffer()->shows_no_match)
751                         view->_owner->getMiniBuffer()->Init();
752                 view->_owner->getMiniBuffer()->shows_no_match = false;
753                 view->work_area_focus = true;
754                 fl_set_timer(view->timer_cursor, 0.4);
755                 break;
756         case FL_UNFOCUS:
757                 view->_owner->getMiniBuffer()->ExecCommand();
758                 view->work_area_focus = false;
759                 break;
760         case FL_ENTER:
761                 SetXtermCursor(view->_owner->getForm()->window);
762                 // reset the timer
763                 view->lyx_focus = true;
764                 fl_set_timer(view->timer_cursor, 0.4);
765                 break;
766         case FL_LEAVE: 
767                 if (!input_prohibited)
768                         XUndefineCursor(fl_display,
769                                         view->_owner->getForm()->window);
770                 view->lyx_focus = false; // This is not an absolute truth
771                 // but if it is not true, it will be changed within a blink
772                 // of an eye. ... Not good enough... use regulare timeperiod
773                 //fl_set_timer(view->timer_cursor, 0.01); // 0.1 sec blink
774                 fl_set_timer(view->timer_cursor, 0.4); // 0.4 sec blink
775                 break;
776         case FL_DBLCLICK: 
777                 // select a word 
778                 if (view->_buffer && !view->_buffer->the_locking_inset) {
779                         if (view->screen && ev->xbutton.button == 1) {
780                                 view->screen->HideCursor();
781                                 view->screen->ToggleSelection(); 
782                                 view->_buffer->text->SelectWord();
783                                 view->screen->ToggleSelection(false);
784                                 /* This will fit the cursor on the screen
785                                  * if necessary */ 
786                                 view->_buffer->update(0); 
787                         }
788                 }
789                 break;
790         case FL_TRPLCLICK:
791                 // select a line
792                 if (view->_buffer && view->screen && ev->xbutton.button == 1) {
793                         view->screen->HideCursor(); 
794                         view->screen->ToggleSelection(); 
795                         view->_buffer->text->CursorHome();
796                         view->_buffer->text->sel_cursor =
797                                 view->_buffer->text->cursor;
798                         view->_buffer->text->CursorEnd();
799                         view->_buffer->text->SetSelection();
800                         view->screen->ToggleSelection(false); 
801                         /* This will fit the cursor on the screen
802                          * if necessary */ 
803                         view->_buffer->update(0); 
804                 }
805                 break;
806         case FL_OTHER:
807                 view->WorkAreaSelectionNotify(ob,
808                                               view->_owner->getForm()->window,
809                                               0,0,ev,0); 
810                 break;
811         }
812         return 1;
813 }
814
815 int BufferView::WorkAreaMotionNotify(FL_OBJECT *ob, Window,
816                                      int /*w*/, int /*h*/,
817                                      XEvent *ev, void */*d*/)
818 {
819
820         if (_buffer == 0) return 0;
821         if (!screen) return 0;
822
823         // Check for inset locking
824         if (_buffer->the_locking_inset) {
825                 LyXCursor cursor = _buffer->text->cursor;
826                 _buffer->the_locking_inset->
827                         InsetMotionNotify(ev->xbutton.x - ob->x - cursor.x,
828                                           ev->xbutton.y - ob->y -
829                                           (cursor.y),
830                                           ev->xbutton.state);
831                 return 0;
832         }
833    
834         // Only use motion with button 1
835         if (!ev->xmotion.state & Button1MotionMask)
836                 return 0; 
837    
838         /* The selection possible is needed, that only motion events are 
839          * used, where the bottom press event was on the drawing area too */
840         if (selection_possible) {
841                 screen->HideCursor();
842
843                 _buffer->text->
844                         SetCursorFromCoordinates(ev->xbutton.x - ob->x,
845                                                  ev->xbutton.y - ob->y +
846                                                  screen->first);
847       
848                 if (!_buffer->text->selection)
849                     _buffer->update(-3); // Maybe an empty line was deleted
850       
851                 _buffer->text->SetSelection();
852                 screen->ToggleToggle();
853                 if (screen->FitCursor())
854                         updateScrollbar(); 
855                 screen->ShowCursor();
856         }
857         return 0;
858 }
859
860
861 extern int bibitemMaxWidth(const class LyXFont &);
862
863 // Single-click on work area
864 int BufferView::WorkAreaButtonPress(FL_OBJECT *ob, Window,
865                         int /*w*/, int /*h*/, XEvent *ev, void */*d*/)
866 {
867         last_click_x = -1;
868         last_click_y = -1;
869
870         if (_buffer == 0) return 0;
871         if (!screen) return 0;
872
873         int const x = ev->xbutton.x - ob->x;
874         int const y = ev->xbutton.y - ob->y;
875         // If we hit an inset, we have the inset coordinates in these
876         // and inset_hit points to the inset.  If we do not hit an
877         // inset, inset_hit is 0, and inset_x == x, inset_y == y.
878         int inset_x = x;
879         int inset_y = y;
880         Inset * inset_hit = checkInsetHit(inset_x, inset_y);
881
882         // ok ok, this is a hack.
883         int button = ev->xbutton.button;
884         if (button == 4 || button == 5) goto wheel;
885
886         {
887                 
888         if (_buffer->the_locking_inset) {
889                 // We are in inset locking mode
890                 
891                 /* Check whether the inset was hit. If not reset mode,
892                    otherwise give the event to the inset */
893                 if (inset_hit != 0) {
894                         _buffer->the_locking_inset->
895                                 InsetButtonPress(inset_x, inset_y, button);
896                         return 0;
897                 } else {
898                         UnlockInset(_buffer->the_locking_inset);
899                 }
900         }
901         
902         selection_possible = true;
903         screen->HideCursor();
904         
905         // Right button mouse click on a table
906         if (button == 3 &&
907             (_buffer->text->cursor.par->table ||
908              _buffer->text->MouseHitInTable(x, y+screen->first))) {
909                 // Set the cursor to the press-position
910                 _buffer->text->SetCursorFromCoordinates(x, y + screen->first);
911                 bool doit = true;
912                 
913                 // Only show the table popup if the hit is in the table, too
914                 if (!_buffer->text->HitInTable(_buffer->text->cursor.row, x))
915                         doit = false;
916                 
917                 // Hit above or below the table?
918                 if (doit) {
919                         if (!_buffer->text->selection) {
920                                 screen->ToggleSelection();
921                                 _buffer->text->ClearSelection();
922                                 _buffer->text->FullRebreak();
923                                 screen->Update();
924                                 updateScrollbar();
925                         }
926                         // Popup table popup when on a table.
927                         // This is obviously temporary, since we should be
928                         // able to 
929                         // popup various context-sensitive-menus with the
930                         // the right mouse. So this should be done more
931                         // general in the future. Matthias.
932                         selection_possible = false;
933                         _owner->getLyXFunc()->Dispatch(LFUN_LAYOUT_TABLE,
934                                                        "true");
935                         return 0;
936                 }
937         }
938         
939         int screen_first = screen->first;
940         
941         // Middle button press pastes if we have a selection
942         bool paste_internally = false;
943         if (button == 2  // && !_buffer->the_locking_inset
944             && _buffer->text->selection) {
945                 _owner->getLyXFunc()->Dispatch(LFUN_COPY);
946                 paste_internally = true;
947         }
948         
949         // Clear the selection
950         screen->ToggleSelection();
951         _buffer->text->ClearSelection();
952         _buffer->text->FullRebreak();
953         screen->Update();
954         updateScrollbar();
955                 
956         // Single left click in math inset?
957         if (inset_hit != 0 && inset_hit->Editable() == 2) {
958                 // Highly editable inset, like math
959                 selection_possible = false;
960                 _owner->updateLayoutChoice();
961                 _owner->getMiniBuffer()->Set(inset_hit->EditMessage());
962                 inset_hit->Edit(inset_x, inset_y);
963                 return 0;
964         } 
965
966         // Right click on a footnote flag opens float menu
967         if (button == 3) { 
968                 selection_possible = false;
969                 return 0;
970         }
971         
972         _buffer->text->SetCursorFromCoordinates(x, y + screen_first);
973         _buffer->text->FinishUndo();
974         _buffer->text->sel_cursor = _buffer->text->cursor;
975         _buffer->text->cursor.x_fix = _buffer->text->cursor.x;
976         
977         _owner->updateLayoutChoice();
978         if (screen->FitCursor()){
979                 updateScrollbar();
980                 selection_possible = false;
981         }
982
983         // Insert primary selection with middle mouse
984         // if there is a local selection in the current buffer, insert this
985         if (button == 2) { //  && !_buffer->the_locking_inset){
986                 if (paste_internally)
987                         _owner->getLyXFunc()->Dispatch(LFUN_PASTE);
988                 else
989                         _owner->getLyXFunc()->Dispatch(LFUN_PASTESELECTION,
990                                                        "paragraph");
991                 selection_possible = false;
992                 return 0;
993         }
994         }
995         goto out;
996  wheel: {
997         // I am not quite sure if this is the correct place to put this,
998         // but it will not cause any harm.
999         // Patch from Mark Huang (markman@mit.edu) to make LyX recognise
1000         // button 4 and 5. This enables LyX use use the scrollwhell on
1001         // certain mice for something useful. (Lgb)
1002         // Added wheel acceleration detection code. (Rvdk)
1003         static Time lastTime = 0;
1004         int diff = ev->xbutton.time - lastTime;
1005         int scroll = int(1.0 + (4.0/(abs(diff)+1.0))*200.0);
1006         switch (button) {
1007         case 4:
1008                 ScrollUp(scroll);
1009                 break;
1010         case 5:
1011                 ScrollDown(scroll);
1012                 break;
1013         }
1014         lastTime = ev->xbutton.time;
1015         return 0;
1016         }
1017  out:
1018         last_click_x = x;
1019         last_click_y = y;
1020         
1021         return 0;
1022 }
1023
1024
1025 int BufferView::WorkAreaButtonRelease(FL_OBJECT *ob, Window ,
1026                           int /*w*/, int /*h*/, XEvent *ev, void */*d*/)
1027 {
1028         if (_buffer == 0 || screen == 0) return 0;
1029
1030         int const x = ev->xbutton.x - ob->x;
1031         int const y = ev->xbutton.y - ob->y;
1032
1033         // If we hit an inset, we have the inset coordinates in these
1034         // and inset_hit points to the inset.  If we do not hit an
1035         // inset, inset_hit is 0, and inset_x == x, inset_y == y.
1036         int inset_x = x;
1037         int inset_y = y;
1038         Inset * inset_hit = checkInsetHit(inset_x, inset_y);
1039
1040         if (_buffer->the_locking_inset) {
1041                 // We are in inset locking mode.
1042
1043                 /* LyX does a kind of work-area grabbing for insets.
1044                    Only a ButtonPress Event outside the inset will 
1045                    force a InsetUnlock. */
1046                 _buffer->the_locking_inset->
1047                         InsetButtonRelease(inset_x, inset_y, 
1048                                            ev->xbutton.button);
1049                 return 0;
1050         }
1051   
1052         selection_possible = false;
1053         if (_buffer->text->cursor.par->table) {
1054                 int cell = _buffer->text->
1055                         NumberOfCell(_buffer->text->cursor.par,
1056                                      _buffer->text->cursor.pos);
1057                 if (_buffer->text->cursor.par->table->IsContRow(cell) &&
1058                     _buffer->text->cursor.par->table->
1059                     CellHasContRow(_buffer->text->cursor.par->table->
1060                                    GetCellAbove(cell))<0) {
1061                         _buffer->text->CursorUp();
1062                 }
1063         }
1064         
1065         if (ev->xbutton.button >= 2)
1066                 return 0;
1067
1068         // Make sure that the press was not far from the release
1069         if ((abs(last_click_x - x) >= 5) ||
1070             (abs(last_click_y - y) >= 5)) {
1071                 return 0;
1072         }
1073
1074         // Did we hit an editable inset?
1075         if (inset_hit != 0) {
1076                 // Inset like error, notes and figures
1077                 selection_possible = false;
1078 #ifdef WITH_WARNINGS
1079 #warning fix this proper in 0.13
1080 #endif
1081                 // Following a ref shouldn't issue
1082                 // a push on the undo-stack
1083                 // anylonger, now that we have
1084                 // keybindings for following
1085                 // references and returning from
1086                 // references.  IMHO though, it
1087                 // should be the inset's own business
1088                 // to push or not push on the undo
1089                 // stack. They don't *have* to
1090                 // alter the document...
1091                 // (Joacim)
1092                 // ...or maybe the SetCursorParUndo()
1093                 // below isn't necessary at all anylonger?
1094                 if (inset_hit->LyxCode() == Inset::REF_CODE) {
1095                         _buffer->text->SetCursorParUndo();
1096                 }
1097
1098                 _owner->getMiniBuffer()->Set(inset_hit->EditMessage());
1099                 inset_hit->Edit(inset_x, inset_y);
1100                 return 0;
1101         }
1102
1103         // check whether we want to open a float
1104         if (_buffer->text) {
1105                 bool hit = false;
1106                 char c = ' ';
1107                 if (_buffer->text->cursor.pos <
1108                     _buffer->text->cursor.par->Last()) {
1109                         c = _buffer->text->cursor.par->
1110                                 GetChar(_buffer->text->cursor.pos);
1111                 }
1112                 if (c == LYX_META_FOOTNOTE || c == LYX_META_MARGIN
1113                     || c == LYX_META_FIG || c == LYX_META_TAB
1114                     || c == LYX_META_WIDE_FIG || c == LYX_META_WIDE_TAB
1115                     || c == LYX_META_ALGORITHM){
1116                         hit = true;
1117                 } else if (_buffer->text->cursor.pos - 1 >= 0) {
1118                         c = _buffer->text->cursor.par->
1119                                 GetChar(_buffer->text->cursor.pos - 1);
1120                         if (c == LYX_META_FOOTNOTE || c == LYX_META_MARGIN
1121                             || c == LYX_META_FIG || c == LYX_META_TAB
1122                             || c == LYX_META_WIDE_FIG 
1123                             || c == LYX_META_WIDE_TAB
1124                             || c == LYX_META_ALGORITHM){
1125                                 // We are one step too far to the right
1126                                 _buffer->text->CursorLeft();
1127                                 hit = true;
1128                         }
1129                 }
1130                 if (hit == true) {
1131                         ToggleFloat();
1132                         selection_possible = false;
1133                         return 0;
1134                 }
1135         }
1136
1137         // Do we want to close a float? (click on the float-label)
1138         if (_buffer->text->cursor.row->par->footnoteflag ==
1139             LyXParagraph::OPEN_FOOTNOTE
1140             && _buffer->text->cursor.pos == 0
1141             && _buffer->text->cursor.row->previous &&
1142             _buffer->text->cursor.row->previous->par->
1143             footnoteflag != LyXParagraph::OPEN_FOOTNOTE){
1144                 LyXFont font (LyXFont::ALL_SANE);
1145                 font.setSize(LyXFont::SIZE_SMALL);
1146
1147                 int box_x = 20; // LYX_PAPER_MARGIN;
1148                 box_x += font.textWidth("Mwide-figM", 10);
1149
1150                 int screen_first = screen->first;
1151
1152                 if (x < box_x
1153                     && y + screen_first > _buffer->text->cursor.y -
1154                     _buffer->text->cursor.row->baseline
1155                     && y + screen_first < _buffer->text->cursor.y -
1156                     _buffer->text->cursor.row->baseline
1157                     + font.maxAscent()*1.2 + font.maxDescent()*1.2) {
1158                         ToggleFloat();
1159                         selection_possible = false;
1160                         return 0;
1161                 }
1162         }
1163
1164         // Maybe we want to edit a bibitem ale970302
1165         if (_buffer->text->cursor.par->bibkey && x < 20 + 
1166             bibitemMaxWidth(lyxstyle.TextClass(_buffer->
1167                                         params.textclass)->defaultfont)) {
1168                 _buffer->text->cursor.par->bibkey->Edit(0, 0);
1169         }
1170
1171         return 0;
1172 }
1173
1174
1175 /* 
1176  * Returns an inset if inset was hit. 0 otherwise.
1177  * If hit, the coordinates are changed relative to the inset. 
1178  * Otherwise coordinates are not changed, and false is returned.
1179  */
1180 Inset * BufferView::checkInsetHit(int &x, int &y)
1181 {
1182         if (!getScreen())
1183                 return 0;
1184   
1185         int y_tmp = y + getScreen()->first;
1186   
1187         LyXCursor cursor = _buffer->text->cursor;
1188         if (cursor.pos < cursor.par->Last() 
1189             && cursor.par->GetChar(cursor.pos) == LYX_META_INSET
1190             && cursor.par->GetInset(cursor.pos)
1191             && cursor.par->GetInset(cursor.pos)->Editable()) {
1192
1193                 // Check whether the inset really was hit
1194                 Inset* tmpinset = cursor.par->GetInset(cursor.pos);
1195                 LyXFont font = _buffer->text->GetFont(cursor.par, cursor.pos);
1196                 if (x > cursor.x
1197                     && x < cursor.x + tmpinset->Width(font) 
1198                     && y_tmp > cursor.y - tmpinset->Ascent(font)
1199                     && y_tmp < cursor.y + tmpinset->Descent(font)) {
1200                         x = x - cursor.x;
1201                         // The origin of an inset is on the baseline
1202                         y = y_tmp - (cursor.y); 
1203                         return tmpinset;
1204                 }
1205         } else if (cursor.pos - 1 >= 0 
1206                    && cursor.par->GetChar(cursor.pos - 1) == LYX_META_INSET
1207                    && cursor.par->GetInset(cursor.pos - 1)
1208                    && cursor.par->GetInset(cursor.pos - 1)->Editable()) {
1209                 _buffer->text->CursorLeft();
1210                 Inset * result = checkInsetHit(x, y);
1211                 if (result == 0) {
1212                         _buffer->text->CursorRight();
1213                         return 0;
1214                 } else {
1215                         return result;
1216                 }
1217         }
1218         return 0;
1219 }
1220
1221
1222 int BufferView::workAreaExpose()
1223 {
1224         if (!work_area || !work_area->form->visible) 
1225                 return 1;
1226
1227         // this is a hack to ensure that we only call this through
1228         // BufferView::redraw().
1229         if (!lgb_hack) {
1230                 redraw();
1231         }
1232         
1233         static int work_area_width = work_area->w;
1234         static int work_area_height = work_area->h;
1235
1236         bool widthChange = work_area->w != work_area_width;
1237         bool heightChange = work_area->h != work_area_height;
1238
1239         // update from work area
1240         work_area_width = work_area->w;
1241         work_area_height = work_area->h;
1242         if (_buffer != 0) {
1243                 if (widthChange) {
1244                         // All buffers need a resize
1245                         bufferlist.resize();
1246                 } else if (heightChange) {
1247                         // Rebuild image of current screen
1248                         updateScreen();
1249                         // fitCursor() ensures we don't jump back
1250                         // to the start of the document on vertical
1251                         // resize
1252                         fitCursor();
1253
1254                         // The main window size has changed, repaint most stuff
1255                         redraw();
1256                         // ...including the minibuffer
1257                         _owner->getMiniBuffer()->Init();
1258
1259                 } else if (screen) screen->Redraw();
1260         } else {
1261                 // Grey box when we don't have a buffer
1262                 fl_winset(FL_ObjWin(work_area));
1263                 fl_rectangle(1, work_area->x, work_area->y,
1264                              work_area->w, work_area->h, FL_GRAY63);
1265         }
1266
1267         // always make sure that the scrollbar is sane.
1268         updateScrollbar();
1269         _owner->updateLayoutChoice();
1270         return 1;
1271 }
1272
1273
1274 // Callback for cursor timer
1275 void BufferView::CursorToggleCB(FL_OBJECT *ob, long)
1276 {
1277         BufferView *view = (BufferView*) ob->u_vdata;
1278         
1279         /* quite a nice place for asyncron Inset updating, isn't it? */
1280         // actually no! This is run even if no buffer exist... so (Lgb)
1281         if (view && !view->_buffer) {
1282                 goto set_timer_and_return;
1283         }
1284 #ifdef WITH_WARNINGS
1285 #warning NOTE!
1286 #endif
1287
1288         // On my quest to solve the gs rendre hangups I am now
1289         // disabling the SIGHUP completely, and will do a wait
1290         // now and then instead. If the guess that xforms somehow
1291         // destroys something is true, this is likely (hopefully)
1292         // to solve the problem...at least I hope so. Lgb
1293
1294         // ...Ok this seems to work...at least it does not make things
1295         // worse so far. However I still see gs processes that hangs.
1296         // I would really like to know _why_ they are hanging. Anyway
1297         // the solution without the SIGCHLD handler seems to be easier
1298         // to debug.
1299
1300         // When attaching gdb to a a running gs that hangs it shows
1301         // that it is waiting for input(?) Is it possible for us to
1302         // provide that input somehow? Or figure what it is expecing
1303         // to read?
1304
1305         // One solution is to, after some time, look if there are some
1306         // old gs processes still running and if there are: kill them
1307         // and re render.
1308
1309         // Another solution is to provide the user an option to rerender
1310         // a picture. This would, for the picture in question, check if
1311         // there is a gs running for it, if so kill it, and start a new
1312         // rendering process.
1313
1314         // these comments posted to lyx@via
1315 //#if 0
1316         {
1317         int status = 1;
1318         int pid = waitpid((pid_t)0, &status, WNOHANG);
1319         if (pid == -1) // error find out what is wrong
1320                 ; // ignore it for now.
1321         else if (pid > 0)
1322                 sigchldhandler(pid, &status);
1323         }
1324 //#endif
1325         if (InsetUpdateList) 
1326                 UpdateInsetUpdateList();
1327
1328         if (view && !view->screen){
1329                 goto set_timer_and_return;
1330         }
1331
1332         if (view->lyx_focus && view->work_area_focus) {
1333                 if (!view->_buffer->the_locking_inset){
1334                         view->screen->CursorToggle();
1335                 } else {
1336                         view->_buffer->the_locking_inset->
1337                                 ToggleInsetCursor();
1338                 }
1339                 goto set_timer_and_return;
1340         } else {
1341                 // Make sure that the cursor is visible.
1342                 if (!view->_buffer->the_locking_inset){
1343                         view->screen->ShowCursor();
1344                 } else {
1345                         if (!view->_buffer->the_locking_inset->isCursorVisible())
1346                                 view->_buffer->the_locking_inset->
1347                                         ToggleInsetCursor();
1348                 }
1349
1350                 // This is only run when work_area_focus or lyx_focus is false.
1351                 Window tmpwin;
1352                 int tmp;
1353                 XGetInputFocus(fl_display, &tmpwin, &tmp);
1354                 if (lyxerr.debugging()) {
1355                         lyxerr << "tmpwin: " << tmpwin
1356                                << "\nwindow: " << view->_owner->getForm()->window
1357                                << "\nwork_area_focus: " << view->work_area_focus
1358                                << "\nlyx_focus      : " << view->lyx_focus
1359                                << endl;
1360                 }
1361                 if (tmpwin != view->_owner->getForm()->window) {
1362                         view->lyx_focus = false;
1363                         goto skip_timer;
1364                 } else {
1365                         view->lyx_focus = true;
1366                         if (!view->work_area_focus)
1367                                 goto skip_timer;
1368                         else
1369                                 goto set_timer_and_return;
1370                 }
1371         }
1372
1373   set_timer_and_return:
1374         fl_set_timer(ob, 0.4);
1375   skip_timer:
1376         return;
1377 }
1378
1379
1380 int BufferView::WorkAreaSelectionNotify(FL_OBJECT *, Window win,
1381                             int /*w*/, int /*h*/, XEvent *event, void */*d*/)
1382 {
1383         if (_buffer == 0) return 0;
1384         if (event->type != SelectionNotify)
1385                 return 0;
1386
1387         Atom tmpatom;
1388         unsigned long ul1;
1389         unsigned long ul2;
1390         unsigned char* uc = 0;
1391         int tmpint;
1392         screen->HideCursor();
1393         BeforeChange();
1394         if (event->xselection.type == XA_STRING
1395             && event->xselection.property) {
1396     
1397                 if (XGetWindowProperty(
1398                         fl_display            /* display */,
1399                         win /* w */,
1400                         event->xselection.property        /* property */,
1401                         0                /* long_offset */,
1402                         0                /* long_length */,
1403                         false                /* 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                         free(uc);
1417                         uc = 0;
1418                 }
1419
1420                 if (XGetWindowProperty(
1421                         fl_display           /* display */,
1422                         win              /* w */,
1423                         event->xselection.property           /* property */,
1424                         0                /* long_offset */,
1425                         ul2/4+1                /* long_length */,
1426                         True                /* delete */,
1427                         XA_STRING                /* req_type */,
1428                         &tmpatom               /* actual_type_return */,
1429                         &tmpint                /* actual_format_return */,
1430                         &ul1      /* nitems_return */,
1431                         &ul2      /* bytes_after_return */,
1432                         &uc     /* prop_return */
1433                         ) != Success) {
1434                         return 0;
1435                 }
1436                 XFlush(fl_display);
1437         
1438                 if (uc){
1439                         if (!ascii_type)
1440                                 _buffer->text->
1441                                         InsertStringA((char*)uc);
1442                         else
1443                                 _buffer->text->
1444                                         InsertStringB((char*)uc);
1445                         free(uc);
1446                         uc = 0;
1447                 }
1448     
1449                 _buffer->update(1);
1450         }
1451         return 0;
1452 }
1453
1454
1455 void BufferView::cursorPrevious()
1456 {
1457         if (!currentBuffer()->text->cursor.row->previous) return;
1458         
1459         long y = getScreen()->first;
1460         Row* cursorrow = currentBuffer()->text->cursor.row;
1461         currentBuffer()->text->
1462           SetCursorFromCoordinates(currentBuffer()->text->
1463                                    cursor.x_fix,
1464                                    y);
1465         currentBuffer()->text->FinishUndo();
1466         /* this is to allow jumping over large insets */
1467         if ((cursorrow == currentBuffer()->text->cursor.row))
1468           currentBuffer()->text->CursorUp();
1469         
1470         if (currentBuffer()->text->cursor.row->height < work_area->h)
1471           getScreen()->Draw(currentBuffer()->text->cursor.y
1472                             - currentBuffer()->text->cursor.row->baseline
1473                             + currentBuffer()->text->cursor.row->height
1474                             - work_area->h +1 );
1475 }
1476
1477
1478 void BufferView::cursorNext()
1479 {
1480         if (!currentBuffer()->text->cursor.row->next) return;
1481         
1482         long y = getScreen()->first;
1483         currentBuffer()->text->GetRowNearY(y);
1484         Row* cursorrow = currentBuffer()->text->cursor.row;
1485         currentBuffer()->text->
1486                 SetCursorFromCoordinates(currentBuffer()->text->
1487                                          cursor.x_fix, 
1488                                          y + work_area->h);
1489         currentBuffer()->text->FinishUndo();
1490         /* this is to allow jumping over large insets */
1491         if ((cursorrow == currentBuffer()->text->cursor.row))
1492           currentBuffer()->text->CursorDown();
1493         
1494         if (currentBuffer()->text->cursor.row->height < work_area->h)
1495           getScreen()->Draw(currentBuffer()->text->cursor.y
1496                             - currentBuffer()->text->cursor.row->baseline);
1497 }
1498
1499
1500 bool BufferView::available() const
1501 {
1502         if (_buffer && _buffer->text) return true;
1503         return false;
1504 }
1505
1506
1507 void BufferView::savePosition()
1508 {
1509         backstack->push(currentBuffer()->getFileName(),
1510                         currentBuffer()->text->cursor.x,
1511                         currentBuffer()->text->cursor.y);
1512 }
1513
1514
1515 void BufferView::restorePosition()
1516 {
1517         if (backstack->empty()) return;
1518         
1519         int  x, y;
1520         string fname = backstack->pop(&x, &y);
1521         
1522         BeforeChange();
1523         Buffer *b = (bufferlist.exists(fname)) ? bufferlist.getBuffer(fname):
1524                 bufferlist.loadLyXFile(fname); // don't ask, just load it
1525         setBuffer(b);
1526         currentBuffer()->text->SetCursorFromCoordinates(x, y);
1527         currentBuffer()->update(0);
1528