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