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