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