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