]> git.lyx.org Git - lyx.git/blob - src/BufferView_pimpl.C
a257021565f8c310879310f8cbb08a31648ff294
[lyx.git] / src / BufferView_pimpl.C
1 #include <config.h>
2
3 #include <unistd.h>
4 #include <sys/wait.h>
5
6 #ifdef __GNUG__
7 #pragma implementation
8 #endif
9
10 #include "BufferView_pimpl.h"
11 #include "WorkArea.h"
12 #include "lyxscreen.h"
13 #include "lyxtext.h"
14 #include "lyxrow.h"
15 #include "LyXView.h"
16 #include "commandtags.h"
17 #include "lyxfunc.h"
18 #include "minibuffer.h"
19 #include "font.h"
20 #include "bufferview_funcs.h"
21 #include "TextCache.h"
22 #include "bufferlist.h"
23 #include "insets/insetbib.h"
24 #include "lyx_gui_misc.h"
25 #include "lyxrc.h"
26 #include "intl.h"
27 #include "support/LAssert.h"
28 #include "frontends/Dialogs.h"
29
30 #ifdef SIGC_CXX_NAMESPACES
31 using SigC::slot;
32 #endif
33
34 using std::pair;
35 using std::endl;
36 using std::vector;
37 using std::make_pair;
38 using std::min;
39
40 /* the selection possible is needed, that only motion events are 
41  * used, where the bottom press event was on the drawing area too */
42 bool selection_possible = false;
43
44 extern BufferList bufferlist;
45 extern char ascii_type;
46
47 extern "C" void TimerCB(FL_OBJECT *, long); 
48 extern void sigchldhandler(pid_t pid, int * status);
49 extern int bibitemMaxWidth(BufferView *, LyXFont const &);
50
51 const unsigned int saved_positions_num = 20;
52
53 static inline
54 void waitForX()
55 {
56         XSync(fl_get_display(), 0);
57 }
58
59
60 static
61 void SetXtermCursor(Window win)
62 {
63         static Cursor cursor;
64         static bool cursor_undefined = true;
65         if (cursor_undefined){
66                 cursor = XCreateFontCursor(fl_get_display(), XC_xterm);
67                 XFlush(fl_get_display());
68                 cursor_undefined = false;
69         }
70         XDefineCursor(fl_get_display(), win, cursor);
71         XFlush(fl_get_display());
72 }
73
74
75 BufferView::Pimpl::Pimpl(BufferView * b, LyXView * o,
76              int xpos, int ypos, int width, int height)
77         : bv_(b), owner_(o), cursor_timeout(400)
78 {
79         buffer_ = 0;
80 #if 0
81         workarea_ = new WorkArea(bv_, xpos, ypos, width, height);
82 #else
83         workarea_ = new WorkArea(xpos, ypos, width, height);
84 #endif
85         // Setup the signals
86         workarea_->scrollCB.connect(slot(this, &BufferView::Pimpl::scrollCB));
87         workarea_->workAreaExpose
88                 .connect(slot(this, &BufferView::Pimpl::workAreaExpose));
89         workarea_->workAreaEnter
90                 .connect(slot(this, &BufferView::Pimpl::enterView));
91         workarea_->workAreaLeave
92                 .connect(slot(this, &BufferView::Pimpl::leaveView));
93         workarea_->workAreaButtonPress
94                 .connect(slot(this, &BufferView::Pimpl::workAreaButtonPress));
95         workarea_->workAreaButtonRelease
96                 .connect(slot(this,
97                               &BufferView::Pimpl::workAreaButtonRelease));
98         workarea_->workAreaMotionNotify
99                 .connect(slot(this, &BufferView::Pimpl::workAreaMotionNotify));
100         workarea_->workAreaDoubleClick
101                 .connect(slot(this, &BufferView::Pimpl::doubleClick));
102         workarea_->workAreaTripleClick
103                 .connect(slot(this, &BufferView::Pimpl::tripleClick));
104         workarea_->workAreaKeyPress
105                 .connect(slot(this, &BufferView::Pimpl::workAreaKeyPress));
106         
107         screen_ = 0;
108
109         cursor_timeout.timeout.connect(slot(this,
110                                             &BufferView::Pimpl::cursorToggle));
111         current_scrollbar_value = 0;
112         cursor_timeout.start();
113         workarea_->setFocus();
114         using_xterm_cursor = false;
115         saved_positions.resize(saved_positions_num);
116 }
117
118
119 Painter & BufferView::Pimpl::painter() 
120 {
121         return workarea_->getPainter();
122 }
123
124
125 void BufferView::Pimpl::buffer(Buffer * b)
126 {
127         lyxerr[Debug::INFO] << "Setting buffer in BufferView ("
128                             << b << ")" << endl;
129         if (buffer_) {
130                 bv_->insetSleep();
131                 buffer_->delUser(bv_);
132
133                 // Put the old text into the TextCache, but
134                 // only if the buffer is still loaded.
135                 // Also set the owner of the test to 0
136                 //              bv_->text->owner(0);
137                 textcache.add(buffer_, workarea_->workWidth(), bv_->text);
138                 if (lyxerr.debugging())
139                         textcache.show(lyxerr, "BufferView::buffer");
140                 
141                 bv_->text = 0;
142         }
143
144         // Set current buffer
145         buffer_ = b;
146
147         if (bufferlist.getState() == BufferList::CLOSING) return;
148         
149         // Nuke old image
150         // screen is always deleted when the buffer is changed.
151         delete screen_;
152         screen_ = 0;
153
154         // If we are closing the buffer, use the first buffer as current
155         if (!buffer_) {
156                 buffer_ = bufferlist.first();
157         }
158
159         if (buffer_) {
160                 lyxerr[Debug::INFO] << "Buffer addr: " << buffer_ << endl;
161                 buffer_->addUser(bv_);
162                 // If we don't have a text object for this, we make one
163                 if (bv_->text == 0) {
164                         resizeCurrentBuffer();
165                 } else {
166                         updateScreen();
167                         updateScrollbar();
168                 }
169                 bv_->text->first = screen_->TopCursorVisible(bv_->text);
170                 owner_->updateMenubar();
171                 owner_->updateToolbar();
172                 // Similarly, buffer-dependent dialogs should be updated or
173                 // hidden. This should go here because some dialogs (eg ToC)
174                 // require bv_->text.
175                 owner_->getDialogs()->updateBufferDependent(true);
176                 redraw();
177                 bv_->insetWakeup();
178         } else {
179                 lyxerr[Debug::INFO] << "  No Buffer!" << endl;
180                 owner_->updateMenubar();
181                 owner_->updateToolbar();
182                 owner_->getDialogs()->hideBufferDependent();
183                 updateScrollbar();
184                 workarea_->redraw();
185
186                 // Also remove all remaining text's from the testcache.
187                 // (there should not be any!) (if there is any it is a
188                 // bug!)
189                 if (lyxerr.debugging())
190                         textcache.show(lyxerr, "buffer delete all");
191                 textcache.clear();
192         }
193         // should update layoutchoice even if we don't have a buffer.
194         owner_->updateLayoutChoice();
195         owner_->getMiniBuffer()->Init();
196         owner_->updateWindowTitle();
197 }
198
199
200 void BufferView::Pimpl::resize(int xpos, int ypos, int width, int height)
201 {
202         workarea_->resize(xpos, ypos, width, height);
203         update(SELECT);
204         redraw();
205 }
206
207
208 void BufferView::Pimpl::resize()
209 {
210         // This will resize the buffer. (Asger)
211         if (buffer_)
212                 resizeCurrentBuffer();
213 }
214
215
216 void BufferView::Pimpl::redraw()
217 {
218         lyxerr[Debug::INFO] << "BufferView::redraw()" << endl;
219         workarea_->redraw();
220 }
221
222
223 bool BufferView::Pimpl::fitCursor(LyXText * text)
224 {
225         Assert(screen_); // it is a programming error to call fitCursor
226         // without a valid screen.
227         bool ret = screen_->FitCursor(text, bv_);
228         if (ret)
229             updateScrollbar();
230         return ret;
231 }
232
233
234 void BufferView::Pimpl::redoCurrentBuffer()
235 {
236         lyxerr[Debug::INFO] << "BufferView::redoCurrentBuffer" << endl;
237         if (buffer_ && bv_->text) {
238                 resize();
239                 owner_->updateLayoutChoice();
240         }
241 }
242
243
244 int BufferView::Pimpl::resizeCurrentBuffer()
245 {
246         lyxerr[Debug::INFO] << "resizeCurrentBuffer" << endl;
247         
248         LyXParagraph * par = 0;
249         LyXParagraph * selstartpar = 0;
250         LyXParagraph * selendpar = 0;
251         int pos = 0;
252         int selstartpos = 0;
253         int selendpos = 0;
254         int selection = 0;
255         int mark_set = 0;
256
257         ProhibitInput(bv_);
258
259         owner_->getMiniBuffer()->Set(_("Formatting document..."));   
260
261         if (bv_->text) {
262                 par = bv_->text->cursor.par();
263                 pos = bv_->text->cursor.pos();
264                 selstartpar = bv_->text->sel_start_cursor.par();
265                 selstartpos = bv_->text->sel_start_cursor.pos();
266                 selendpar = bv_->text->sel_end_cursor.par();
267                 selendpos = bv_->text->sel_end_cursor.pos();
268                 selection = bv_->text->selection;
269                 mark_set = bv_->text->mark_set;
270                 delete bv_->text;
271                 bv_->text = new LyXText(bv_);
272         } else {
273                 // See if we have a text in TextCache that fits
274                 // the new buffer_ with the correct width.
275                 bv_->text = textcache.findFit(buffer_, workarea_->workWidth());
276                 if (bv_->text) {
277                         if (lyxerr.debugging()) {
278                                 lyxerr << "Found a LyXText that fits:\n";
279                                 textcache.show(lyxerr, make_pair(buffer_, make_pair(workarea_->workWidth(), bv_->text)));
280                         }
281                         // Set the owner of the newly found text
282                         //      bv_->text->owner(bv_);
283                         if (lyxerr.debugging())
284                                 textcache.show(lyxerr, "resizeCurrentBuffer");
285                 } else {
286                         bv_->text = new LyXText(bv_);
287                 }
288         }
289         updateScreen();
290
291         if (par) {
292                 bv_->text->selection = true;
293                 /* at this point just to avoid the Delete-Empty-Paragraph
294                  * Mechanism when setting the cursor */
295                 bv_->text->mark_set = mark_set;
296                 if (selection) {
297                         bv_->text->SetCursor(bv_, selstartpar, selstartpos);
298                         bv_->text->sel_cursor = bv_->text->cursor;
299                         bv_->text->SetCursor(bv_, selendpar, selendpos);
300                         bv_->text->SetSelection();
301                         bv_->text->SetCursor(bv_, par, pos);
302                 } else {
303                         bv_->text->SetCursor(bv_, par, pos);
304                         bv_->text->sel_cursor = bv_->text->cursor;
305                         bv_->text->selection = false;
306                 }
307         }
308         bv_->text->first = screen_->TopCursorVisible(bv_->text);
309         buffer_->resizeInsets(bv_);
310         // this will scroll the screen such that the cursor becomes visible
311         updateScrollbar();
312         redraw();
313         owner_->getMiniBuffer()->Init();
314         bv_->setState();
315         AllowInput(bv_);
316
317         // Now if the title form still exist kill it
318         TimerCB(0, 0);
319
320         return 0;
321 }
322
323
324 void BufferView::Pimpl::updateScreen()
325 {
326         // Regenerate the screen.
327         delete screen_;
328         screen_ = new LyXScreen(*workarea_);
329 }
330
331
332 void BufferView::Pimpl::updateScrollbar()
333 {
334         /* If the text is smaller than the working area, the scrollbar
335          * maximum must be the working area height. No scrolling will 
336          * be possible */
337
338         if (!buffer_) {
339                 workarea_->setScrollbar(0, 1.0);
340                 return;
341         }
342         
343         static unsigned long max2 = 0;
344         static unsigned long height2 = 0;
345
346         unsigned long cbth = 0;
347         long cbsf = 0;
348
349         if (bv_->text) {
350                 cbth = bv_->text->height;
351                 cbsf = bv_->text->first;
352         }
353
354         // check if anything has changed.
355         if (max2 == cbth &&
356             height2 == workarea_->height() &&
357             current_scrollbar_value == cbsf)
358                 return; // no
359         max2 = cbth;
360         height2 = workarea_->height();
361         current_scrollbar_value = cbsf;
362
363         if (cbth <= height2) { // text is smaller than screen
364                 workarea_->setScrollbar(0, 1.0); // right?
365                 return;
366         }
367
368         long maximum_height = workarea_->height() * 3 / 4 + cbth;
369         long value = cbsf;
370
371         // set the scrollbar
372         double hfloat = workarea_->height();
373         double maxfloat = maximum_height;
374
375         float slider_size = 0.0;
376         int slider_value = value;
377
378         workarea_->setScrollbarBounds(0, bv_->text->height - workarea_->height());
379         double lineh = bv_->text->DefaultHeight();
380         workarea_->setScrollbarIncrements(lineh);
381         if (maxfloat > 0.0) {
382                 if ((hfloat / maxfloat) * float(height2) < 3)
383                         slider_size = 3.0/float(height2);
384                 else
385                         slider_size = hfloat / maxfloat;
386         } else
387                 slider_size = hfloat;
388
389         workarea_->setScrollbar(slider_value, slider_size / workarea_->height());
390 }
391
392
393 // Callback for scrollbar slider
394 void BufferView::Pimpl::scrollCB(double value)
395 {
396         if (buffer_ == 0) return;
397
398         current_scrollbar_value = long(value);
399         if (current_scrollbar_value < 0)
400                 current_scrollbar_value = 0;
401    
402         if (!screen_)
403                 return;
404
405         screen_->Draw(bv_->text, bv_, current_scrollbar_value);
406
407         if (lyxrc.cursor_follows_scrollbar) {
408                 LyXText * vbt = bv_->text;
409                 int height = vbt->DefaultHeight();
410                 
411                 if (vbt->cursor.y() < static_cast<int>((bv_->text->first + height))) {
412                         vbt->SetCursorFromCoordinates(bv_, 0,
413                                                       bv_->text->first +
414                                                       height);
415                 } else if (vbt->cursor.y() >
416                            static_cast<int>((bv_->text->first+workarea_->height()-height)))
417                 {
418                         vbt->SetCursorFromCoordinates(bv_, 0,
419                                                       bv_->text->first +
420                                                       workarea_->height()  -
421                                                       height);
422                 }
423         }
424         waitForX();
425 }
426
427
428 int BufferView::Pimpl::scrollUp(long time)
429 {
430         if (buffer_ == 0) return 0;
431         if (!screen_) return 0;
432    
433         double value = workarea_->getScrollbarValue();
434    
435         if (value == 0) return 0;
436
437         float add_value =  (bv_->text->DefaultHeight()
438                             + float(time) * float(time) * 0.125);
439    
440         if (add_value > workarea_->height())
441                 add_value = float(workarea_->height() -
442                                   bv_->text->DefaultHeight());
443    
444         value -= add_value;
445
446         if (value < 0)
447                 value = 0;
448    
449         workarea_->setScrollbarValue(value);
450    
451         scrollCB(value); 
452         return 0;
453 }
454
455
456 int BufferView::Pimpl::scrollDown(long time)
457 {
458         if (buffer_ == 0) return 0;
459         if (!screen_) return 0;
460    
461         double value= workarea_->getScrollbarValue();
462         pair<float, float> p = workarea_->getScrollbarBounds();
463         double max = p.second;
464         
465         if (value == max) return 0;
466
467         float add_value =  (bv_->text->DefaultHeight()
468                             + float(time) * float(time) * 0.125);
469    
470         if (add_value > workarea_->height())
471                 add_value = float(workarea_->height() -
472                                   bv_->text->DefaultHeight());
473    
474         value += add_value;
475    
476         if (value > max)
477                 value = max;
478
479         workarea_->setScrollbarValue(value);
480         
481         scrollCB(value); 
482         return 0;
483 }
484
485
486 void BufferView::Pimpl::workAreaKeyPress(KeySym keysym, unsigned int state)
487 {
488         bv_->owner()->getLyXFunc()->processKeySym(keysym, state);
489 }
490
491
492 void BufferView::Pimpl::workAreaMotionNotify(int x, int y, unsigned int state)
493 {
494         // Only use motion with button 1
495         if (!(state & Button1MotionMask))
496                 return;
497
498         if (buffer_ == 0 || !screen_) return;
499
500         // Check for inset locking
501         if (bv_->theLockingInset()) {
502                 LyXCursor cursor = bv_->text->cursor;
503                 LyXFont font = bv_->text->GetFont(bv_->buffer(),
504                                                   cursor.par(), cursor.pos());
505                 int width = bv_->theLockingInset()->width(bv_, font);
506                 int inset_x = font.isVisibleRightToLeft()
507                         ? cursor.x() - width : cursor.x();
508                 int start_x = inset_x + bv_->theLockingInset()->scroll();
509                 bv_->theLockingInset()->
510                         InsetMotionNotify(bv_,
511                                           x - start_x,
512                                           y - cursor.y() + bv_->text->first,
513                                           state);
514                 return;
515         }
516    
517         /* The selection possible is needed, that only motion events are 
518          * used, where the bottom press event was on the drawing area too */
519         if (selection_possible) {
520                 screen_->HideCursor();
521
522                 bv_->text->SetCursorFromCoordinates(bv_, x, y + bv_->text->first);
523       
524                 if (!bv_->text->selection)
525                         update(BufferView::UPDATE); // Maybe an empty line was deleted
526       
527                 bv_->text->SetSelection();
528                 screen_->ToggleToggle(bv_->text, bv_);
529                 fitCursor(bv_->text);
530                 screen_->ShowCursor(bv_->text, bv_);
531         }
532         return;
533 }
534
535
536 // Single-click on work area
537 void BufferView::Pimpl::workAreaButtonPress(int xpos, int ypos,
538                                             unsigned int button)
539 {
540         last_click_x = -1;
541         last_click_y = -1;
542
543         if (buffer_ == 0 || !screen_) return;
544
545         Inset * inset_hit = checkInsetHit(bv_->text, xpos, ypos, button);
546
547         // ok ok, this is a hack.
548         if (button == 4 || button == 5) {
549                 switch (button) {
550                 case 4:
551                         scrollUp(lyxrc.wheel_jump); // default 100, set in lyxrc
552                         break;
553                 case 5:
554                         scrollDown(lyxrc.wheel_jump);
555                         break;
556                 }
557         }
558         
559         if (bv_->theLockingInset()) {
560                 // We are in inset locking mode
561                 
562                 /* Check whether the inset was hit. If not reset mode,
563                    otherwise give the event to the inset */
564                 if (inset_hit == bv_->theLockingInset()) {
565                         bv_->theLockingInset()->
566                                 InsetButtonPress(bv_,
567                                                  xpos, ypos,
568                                                  button);
569                         return;
570                 } else {
571                         bv_->unlockInset(bv_->theLockingInset());
572                 }
573         }
574         
575         if (!inset_hit)
576                 selection_possible = true;
577         screen_->HideCursor();
578
579         int const screen_first = bv_->text->first;
580         
581         // Middle button press pastes if we have a selection
582         bool paste_internally = false;
583         if (button == 2
584             && bv_->text->selection) {
585                 owner_->getLyXFunc()->Dispatch(LFUN_COPY);
586                 paste_internally = true;
587         }
588         
589         // Clear the selection
590         screen_->ToggleSelection(bv_->text, bv_);
591         bv_->text->ClearSelection();
592         bv_->text->FullRebreak(bv_);
593         screen_->Update(bv_->text, bv_);
594         updateScrollbar();
595         
596         // Single left click in math inset?
597         if ((inset_hit != 0) &&
598             (inset_hit->Editable()==Inset::HIGHLY_EDITABLE)) {
599                 // Highly editable inset, like math
600                 UpdatableInset * inset = static_cast<UpdatableInset *>(inset_hit);
601                 selection_possible = false;
602                 owner_->updateLayoutChoice();
603                 owner_->getMiniBuffer()->Set(inset->EditMessage());
604                 inset->InsetButtonPress(bv_, xpos, ypos, button);
605                 inset->Edit(bv_, xpos, ypos, button);
606                 return;
607         } 
608         
609         // Right click on a footnote flag opens float menu
610         if (button == 3) { 
611                 selection_possible = false;
612                 return;
613         }
614         
615         if (!inset_hit) // otherwise it was already set in checkInsetHit(...)
616                 bv_->text->SetCursorFromCoordinates(bv_, xpos, ypos + screen_first);
617         bv_->text->FinishUndo();
618         bv_->text->sel_cursor = bv_->text->cursor;
619         bv_->text->cursor.x_fix(bv_->text->cursor.x());
620         
621         owner_->updateLayoutChoice();
622         if (fitCursor(bv_->text)) {
623                 selection_possible = false;
624         }
625         
626         // Insert primary selection with middle mouse
627         // if there is a local selection in the current buffer,
628         // insert this
629         if (button == 2) {
630                 if (paste_internally)
631                         owner_->getLyXFunc()->Dispatch(LFUN_PASTE);
632                 else
633                         owner_->getLyXFunc()->Dispatch(LFUN_PASTESELECTION,
634                                                        "paragraph");
635                 selection_possible = false;
636                 return;
637         }
638 }
639
640
641 void BufferView::Pimpl::doubleClick(int /*x*/, int /*y*/, unsigned int button) 
642 {
643         // select a word
644         if (buffer_ && !bv_->theLockingInset()) {
645                 if (screen_ && button == 1) {
646                         screen_->HideCursor();
647                         screen_->ToggleSelection(bv_->text, bv_);
648                         bv_->text->SelectWord(bv_);
649                         screen_->ToggleSelection(bv_->text, bv_, false);
650                         /* This will fit the cursor on the screen
651                          * if necessary */
652                         update(BufferView::SELECT|BufferView::FITCUR);
653                 }
654         }   
655 }
656
657
658 void BufferView::Pimpl::tripleClick(int /*x*/, int /*y*/, unsigned int button)
659 {
660         // select a line
661         if (buffer_ && screen_ && !bv_->theLockingInset() && (button == 1)) {
662                 screen_->HideCursor();
663                 screen_->ToggleSelection(bv_->text, bv_);
664                 bv_->text->CursorHome(bv_);
665                 bv_->text->sel_cursor = bv_->text->cursor;
666                 bv_->text->CursorEnd(bv_);
667                 bv_->text->SetSelection();
668                 screen_->ToggleSelection(bv_->text, bv_, false);
669                 /* This will fit the cursor on the screen
670                  * if necessary */
671                 update(BufferView::SELECT|BufferView::FITCUR);
672         }
673 }
674
675
676 void BufferView::Pimpl::enterView()
677 {
678         if (active() && available()) {
679                 SetXtermCursor(workarea_->getWin());
680                 using_xterm_cursor = true;
681         }
682 }
683
684
685 void BufferView::Pimpl::leaveView()
686 {
687         if (using_xterm_cursor) {
688                 XUndefineCursor(fl_get_display(), workarea_->getWin());
689                 using_xterm_cursor = false;
690         }
691 }
692
693
694 void BufferView::Pimpl::workAreaButtonRelease(int x, int y,
695                                               unsigned int button)
696 {
697         if (buffer_ == 0 || screen_ == 0) return;
698
699         // If we hit an inset, we have the inset coordinates in these
700         // and inset_hit points to the inset.  If we do not hit an
701         // inset, inset_hit is 0, and inset_x == x, inset_y == y.
702         Inset * inset_hit = checkInsetHit(bv_->text, x, y, button);
703
704         if (bv_->theLockingInset()) {
705                 // We are in inset locking mode.
706
707                 /* LyX does a kind of work-area grabbing for insets.
708                    Only a ButtonPress Event outside the inset will 
709                    force a InsetUnlock. */
710                 bv_->theLockingInset()->
711                         InsetButtonRelease(bv_, x, y, button);
712                 return;
713         }
714         
715         selection_possible = false;
716         
717         if (button >= 2) return;
718
719         bv_->setState();
720         owner_->showState();
721
722         // Did we hit an editable inset?
723         if (inset_hit != 0) {
724                 // Inset like error, notes and figures
725                 selection_possible = false;
726
727                 // CHECK fix this proper in 0.13
728
729                 // Following a ref shouldn't issue
730                 // a push on the undo-stack
731                 // anylonger, now that we have
732                 // keybindings for following
733                 // references and returning from
734                 // references.  IMHO though, it
735                 // should be the inset's own business
736                 // to push or not push on the undo
737                 // stack. They don't *have* to
738                 // alter the document...
739                 // (Joacim)
740                 // ...or maybe the SetCursorParUndo()
741                 // below isn't necessary at all anylonger?
742                 if (inset_hit->LyxCode() == Inset::REF_CODE) {
743                         bv_->text->SetCursorParUndo(bv_->buffer());
744                 }
745
746                 owner_->getMiniBuffer()->Set(inset_hit->EditMessage());
747                 if (inset_hit->Editable()==Inset::HIGHLY_EDITABLE) {
748                         // Highly editable inset, like math
749                         UpdatableInset *inset = (UpdatableInset *)inset_hit;
750                         inset->InsetButtonRelease(bv_, x, y, button);
751                 } else {
752                         inset_hit->InsetButtonRelease(bv_, x, y, button);
753                         inset_hit->Edit(bv_, x, y, button);
754                 }
755                 return;
756         }
757
758         // check whether we want to open a float
759         if (bv_->text) {
760                 bool hit = false;
761                 char c = ' ';
762                 if (bv_->text->cursor.pos() <
763                     bv_->text->cursor.par()->Last()) {
764                         c = bv_->text->cursor.par()->
765                                 GetChar(bv_->text->cursor.pos());
766                 }
767 #ifndef NEW_INSETS
768                if(!bv_->text->selection)
769                 if (c == LyXParagraph::META_FOOTNOTE
770                     || c == LyXParagraph::META_MARGIN
771                     || c == LyXParagraph::META_FIG
772                     || c == LyXParagraph::META_TAB
773                     || c == LyXParagraph::META_WIDE_FIG
774                     || c == LyXParagraph::META_WIDE_TAB
775                     || c == LyXParagraph::META_ALGORITHM){
776                         hit = true;
777                 } else
778 #endif
779                         if (bv_->text->cursor.pos() - 1 >= 0) {
780                         c = bv_->text->cursor.par()->
781                                 GetChar(bv_->text->cursor.pos() - 1);
782 #ifndef NEW_INSETS
783                         if (c == LyXParagraph::META_FOOTNOTE
784                             || c == LyXParagraph::META_MARGIN
785                             || c == LyXParagraph::META_FIG
786                             || c == LyXParagraph::META_TAB
787                             || c == LyXParagraph::META_WIDE_FIG 
788                             || c == LyXParagraph::META_WIDE_TAB
789                             || c == LyXParagraph::META_ALGORITHM){
790                                 // We are one step too far to the right
791                                 bv_->text->CursorLeft(bv_);
792                                 hit = true;
793                         }
794 #endif
795                 }
796                 if (hit == true) {
797 #ifndef NEW_INSETS
798                         bv_->toggleFloat();
799 #endif
800                         selection_possible = false;
801                         return;
802                 }
803         }
804
805 #ifndef NEW_INSETS
806         // Do we want to close a float? (click on the float-label)
807         if (bv_->text->cursor.row()->par()->footnoteflag == 
808             LyXParagraph::OPEN_FOOTNOTE
809             && bv_->text->cursor.row()->previous() &&
810             bv_->text->cursor.row()->previous()->par()->
811             footnoteflag != LyXParagraph::OPEN_FOOTNOTE){
812                 LyXFont font(LyXFont::ALL_SANE);
813                 font.setSize(LyXFont::SIZE_FOOTNOTE);
814
815                 int box_x = 20; // LYX_PAPER_MARGIN;
816                 box_x += lyxfont::width(" wide-tab ", font);
817
818                 unsigned int screen_first = bv_->text->first;
819
820                 if (x < box_x
821                     && y + screen_first > bv_->text->cursor.y() -
822                     bv_->text->cursor.row()->baseline()
823                     && y + screen_first < bv_->text->cursor.y() -
824                     bv_->text->cursor.row()->baseline()
825                     + lyxfont::maxAscent(font) * 1.2 + lyxfont::maxDescent(font) * 1.2) {
826                         bv_->toggleFloat();
827                         selection_possible = false;
828                         return;
829                 }
830         }
831 #endif
832
833         // Maybe we want to edit a bibitem ale970302
834         if (bv_->text->cursor.par()->bibkey && x < 20 + 
835             bibitemMaxWidth(bv_, textclasslist.
836                             TextClass(buffer_->
837                                       params.textclass).defaultfont())) {
838                 bv_->text->cursor.par()->bibkey->Edit(bv_, 0, 0, 0);
839         }
840
841         return;
842 }
843
844
845 /* 
846  * Returns an inset if inset was hit. 0 otherwise.
847  * If hit, the coordinates are changed relative to the inset. 
848  * Otherwise coordinates are not changed, and false is returned.
849  */
850 Inset * BufferView::Pimpl::checkInsetHit(LyXText * text, int & x, int & y,
851                                          unsigned int /* button */)
852 {
853         if (!screen_)
854                 return 0;
855   
856         int y_tmp = y + text->first;
857   
858         LyXCursor cursor;
859         text->SetCursorFromCoordinates(bv_, cursor, x, y_tmp);
860
861         if (cursor.pos() < cursor.par()->Last()
862             && cursor.par()->GetChar(cursor.pos()) == LyXParagraph::META_INSET
863             && cursor.par()->GetInset(cursor.pos())
864             && cursor.par()->GetInset(cursor.pos())->Editable()) {
865
866                 // Check whether the inset really was hit
867                 Inset * tmpinset = cursor.par()->GetInset(cursor.pos());
868                 LyXFont font = text->GetFont(bv_->buffer(),
869                                                   cursor.par(), cursor.pos());
870                 int width = tmpinset->width(bv_, font);
871                 int inset_x = font.isVisibleRightToLeft()
872                         ? cursor.x() - width : cursor.x();
873                 int start_x = inset_x + tmpinset->scroll();
874                 int end_x = inset_x + width;
875
876                 if (x > start_x && x < end_x
877                     && y_tmp > cursor.y() - tmpinset->ascent(bv_, font)
878                     && y_tmp < cursor.y() + tmpinset->descent(bv_, font)) {
879                         text->SetCursor(bv_, cursor.par(),cursor.pos(),true);
880                         x = x - start_x;
881                         // The origin of an inset is on the baseline
882                         y = y_tmp - (text->cursor.y()); 
883                         return tmpinset;
884                 }
885         }
886
887         if ((cursor.pos() - 1 >= 0) &&
888             (cursor.par()->GetChar(cursor.pos()-1) == LyXParagraph::META_INSET) &&
889             (cursor.par()->GetInset(cursor.pos() - 1)) &&
890             (cursor.par()->GetInset(cursor.pos() - 1)->Editable())) {
891                 Inset * tmpinset = cursor.par()->GetInset(cursor.pos()-1);
892                 LyXFont font = text->GetFont(bv_->buffer(), cursor.par(),
893                                                   cursor.pos()-1);
894                 int width = tmpinset->width(bv_, font);
895                 int inset_x = font.isVisibleRightToLeft()
896                         ? cursor.x() : cursor.x() - width;
897                 int start_x = inset_x + tmpinset->scroll();
898                 int end_x = inset_x + width;
899
900                 if (x > start_x && x < end_x
901                     && y_tmp > cursor.y() - tmpinset->ascent(bv_, font)
902                     && y_tmp < cursor.y() + tmpinset->descent(bv_, font)) {
903 #if 0
904                         if (move_cursor && (tmpinset != bv_->theLockingInset()))
905 #endif
906                                 text->SetCursor(bv_, cursor.par(),cursor.pos()-1,true);
907                         x = x - start_x;
908                         // The origin of an inset is on the baseline
909                         y = y_tmp - (text->cursor.y()); 
910                         return tmpinset;
911                 }
912         }
913         return 0;
914 }
915
916
917 void BufferView::Pimpl::workAreaExpose()
918 {
919         // this is a hack to ensure that we only call this through
920         // BufferView::redraw().
921         //if (!lgb_hack) {
922         //      redraw();
923         //}
924         
925         static int work_area_width = 0;
926         static unsigned int work_area_height = 0;
927
928         bool widthChange = workarea_->workWidth() != work_area_width;
929         bool heightChange = workarea_->height() != work_area_height;
930
931         // update from work area
932         work_area_width = workarea_->workWidth();
933         work_area_height = workarea_->height();
934         if (buffer_ != 0) {
935                 if (widthChange) {
936                         // All buffers need a resize
937                         bufferlist.resize();
938
939                         // Remove all texts from the textcache
940                         // This is not _really_ what we want to do. What
941                         // we really want to do is to delete in textcache
942                         // that does not have a BufferView with matching
943                         // width, but as long as we have only one BufferView
944                         // deleting all gives the same result.
945                         if (lyxerr.debugging())
946                                 textcache.show(lyxerr, "Expose delete all");
947                         textcache.clear();
948                 } else if (heightChange) {
949                         // Rebuild image of current screen
950                         updateScreen();
951                         // fitCursor() ensures we don't jump back
952                         // to the start of the document on vertical
953                         // resize
954                         fitCursor(bv_->text);
955
956                         // The main window size has changed, repaint most stuff
957                         redraw();
958                         // ...including the minibuffer
959                         owner_->getMiniBuffer()->Init();
960
961                 } else if (screen_)
962                     screen_->Redraw(bv_->text, bv_);
963         } else {
964                 // Grey box when we don't have a buffer
965                 workarea_->greyOut();
966         }
967
968         // always make sure that the scrollbar is sane.
969         updateScrollbar();
970         owner_->updateLayoutChoice();
971         return;
972 }
973
974
975 void BufferView::Pimpl::update()
976 {
977         if (screen_) screen_->Update(bv_->text, bv_);
978 }
979
980 // Values used when calling update:
981 // -3 - update
982 // -2 - update, move sel_cursor if selection, fitcursor
983 // -1 - update, move sel_cursor if selection, fitcursor, mark dirty
984 //  0 - update, move sel_cursor if selection, fitcursor 
985 //  1 - update, move sel_cursor if selection, fitcursor, mark dirty
986 //  3 - update, move sel_cursor if selection
987 //
988 // update -
989 // a simple redraw of the parts that need refresh
990 //
991 // move sel_cursor if selection -
992 // the text's sel_cursor is moved if there is selection is progress
993 //
994 // fitcursor -
995 // fitCursor() is called and the scrollbar updated
996 //
997 // mark dirty -
998 // the buffer is marked dirty.
999 //
1000 // enum {
1001 //       UPDATE = 0,
1002 //       SELECT = 1,
1003 //       FITCUR = 2,
1004 //       CHANGE = 4 
1005 // };
1006 //
1007 // UPDATE_ONLY = UPDATE;
1008 // UPDATE_SELECT = UPDATE | SELECT;
1009 // UPDATE_SELECT_MOVE = UPDATE | SELECT | FITCUR;
1010 // UPDATE_SELECT_MOVE_AFTER_CHANGE = UPDATE | SELECT | FITCUR | CHANGE;
1011 //
1012 // update(-3) -> update(0)         -> update(0) -> update(UPDATE)
1013 // update(-2) -> update(1 + 2)     -> update(3) -> update(SELECT|FITCUR)
1014 // update(-1) -> update(1 + 2 + 4) -> update(7) -> update(SELECT|FITCUR|CHANGE)
1015 // update(1)  -> update(1 + 2 + 4) -> update(7) -> update(SELECT|FITCUR|CHANGE)
1016 // update(3)  -> update(1)         -> update(1) -> update(SELECT)
1017
1018 void BufferView::Pimpl::update(BufferView::UpdateCodes f)
1019 {
1020         owner_->updateLayoutChoice();
1021
1022         if (!bv_->text->selection && (f & SELECT)) {
1023                 bv_->text->sel_cursor = bv_->text->cursor;
1024         }
1025
1026         bv_->text->FullRebreak(bv_);
1027
1028         update();
1029
1030         if ((f & FITCUR)) {
1031                 fitCursor(bv_->text);
1032         }
1033
1034         if ((f & CHANGE)) {
1035                 if (buffer_->isLyxClean()) {
1036                         buffer_->markDirty();
1037                         owner_->getMiniBuffer()->setTimer(4);
1038                 } else {
1039                         buffer_->markDirty();
1040                 }
1041         }
1042 }
1043
1044
1045 // Callback for cursor timer
1046 void BufferView::Pimpl::cursorToggle()
1047 {
1048         // Quite a nice place for asyncron Inset updating, isn't it?
1049         // Actually no! This is run even if no buffer exist... so (Lgb)
1050         if (!buffer_) {
1051                 goto set_timer_and_return;
1052         }
1053
1054         // NOTE:
1055         // On my quest to solve the gs render hangups I am now
1056         // disabling the SIGHUP completely, and will do a wait
1057         // now and then instead. If the guess that xforms somehow
1058         // destroys something is true, this is likely (hopefully)
1059         // to solve the problem...at least I hope so. Lgb
1060
1061         // ...Ok this seems to work...at least it does not make things
1062         // worse so far. However I still see gs processes that hangs.
1063         // I would really like to know _why_ they are hanging. Anyway
1064         // the solution without the SIGCHLD handler seems to be easier
1065         // to debug.
1066
1067         // When attaching gdb to a a running gs that hangs it shows
1068         // that it is waiting for input(?) Is it possible for us to
1069         // provide that input somehow? Or figure what it is expecing
1070         // to read?
1071
1072         // One solution is to, after some time, look if there are some
1073         // old gs processes still running and if there are: kill them
1074         // and re render.
1075
1076         // Another solution is to provide the user an option to rerender
1077         // a picture. This would, for the picture in question, check if
1078         // there is a gs running for it, if so kill it, and start a new
1079         // rendering process.
1080
1081         // these comments posted to lyx@via
1082         {
1083                 int status = 1;
1084                 int pid = waitpid(static_cast<pid_t>(0), &status, WNOHANG);
1085                 if (pid == -1) // error find out what is wrong
1086                         ; // ignore it for now.
1087                 else if (pid > 0)
1088                         sigchldhandler(pid, &status);
1089         }
1090
1091         updatelist.update(bv_);
1092         
1093         if (!screen_) {
1094                 goto set_timer_and_return;
1095         }
1096
1097         if (!bv_->theLockingInset()) {
1098                 screen_->CursorToggle(bv_->text, bv_);
1099         } else {
1100                 bv_->theLockingInset()->ToggleInsetCursor(bv_);
1101         }
1102         
1103   set_timer_and_return:
1104         cursor_timeout.restart();
1105         return;
1106 }
1107
1108
1109 void BufferView::Pimpl::cursorPrevious(LyXText * text)
1110 {
1111         if (!text->cursor.row()->previous())
1112                 return;
1113         
1114         int y = text->first;
1115         if (text->inset_owner)
1116                 y += bv_->text->first;
1117         Row * cursorrow = text->cursor.row();
1118         text->SetCursorFromCoordinates(bv_, bv_->text->cursor.x_fix(), y);
1119         bv_->text->FinishUndo();
1120         // This is to allow jumping over large insets
1121         if ((cursorrow == text->cursor.row()))
1122                 text->CursorUp(bv_);
1123         
1124         if (text->inset_owner ||
1125             text->cursor.row()->height() < workarea_->height())
1126                 screen_->Draw(bv_->text, bv_,
1127                               text->cursor.y()
1128                               - text->cursor.row()->baseline()
1129                               + text->cursor.row()->height()
1130                               - workarea_->height() + 1 );
1131         updateScrollbar();
1132 }
1133
1134
1135 void BufferView::Pimpl::cursorNext(LyXText * text)
1136 {
1137         if (!text->cursor.row()->next())
1138                 return;
1139         
1140         int y = text->first + workarea_->height();
1141 //      if (text->inset_owner)
1142 //              y += bv_->text->first;
1143         text->GetRowNearY(y);
1144     
1145         Row * cursorrow = text->cursor.row();
1146         text->SetCursorFromCoordinates(bv_, text->cursor.x_fix(), y); // + workarea_->height());
1147         bv_->text->FinishUndo();
1148         // This is to allow jumping over large insets
1149         if ((cursorrow == bv_->text->cursor.row()))
1150                 text->CursorDown(bv_);
1151         
1152         if (text->inset_owner ||
1153             text->cursor.row()->height() < workarea_->height())
1154                 screen_->Draw(bv_->text, bv_, text->cursor.y() -
1155                               text->cursor.row()->baseline());
1156         updateScrollbar();
1157 }
1158
1159
1160 bool BufferView::Pimpl::available() const
1161 {
1162         if (buffer_ && bv_->text) return true;
1163         return false;
1164 }
1165
1166
1167 void BufferView::Pimpl::beforeChange()
1168 {
1169         toggleSelection();
1170         bv_->text->ClearSelection();
1171
1172         // CHECK
1173         //owner_->update_timeout.stop();
1174 }
1175
1176
1177 void BufferView::Pimpl::savePosition(unsigned int i)
1178 {
1179         if (i >= saved_positions_num)
1180                 return;
1181         saved_positions[i] = Position(buffer_->fileName(),
1182                                       bv_->text->cursor.par()->id(),
1183                                       bv_->text->cursor.pos());
1184 }
1185
1186
1187 void BufferView::Pimpl::restorePosition(unsigned int i)
1188 {
1189         if (i >= saved_positions_num)
1190                 return;
1191
1192
1193         string fname = saved_positions[i].filename;
1194
1195         beforeChange();
1196
1197         if (fname != buffer_->fileName()) {
1198                 Buffer * b = bufferlist.exists(fname) ?
1199                         bufferlist.getBuffer(fname) :
1200                         bufferlist.loadLyXFile(fname); // don't ask, just load it
1201                 if (b != 0 ) buffer(b);
1202         }
1203
1204         LyXParagraph * par = bv_->text->GetParFromID(saved_positions[i].par_id);
1205         if (!par)
1206                 return;
1207
1208         bv_->text->SetCursor(bv_, par,
1209                              min(par->Last(), saved_positions[i].par_pos));
1210         update(BufferView::SELECT|BufferView::FITCUR);
1211 }
1212
1213
1214 bool BufferView::Pimpl::isSavedPosition(unsigned int i)
1215 {
1216         if (i >= saved_positions_num)
1217                 return false;
1218
1219         return !saved_positions[i].filename.empty();
1220 }
1221
1222
1223 void BufferView::Pimpl::setState()
1224 {
1225         if (!lyxrc.rtl_support)
1226                 return;
1227
1228         LyXText * text = bv_->getLyXText();
1229         if (text->real_current_font.isRightToLeft() &&
1230             text->real_current_font.latex() != LyXFont::ON) {
1231                 if (owner_->getIntl()->primarykeymap)
1232                         owner_->getIntl()->KeyMapSec();
1233         } else {
1234                 if (!owner_->getIntl()->primarykeymap)
1235                         owner_->getIntl()->KeyMapPrim();
1236         }
1237 }
1238
1239
1240 void BufferView::Pimpl::insetSleep()
1241 {
1242         if (bv_->theLockingInset() && !bv_->inset_slept) {
1243                 bv_->theLockingInset()->GetCursorPos(bv_, bv_->slx, bv_->sly);
1244                 bv_->theLockingInset()->InsetUnlock(bv_);
1245                 bv_->inset_slept = true;
1246         }
1247 }
1248
1249
1250 void BufferView::Pimpl::insetWakeup()
1251 {
1252         if (bv_->theLockingInset() && bv_->inset_slept) {
1253                 bv_->theLockingInset()->Edit(bv_, bv_->slx, bv_->sly, 0);
1254                 bv_->inset_slept = false;
1255         }
1256 }
1257
1258
1259 void BufferView::Pimpl::insetUnlock()
1260 {
1261         if (bv_->theLockingInset()) {
1262                 if (!bv_->inset_slept)
1263                         bv_->theLockingInset()->InsetUnlock(bv_);
1264                 bv_->theLockingInset(0);
1265                 bv_->text->FinishUndo();
1266                 bv_->inset_slept = false;
1267         }
1268 }
1269
1270
1271 bool BufferView::Pimpl::focus() const
1272 {
1273         return workarea_->hasFocus();
1274 }
1275
1276
1277 void BufferView::Pimpl::focus(bool f)
1278 {
1279         if (f) workarea_->setFocus();
1280 }
1281
1282
1283 bool BufferView::Pimpl::active() const
1284 {
1285         return workarea_->active();
1286 }
1287
1288
1289 bool BufferView::Pimpl::belowMouse() const 
1290 {
1291         return workarea_->belowMouse();
1292 }
1293
1294
1295 void BufferView::Pimpl::showCursor()
1296 {
1297         if (screen_)
1298                 screen_->ShowCursor(bv_->text, bv_);
1299 }
1300
1301
1302 void BufferView::Pimpl::hideCursor()
1303 {
1304         if (screen_)
1305                 screen_->HideCursor();
1306 }
1307
1308
1309 void BufferView::Pimpl::toggleSelection(bool b)
1310 {
1311         if (screen_)
1312                 screen_->ToggleSelection(bv_->text, bv_, b);
1313 }
1314
1315
1316 void BufferView::Pimpl::toggleToggle()
1317 {
1318         if (screen_)
1319                 screen_->ToggleToggle(bv_->text, bv_);
1320 }
1321
1322
1323 void BufferView::Pimpl::center() 
1324 {
1325         beforeChange();
1326         if (bv_->text->cursor.y() > static_cast<int>((workarea_->height() / 2))) {
1327                 screen_->Draw(bv_->text, bv_, bv_->text->cursor.y() - workarea_->height() / 2);
1328         } else {
1329                 screen_->Draw(bv_->text, bv_, 0);
1330         }
1331         update(BufferView::SELECT|BufferView::FITCUR);
1332         redraw();
1333 }
1334
1335
1336 void BufferView::Pimpl::pasteClipboard(bool asPara) 
1337 {
1338         if (buffer_ == 0) return;
1339
1340         screen_->HideCursor();
1341         bv_->beforeChange();
1342         
1343         string const clip(workarea_->getClipboard());
1344         
1345         if (clip.empty()) return;
1346
1347         if (asPara) {
1348                 bv_->text->InsertStringB(bv_, clip);
1349         } else {
1350                 bv_->text->InsertStringA(bv_, clip);
1351         }
1352         update(BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
1353 }
1354
1355
1356 void BufferView::Pimpl::stuffClipboard(string const & stuff) const
1357 {
1358         workarea_->putClipboard(stuff);
1359 }