10 #include "BufferView_pimpl.h"
12 #include "lyxscreen.h"
16 #include "commandtags.h"
18 #include "minibuffer.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"
27 #include "support/LAssert.h"
28 #include "frontends/Dialogs.h"
30 #ifdef SIGC_CXX_NAMESPACES
39 /* the selection possible is needed, that only motion events are
40 * used, where the bottom press event was on the drawing area too */
41 bool selection_possible = false;
43 extern BufferList bufferlist;
44 extern char ascii_type;
46 extern "C" void TimerCB(FL_OBJECT *, long);
47 extern void sigchldhandler(pid_t pid, int * status);
48 extern int bibitemMaxWidth(BufferView *, LyXFont const &);
54 XSync(fl_get_display(), 0);
59 void SetXtermCursor(Window win)
62 static bool cursor_undefined = true;
63 if (cursor_undefined){
64 cursor = XCreateFontCursor(fl_get_display(), XC_xterm);
65 XFlush(fl_get_display());
66 cursor_undefined = false;
68 XDefineCursor(fl_get_display(), win, cursor);
69 XFlush(fl_get_display());
73 BufferView::Pimpl::Pimpl(BufferView * b, LyXView * o,
74 int xpos, int ypos, int width, int height)
75 : bv_(b), owner_(o), cursor_timeout(400)
79 workarea_ = new WorkArea(bv_, xpos, ypos, width, height);
81 workarea_ = new WorkArea(xpos, ypos, width, height);
84 workarea_->scrollCB.connect(slot(this, &BufferView::Pimpl::scrollCB));
85 workarea_->workAreaExpose
86 .connect(slot(this, &BufferView::Pimpl::workAreaExpose));
87 workarea_->workAreaEnter
88 .connect(slot(this, &BufferView::Pimpl::enterView));
89 workarea_->workAreaLeave
90 .connect(slot(this, &BufferView::Pimpl::leaveView));
91 workarea_->workAreaButtonPress
92 .connect(slot(this, &BufferView::Pimpl::workAreaButtonPress));
93 workarea_->workAreaButtonRelease
95 &BufferView::Pimpl::workAreaButtonRelease));
96 workarea_->workAreaMotionNotify
97 .connect(slot(this, &BufferView::Pimpl::workAreaMotionNotify));
98 workarea_->workAreaDoubleClick
99 .connect(slot(this, &BufferView::Pimpl::doubleClick));
100 workarea_->workAreaTripleClick
101 .connect(slot(this, &BufferView::Pimpl::tripleClick));
102 workarea_->workAreaKeyPress
103 .connect(slot(this, &BufferView::Pimpl::workAreaKeyPress));
107 cursor_timeout.timeout.connect(slot(this,
108 &BufferView::Pimpl::cursorToggle));
109 current_scrollbar_value = 0;
110 cursor_timeout.start();
111 workarea_->setFocus();
112 using_xterm_cursor = false;
116 Painter & BufferView::Pimpl::painter()
118 return workarea_->getPainter();
122 void BufferView::Pimpl::buffer(Buffer * b)
124 lyxerr[Debug::INFO] << "Setting buffer in BufferView ("
128 buffer_->delUser(bv_);
130 // Put the old text into the TextCache, but
131 // only if the buffer is still loaded.
132 // Also set the owner of the test to 0
133 // bv_->text->owner(0);
134 textcache.add(buffer_, workarea_->workWidth(), bv_->text);
135 if (lyxerr.debugging())
136 textcache.show(lyxerr, "BufferView::buffer");
141 // Set current buffer
144 if (bufferlist.getState() == BufferList::CLOSING) return;
147 // screen is always deleted when the buffer is changed.
151 // If we are closing the buffer, use the first buffer as current
153 buffer_ = bufferlist.first();
157 lyxerr[Debug::INFO] << "Buffer addr: " << buffer_ << endl;
158 buffer_->addUser(bv_);
159 // If we don't have a text object for this, we make one
160 if (bv_->text == 0) {
161 resizeCurrentBuffer();
166 bv_->text->first = screen_->TopCursorVisible(bv_->text);
167 owner_->updateMenubar();
168 owner_->updateToolbar();
169 // Similarly, buffer-dependent dialogs should be updated or
170 // hidden. This should go here because some dialogs (eg ToC)
171 // require bv_->text.
172 owner_->getDialogs()->updateBufferDependent(true);
176 lyxerr[Debug::INFO] << " No Buffer!" << endl;
177 owner_->updateMenubar();
178 owner_->updateToolbar();
179 owner_->getDialogs()->hideBufferDependent();
183 // Also remove all remaining text's from the testcache.
184 // (there should not be any!) (if there is any it is a
186 if (lyxerr.debugging())
187 textcache.show(lyxerr, "buffer delete all");
190 // should update layoutchoice even if we don't have a buffer.
191 owner_->updateLayoutChoice();
192 owner_->getMiniBuffer()->Init();
193 owner_->updateWindowTitle();
197 void BufferView::Pimpl::resize(int xpos, int ypos, int width, int height)
199 workarea_->resize(xpos, ypos, width, height);
205 void BufferView::Pimpl::resize()
207 // This will resize the buffer. (Asger)
209 resizeCurrentBuffer();
213 void BufferView::Pimpl::redraw()
215 lyxerr[Debug::INFO] << "BufferView::redraw()" << endl;
220 bool BufferView::Pimpl::fitCursor(LyXText * text)
222 Assert(screen_); // it is a programming error to call fitCursor
223 // without a valid screen.
224 bool ret = screen_->FitCursor(text);
231 void BufferView::Pimpl::redoCurrentBuffer()
233 lyxerr[Debug::INFO] << "BufferView::redoCurrentBuffer" << endl;
234 if (buffer_ && bv_->text) {
236 owner_->updateLayoutChoice();
241 int BufferView::Pimpl::resizeCurrentBuffer()
243 lyxerr[Debug::INFO] << "resizeCurrentBuffer" << endl;
245 LyXParagraph * par = 0;
246 LyXParagraph * selstartpar = 0;
247 LyXParagraph * selendpar = 0;
256 owner_->getMiniBuffer()->Set(_("Formatting document..."));
259 par = bv_->text->cursor.par();
260 pos = bv_->text->cursor.pos();
261 selstartpar = bv_->text->sel_start_cursor.par();
262 selstartpos = bv_->text->sel_start_cursor.pos();
263 selendpar = bv_->text->sel_end_cursor.par();
264 selendpos = bv_->text->sel_end_cursor.pos();
265 selection = bv_->text->selection;
266 mark_set = bv_->text->mark_set;
268 bv_->text = new LyXText(bv_);
270 // See if we have a text in TextCache that fits
271 // the new buffer_ with the correct width.
272 bv_->text = textcache.findFit(buffer_, workarea_->workWidth());
274 if (lyxerr.debugging()) {
275 lyxerr << "Found a LyXText that fits:\n";
276 textcache.show(lyxerr, make_pair(buffer_, make_pair(workarea_->workWidth(), bv_->text)));
278 // Set the owner of the newly found text
279 // bv_->text->owner(bv_);
280 if (lyxerr.debugging())
281 textcache.show(lyxerr, "resizeCurrentBuffer");
283 bv_->text = new LyXText(bv_);
289 bv_->text->selection = true;
290 /* at this point just to avoid the Delete-Empty-Paragraph
291 * Mechanism when setting the cursor */
292 bv_->text->mark_set = mark_set;
294 bv_->text->SetCursor(bv_, selstartpar, selstartpos);
295 bv_->text->sel_cursor = bv_->text->cursor;
296 bv_->text->SetCursor(bv_, selendpar, selendpos);
297 bv_->text->SetSelection();
298 bv_->text->SetCursor(bv_, par, pos);
300 bv_->text->SetCursor(bv_, par, pos);
301 bv_->text->sel_cursor = bv_->text->cursor;
302 bv_->text->selection = false;
305 bv_->text->first = screen_->TopCursorVisible(bv_->text);
306 buffer_->resizeInsets(bv_);
307 // this will scroll the screen such that the cursor becomes visible
310 owner_->getMiniBuffer()->Init();
314 // Now if the title form still exist kill it
321 void BufferView::Pimpl::gotoError()
326 screen_->HideCursor();
328 update(BufferView::SELECT|BufferView::FITCUR);
331 if (!bv_->text->GotoNextError(bv_)) {
332 if (bv_->text->cursor.pos()
333 || bv_->text->cursor.par() != bv_->text->FirstParagraph()) {
334 tmp = bv_->text->cursor;
335 bv_->text->cursor.par(bv_->text->FirstParagraph());
336 bv_->text->cursor.pos(0);
337 if (!bv_->text->GotoNextError(bv_)) {
338 bv_->text->cursor = tmp;
339 owner_->getMiniBuffer()
340 ->Set(_("No more errors"));
344 owner_->getMiniBuffer()->Set(_("No more errors"));
348 update(BufferView::SELECT|BufferView::FITCUR);
349 bv_->text->sel_cursor = bv_->text->cursor;
353 void BufferView::Pimpl::updateScreen()
355 // Regenerate the screen.
357 screen_ = new LyXScreen(*workarea_);
361 void BufferView::Pimpl::updateScrollbar()
363 /* If the text is smaller than the working area, the scrollbar
364 * maximum must be the working area height. No scrolling will
368 workarea_->setScrollbar(0, 1.0);
372 static unsigned long max2 = 0;
373 static unsigned long height2 = 0;
375 unsigned long cbth = 0;
379 cbth = bv_->text->height;
380 cbsf = bv_->text->first;
383 // check if anything has changed.
385 height2 == workarea_->height() &&
386 current_scrollbar_value == cbsf)
389 height2 = workarea_->height();
390 current_scrollbar_value = cbsf;
392 if (cbth <= height2) { // text is smaller than screen
393 workarea_->setScrollbar(0, 1.0); // right?
397 long maximum_height = workarea_->height() * 3 / 4 + cbth;
401 double hfloat = workarea_->height();
402 double maxfloat = maximum_height;
404 float slider_size = 0.0;
405 int slider_value = value;
407 workarea_->setScrollbarBounds(0, bv_->text->height - workarea_->height());
408 double lineh = bv_->text->DefaultHeight();
409 workarea_->setScrollbarIncrements(lineh);
410 if (maxfloat > 0.0) {
411 if ((hfloat / maxfloat) * float(height2) < 3)
412 slider_size = 3.0/float(height2);
414 slider_size = hfloat / maxfloat;
416 slider_size = hfloat;
418 workarea_->setScrollbar(slider_value, slider_size / workarea_->height());
422 // Callback for scrollbar slider
423 void BufferView::Pimpl::scrollCB(double value)
425 if (buffer_ == 0) return;
427 current_scrollbar_value = long(value);
428 if (current_scrollbar_value < 0)
429 current_scrollbar_value = 0;
434 screen_->Draw(bv_->text, current_scrollbar_value);
436 if (lyxrc.cursor_follows_scrollbar) {
437 LyXText * vbt = bv_->text;
438 int height = vbt->DefaultHeight();
440 if (vbt->cursor.y() < static_cast<int>((bv_->text->first + height))) {
441 vbt->SetCursorFromCoordinates(bv_, 0,
444 } else if (vbt->cursor.y() >
445 static_cast<int>((bv_->text->first+workarea_->height()-height)))
447 vbt->SetCursorFromCoordinates(bv_, 0,
449 workarea_->height() -
457 int BufferView::Pimpl::scrollUp(long time)
459 if (buffer_ == 0) return 0;
460 if (!screen_) return 0;
462 double value = workarea_->getScrollbarValue();
464 if (value == 0) return 0;
466 float add_value = (bv_->text->DefaultHeight()
467 + float(time) * float(time) * 0.125);
469 if (add_value > workarea_->height())
470 add_value = float(workarea_->height() -
471 bv_->text->DefaultHeight());
478 workarea_->setScrollbarValue(value);
485 int BufferView::Pimpl::scrollDown(long time)
487 if (buffer_ == 0) return 0;
488 if (!screen_) return 0;
490 double value= workarea_->getScrollbarValue();
491 pair<float, float> p = workarea_->getScrollbarBounds();
492 double max = p.second;
494 if (value == max) return 0;
496 float add_value = (bv_->text->DefaultHeight()
497 + float(time) * float(time) * 0.125);
499 if (add_value > workarea_->height())
500 add_value = float(workarea_->height() -
501 bv_->text->DefaultHeight());
508 workarea_->setScrollbarValue(value);
515 void BufferView::Pimpl::workAreaKeyPress(KeySym keysym, unsigned int state)
517 bv_->owner()->getLyXFunc()->processKeySym(keysym, state);
521 void BufferView::Pimpl::workAreaMotionNotify(int x, int y, unsigned int state)
523 // Only use motion with button 1
524 if (!(state & Button1MotionMask))
527 if (buffer_ == 0 || !screen_) return;
529 // Check for inset locking
530 if (bv_->theLockingInset()) {
531 LyXCursor cursor = bv_->text->cursor;
532 LyXFont font = bv_->text->GetFont(bv_->buffer(),
533 cursor.par(), cursor.pos());
534 int width = bv_->theLockingInset()->width(bv_, font);
535 int inset_x = font.isVisibleRightToLeft()
536 ? cursor.x() - width : cursor.x();
537 int start_x = inset_x + bv_->theLockingInset()->scroll();
538 bv_->theLockingInset()->
539 InsetMotionNotify(bv_,
541 y - cursor.y() + bv_->text->first,
546 /* The selection possible is needed, that only motion events are
547 * used, where the bottom press event was on the drawing area too */
548 if (selection_possible) {
549 screen_->HideCursor();
551 bv_->text->SetCursorFromCoordinates(bv_, x, y + bv_->text->first);
553 if (!bv_->text->selection)
554 update(BufferView::UPDATE); // Maybe an empty line was deleted
556 bv_->text->SetSelection();
557 screen_->ToggleToggle(bv_->text);
558 fitCursor(bv_->text);
559 screen_->ShowCursor(bv_->text);
565 // Single-click on work area
566 void BufferView::Pimpl::workAreaButtonPress(int xpos, int ypos,
572 if (buffer_ == 0 || !screen_) return;
574 Inset * inset_hit = checkInsetHit(bv_->text, xpos, ypos, button);
576 // ok ok, this is a hack.
577 if (button == 4 || button == 5) {
580 scrollUp(lyxrc.wheel_jump); // default 100, set in lyxrc
583 scrollDown(lyxrc.wheel_jump);
588 if (bv_->theLockingInset()) {
589 // We are in inset locking mode
591 /* Check whether the inset was hit. If not reset mode,
592 otherwise give the event to the inset */
593 if (inset_hit == bv_->theLockingInset()) {
594 bv_->theLockingInset()->
595 InsetButtonPress(bv_,
600 bv_->unlockInset(bv_->theLockingInset());
605 selection_possible = true;
606 screen_->HideCursor();
608 int const screen_first = bv_->text->first;
610 // Middle button press pastes if we have a selection
611 bool paste_internally = false;
613 && bv_->text->selection) {
614 owner_->getLyXFunc()->Dispatch(LFUN_COPY);
615 paste_internally = true;
618 // Clear the selection
619 screen_->ToggleSelection(bv_->text);
620 bv_->text->ClearSelection();
621 bv_->text->FullRebreak(bv_);
622 screen_->Update(bv_->text);
625 // Single left click in math inset?
626 if ((inset_hit != 0) &&
627 (inset_hit->Editable()==Inset::HIGHLY_EDITABLE)) {
628 // Highly editable inset, like math
629 UpdatableInset * inset = static_cast<UpdatableInset *>(inset_hit);
630 selection_possible = false;
631 owner_->updateLayoutChoice();
632 owner_->getMiniBuffer()->Set(inset->EditMessage());
633 inset->InsetButtonPress(bv_, xpos, ypos, button);
634 inset->Edit(bv_, xpos, ypos, button);
638 // Right click on a footnote flag opens float menu
640 selection_possible = false;
644 if (!inset_hit) // otherwise it was already set in checkInsetHit(...)
645 bv_->text->SetCursorFromCoordinates(bv_, xpos, ypos + screen_first);
646 bv_->text->FinishUndo();
647 bv_->text->sel_cursor = bv_->text->cursor;
648 bv_->text->cursor.x_fix(bv_->text->cursor.x());
650 owner_->updateLayoutChoice();
651 if (fitCursor(bv_->text)) {
652 selection_possible = false;
655 // Insert primary selection with middle mouse
656 // if there is a local selection in the current buffer,
659 if (paste_internally)
660 owner_->getLyXFunc()->Dispatch(LFUN_PASTE);
662 owner_->getLyXFunc()->Dispatch(LFUN_PASTESELECTION,
664 selection_possible = false;
670 void BufferView::Pimpl::doubleClick(int /*x*/, int /*y*/, unsigned int button)
673 if (buffer_ && !bv_->theLockingInset()) {
674 if (screen_ && button == 1) {
675 screen_->HideCursor();
676 screen_->ToggleSelection(bv_->text);
677 bv_->text->SelectWord(bv_);
678 screen_->ToggleSelection(bv_->text, false);
679 /* This will fit the cursor on the screen
681 update(BufferView::SELECT|BufferView::FITCUR);
687 void BufferView::Pimpl::tripleClick(int /*x*/, int /*y*/, unsigned int button)
690 if (buffer_ && screen_ && !bv_->theLockingInset() && (button == 1)) {
691 screen_->HideCursor();
692 screen_->ToggleSelection(bv_->text);
693 bv_->text->CursorHome(bv_);
694 bv_->text->sel_cursor = bv_->text->cursor;
695 bv_->text->CursorEnd(bv_);
696 bv_->text->SetSelection();
697 screen_->ToggleSelection(bv_->text, false);
698 /* This will fit the cursor on the screen
700 update(BufferView::SELECT|BufferView::FITCUR);
705 void BufferView::Pimpl::enterView()
707 if (active() && available()) {
708 SetXtermCursor(workarea_->getWin());
709 using_xterm_cursor = true;
714 void BufferView::Pimpl::leaveView()
716 if (using_xterm_cursor) {
717 XUndefineCursor(fl_get_display(), workarea_->getWin());
718 using_xterm_cursor = false;
723 void BufferView::Pimpl::workAreaButtonRelease(int x, int y,
726 if (buffer_ == 0 || screen_ == 0) return;
728 // If we hit an inset, we have the inset coordinates in these
729 // and inset_hit points to the inset. If we do not hit an
730 // inset, inset_hit is 0, and inset_x == x, inset_y == y.
731 Inset * inset_hit = checkInsetHit(bv_->text, x, y, button);
733 if (bv_->theLockingInset()) {
734 // We are in inset locking mode.
736 /* LyX does a kind of work-area grabbing for insets.
737 Only a ButtonPress Event outside the inset will
738 force a InsetUnlock. */
739 bv_->theLockingInset()->
740 InsetButtonRelease(bv_, x, y, button);
744 selection_possible = false;
746 if (button >= 2) return;
751 // Did we hit an editable inset?
752 if (inset_hit != 0) {
753 // Inset like error, notes and figures
754 selection_possible = false;
756 // CHECK fix this proper in 0.13
758 // Following a ref shouldn't issue
759 // a push on the undo-stack
760 // anylonger, now that we have
761 // keybindings for following
762 // references and returning from
763 // references. IMHO though, it
764 // should be the inset's own business
765 // to push or not push on the undo
766 // stack. They don't *have* to
767 // alter the document...
769 // ...or maybe the SetCursorParUndo()
770 // below isn't necessary at all anylonger?
771 if (inset_hit->LyxCode() == Inset::REF_CODE) {
772 bv_->text->SetCursorParUndo(bv_->buffer());
775 owner_->getMiniBuffer()->Set(inset_hit->EditMessage());
776 if (inset_hit->Editable()==Inset::HIGHLY_EDITABLE) {
777 // Highly editable inset, like math
778 UpdatableInset *inset = (UpdatableInset *)inset_hit;
779 inset->InsetButtonRelease(bv_, x, y, button);
781 inset_hit->InsetButtonRelease(bv_, x, y, button);
782 inset_hit->Edit(bv_, x, y, button);
787 // check whether we want to open a float
791 if (bv_->text->cursor.pos() <
792 bv_->text->cursor.par()->Last()) {
793 c = bv_->text->cursor.par()->
794 GetChar(bv_->text->cursor.pos());
797 if(!bv_->text->selection)
798 if (c == LyXParagraph::META_FOOTNOTE
799 || c == LyXParagraph::META_MARGIN
800 || c == LyXParagraph::META_FIG
801 || c == LyXParagraph::META_TAB
802 || c == LyXParagraph::META_WIDE_FIG
803 || c == LyXParagraph::META_WIDE_TAB
804 || c == LyXParagraph::META_ALGORITHM){
808 if (bv_->text->cursor.pos() - 1 >= 0) {
809 c = bv_->text->cursor.par()->
810 GetChar(bv_->text->cursor.pos() - 1);
812 if (c == LyXParagraph::META_FOOTNOTE
813 || c == LyXParagraph::META_MARGIN
814 || c == LyXParagraph::META_FIG
815 || c == LyXParagraph::META_TAB
816 || c == LyXParagraph::META_WIDE_FIG
817 || c == LyXParagraph::META_WIDE_TAB
818 || c == LyXParagraph::META_ALGORITHM){
819 // We are one step too far to the right
820 bv_->text->CursorLeft(bv_);
829 selection_possible = false;
835 // Do we want to close a float? (click on the float-label)
836 if (bv_->text->cursor.row()->par()->footnoteflag ==
837 LyXParagraph::OPEN_FOOTNOTE
838 && bv_->text->cursor.row()->previous() &&
839 bv_->text->cursor.row()->previous()->par()->
840 footnoteflag != LyXParagraph::OPEN_FOOTNOTE){
841 LyXFont font(LyXFont::ALL_SANE);
842 font.setSize(LyXFont::SIZE_FOOTNOTE);
844 int box_x = 20; // LYX_PAPER_MARGIN;
845 box_x += lyxfont::width(" wide-tab ", font);
847 unsigned int screen_first = bv_->text->first;
850 && y + screen_first > bv_->text->cursor.y() -
851 bv_->text->cursor.row()->baseline()
852 && y + screen_first < bv_->text->cursor.y() -
853 bv_->text->cursor.row()->baseline()
854 + lyxfont::maxAscent(font) * 1.2 + lyxfont::maxDescent(font) * 1.2) {
856 selection_possible = false;
862 // Maybe we want to edit a bibitem ale970302
863 if (bv_->text->cursor.par()->bibkey && x < 20 +
864 bibitemMaxWidth(bv_, textclasslist.
866 params.textclass).defaultfont())) {
867 bv_->text->cursor.par()->bibkey->Edit(bv_, 0, 0, 0);
875 * Returns an inset if inset was hit. 0 otherwise.
876 * If hit, the coordinates are changed relative to the inset.
877 * Otherwise coordinates are not changed, and false is returned.
879 Inset * BufferView::Pimpl::checkInsetHit(LyXText * text, int & x, int & y,
880 unsigned int /* button */)
885 int y_tmp = y + text->first;
888 text->SetCursorFromCoordinates(bv_, cursor, x, y_tmp);
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()) {
895 // Check whether the inset really was hit
896 Inset * tmpinset = cursor.par()->GetInset(cursor.pos());
897 LyXFont font = text->GetFont(bv_->buffer(),
898 cursor.par(), cursor.pos());
899 int width = tmpinset->width(bv_, font);
900 int inset_x = font.isVisibleRightToLeft()
901 ? cursor.x() - width : cursor.x();
902 int start_x = inset_x + tmpinset->scroll();
903 int end_x = inset_x + width;
905 if (x > start_x && x < end_x
906 && y_tmp > cursor.y() - tmpinset->ascent(bv_, font)
907 && y_tmp < cursor.y() + tmpinset->descent(bv_, font)) {
908 text->SetCursor(bv_, cursor.par(),cursor.pos(),true);
910 // The origin of an inset is on the baseline
911 y = y_tmp - (text->cursor.y());
916 if ((cursor.pos() - 1 >= 0) &&
917 (cursor.par()->GetChar(cursor.pos()-1) == LyXParagraph::META_INSET) &&
918 (cursor.par()->GetInset(cursor.pos() - 1)) &&
919 (cursor.par()->GetInset(cursor.pos() - 1)->Editable())) {
920 Inset * tmpinset = cursor.par()->GetInset(cursor.pos()-1);
921 LyXFont font = text->GetFont(bv_->buffer(), cursor.par(),
923 int width = tmpinset->width(bv_, font);
924 int inset_x = font.isVisibleRightToLeft()
925 ? cursor.x() : cursor.x() - width;
926 int start_x = inset_x + tmpinset->scroll();
927 int end_x = inset_x + width;
929 if (x > start_x && x < end_x
930 && y_tmp > cursor.y() - tmpinset->ascent(bv_, font)
931 && y_tmp < cursor.y() + tmpinset->descent(bv_, font)) {
933 if (move_cursor && (tmpinset != bv_->theLockingInset()))
935 text->SetCursor(bv_, cursor.par(),cursor.pos()-1,true);
937 // The origin of an inset is on the baseline
938 y = y_tmp - (text->cursor.y());
946 void BufferView::Pimpl::workAreaExpose()
948 // this is a hack to ensure that we only call this through
949 // BufferView::redraw().
954 static int work_area_width = 0;
955 static unsigned int work_area_height = 0;
957 bool widthChange = workarea_->workWidth() != work_area_width;
958 bool heightChange = workarea_->height() != work_area_height;
960 // update from work area
961 work_area_width = workarea_->workWidth();
962 work_area_height = workarea_->height();
965 // All buffers need a resize
968 // Remove all texts from the textcache
969 // This is not _really_ what we want to do. What
970 // we really want to do is to delete in textcache
971 // that does not have a BufferView with matching
972 // width, but as long as we have only one BufferView
973 // deleting all gives the same result.
974 if (lyxerr.debugging())
975 textcache.show(lyxerr, "Expose delete all");
977 } else if (heightChange) {
978 // Rebuild image of current screen
980 // fitCursor() ensures we don't jump back
981 // to the start of the document on vertical
983 fitCursor(bv_->text);
985 // The main window size has changed, repaint most stuff
987 // ...including the minibuffer
988 owner_->getMiniBuffer()->Init();
990 } else if (screen_) screen_->Redraw(bv_->text);
992 // Grey box when we don't have a buffer
993 workarea_->greyOut();
996 // always make sure that the scrollbar is sane.
998 owner_->updateLayoutChoice();
1003 void BufferView::Pimpl::update()
1005 if (screen_) screen_->Update(bv_->text);
1008 // Values used when calling update:
1010 // -2 - update, move sel_cursor if selection, fitcursor
1011 // -1 - update, move sel_cursor if selection, fitcursor, mark dirty
1012 // 0 - update, move sel_cursor if selection, fitcursor
1013 // 1 - update, move sel_cursor if selection, fitcursor, mark dirty
1014 // 3 - update, move sel_cursor if selection
1017 // a simple redraw of the parts that need refresh
1019 // move sel_cursor if selection -
1020 // the text's sel_cursor is moved if there is selection is progress
1023 // fitCursor() is called and the scrollbar updated
1026 // the buffer is marked dirty.
1035 // UPDATE_ONLY = UPDATE;
1036 // UPDATE_SELECT = UPDATE | SELECT;
1037 // UPDATE_SELECT_MOVE = UPDATE | SELECT | FITCUR;
1038 // UPDATE_SELECT_MOVE_AFTER_CHANGE = UPDATE | SELECT | FITCUR | CHANGE;
1040 // update(-3) -> update(0) -> update(0) -> update(UPDATE)
1041 // update(-2) -> update(1 + 2) -> update(3) -> update(SELECT|FITCUR)
1042 // update(-1) -> update(1 + 2 + 4) -> update(7) -> update(SELECT|FITCUR|CHANGE)
1043 // update(1) -> update(1 + 2 + 4) -> update(7) -> update(SELECT|FITCUR|CHANGE)
1044 // update(3) -> update(1) -> update(1) -> update(SELECT)
1046 void BufferView::Pimpl::update(BufferView::UpdateCodes f)
1048 owner_->updateLayoutChoice();
1050 if (!bv_->text->selection && (f & SELECT)) {
1051 bv_->text->sel_cursor = bv_->text->cursor;
1054 bv_->text->FullRebreak(bv_);
1059 fitCursor(bv_->text);
1063 if (buffer_->isLyxClean()) {
1064 buffer_->markDirty();
1065 owner_->getMiniBuffer()->setTimer(4);
1067 buffer_->markDirty();
1073 // Callback for cursor timer
1074 void BufferView::Pimpl::cursorToggle()
1076 // Quite a nice place for asyncron Inset updating, isn't it?
1077 // Actually no! This is run even if no buffer exist... so (Lgb)
1079 goto set_timer_and_return;
1083 // On my quest to solve the gs render hangups I am now
1084 // disabling the SIGHUP completely, and will do a wait
1085 // now and then instead. If the guess that xforms somehow
1086 // destroys something is true, this is likely (hopefully)
1087 // to solve the problem...at least I hope so. Lgb
1089 // ...Ok this seems to work...at least it does not make things
1090 // worse so far. However I still see gs processes that hangs.
1091 // I would really like to know _why_ they are hanging. Anyway
1092 // the solution without the SIGCHLD handler seems to be easier
1095 // When attaching gdb to a a running gs that hangs it shows
1096 // that it is waiting for input(?) Is it possible for us to
1097 // provide that input somehow? Or figure what it is expecing
1100 // One solution is to, after some time, look if there are some
1101 // old gs processes still running and if there are: kill them
1104 // Another solution is to provide the user an option to rerender
1105 // a picture. This would, for the picture in question, check if
1106 // there is a gs running for it, if so kill it, and start a new
1107 // rendering process.
1109 // these comments posted to lyx@via
1112 int pid = waitpid(static_cast<pid_t>(0), &status, WNOHANG);
1113 if (pid == -1) // error find out what is wrong
1114 ; // ignore it for now.
1116 sigchldhandler(pid, &status);
1119 updatelist.update(bv_);
1122 goto set_timer_and_return;
1125 if (!bv_->theLockingInset()) {
1126 screen_->CursorToggle(bv_->text);
1128 bv_->theLockingInset()->ToggleInsetCursor(bv_);
1131 set_timer_and_return:
1132 cursor_timeout.restart();
1137 void BufferView::Pimpl::cursorPrevious(LyXText * text)
1139 if (!text->cursor.row()->previous())
1142 int y = text->first;
1143 if (text->inset_owner)
1144 y += bv_->text->first;
1145 Row * cursorrow = text->cursor.row();
1146 text->SetCursorFromCoordinates(bv_, bv_->text->cursor.x_fix(), y);
1147 bv_->text->FinishUndo();
1148 // This is to allow jumping over large insets
1149 if ((cursorrow == text->cursor.row()))
1150 text->CursorUp(bv_);
1152 if (text->inset_owner ||
1153 text->cursor.row()->height() < workarea_->height())
1154 screen_->Draw(bv_->text,
1156 - text->cursor.row()->baseline()
1157 + text->cursor.row()->height()
1158 - workarea_->height() + 1 );
1163 void BufferView::Pimpl::cursorNext(LyXText * text)
1165 if (!text->cursor.row()->next())
1168 int y = text->first + workarea_->height();
1169 // if (text->inset_owner)
1170 // y += bv_->text->first;
1171 text->GetRowNearY(y);
1173 Row * cursorrow = text->cursor.row();
1174 text->SetCursorFromCoordinates(bv_, text->cursor.x_fix(), y); // + workarea_->height());
1175 bv_->text->FinishUndo();
1176 // This is to allow jumping over large insets
1177 if ((cursorrow == bv_->text->cursor.row()))
1178 text->CursorDown(bv_);
1180 if (text->inset_owner ||
1181 text->cursor.row()->height() < workarea_->height())
1182 screen_->Draw(bv_->text, text->cursor.y() -
1183 text->cursor.row()->baseline());
1188 bool BufferView::Pimpl::available() const
1190 if (buffer_ && bv_->text) return true;
1195 void BufferView::Pimpl::beforeChange()
1198 bv_->text->ClearSelection();
1201 //owner_->update_timeout.stop();
1205 void BufferView::Pimpl::savePosition()
1207 backstack.push(buffer_->fileName(),
1208 bv_->text->cursor.x(),
1209 bv_->text->cursor.y());
1213 void BufferView::Pimpl::restorePosition()
1215 if (backstack.empty()) return;
1218 string fname = backstack.pop(&x, &y);
1222 if (fname != buffer_->fileName()) {
1223 Buffer * b = bufferlist.exists(fname) ?
1224 bufferlist.getBuffer(fname) :
1225 bufferlist.loadLyXFile(fname); // don't ask, just load it
1226 if (b != 0 ) buffer(b);
1229 bv_->text->SetCursorFromCoordinates(bv_, x, y);
1230 update(BufferView::SELECT|BufferView::FITCUR);
1234 bool BufferView::Pimpl::NoSavedPositions()
1236 return backstack.empty();
1240 void BufferView::Pimpl::setState()
1242 if (!lyxrc.rtl_support)
1245 LyXText * text = bv_->getLyXText();
1246 if (text->real_current_font.isRightToLeft() &&
1247 text->real_current_font.latex() != LyXFont::ON) {
1248 if (owner_->getIntl()->primarykeymap)
1249 owner_->getIntl()->KeyMapSec();
1251 if (!owner_->getIntl()->primarykeymap)
1252 owner_->getIntl()->KeyMapPrim();
1257 void BufferView::Pimpl::insetSleep()
1259 if (bv_->theLockingInset() && !bv_->inset_slept) {
1260 bv_->theLockingInset()->GetCursorPos(bv_, bv_->slx, bv_->sly);
1261 bv_->theLockingInset()->InsetUnlock(bv_);
1262 bv_->inset_slept = true;
1267 void BufferView::Pimpl::insetWakeup()
1269 if (bv_->theLockingInset() && bv_->inset_slept) {
1270 bv_->theLockingInset()->Edit(bv_, bv_->slx, bv_->sly, 0);
1271 bv_->inset_slept = false;
1276 void BufferView::Pimpl::insetUnlock()
1278 if (bv_->theLockingInset()) {
1279 if (!bv_->inset_slept)
1280 bv_->theLockingInset()->InsetUnlock(bv_);
1281 bv_->theLockingInset(0);
1282 bv_->text->FinishUndo();
1283 bv_->inset_slept = false;
1288 bool BufferView::Pimpl::focus() const
1290 return workarea_->hasFocus();
1294 void BufferView::Pimpl::focus(bool f)
1296 if (f) workarea_->setFocus();
1300 bool BufferView::Pimpl::active() const
1302 return workarea_->active();
1306 bool BufferView::Pimpl::belowMouse() const
1308 return workarea_->belowMouse();
1312 void BufferView::Pimpl::showCursor()
1315 screen_->ShowCursor(bv_->text);
1319 void BufferView::Pimpl::hideCursor()
1322 screen_->HideCursor();
1326 void BufferView::Pimpl::toggleSelection(bool b)
1329 screen_->ToggleSelection(bv_->text, b);
1333 void BufferView::Pimpl::toggleToggle()
1336 screen_->ToggleToggle(bv_->text);
1340 void BufferView::Pimpl::center()
1343 if (bv_->text->cursor.y() > static_cast<int>((workarea_->height() / 2))) {
1344 screen_->Draw(bv_->text, bv_->text->cursor.y() - workarea_->height() / 2);
1346 screen_->Draw(bv_->text, 0);
1348 update(BufferView::SELECT|BufferView::FITCUR);
1353 void BufferView::Pimpl::pasteClipboard(bool asPara)
1355 if (buffer_ == 0) return;
1357 screen_->HideCursor();
1358 bv_->beforeChange();
1360 string const clip(workarea_->getClipboard());
1362 if (clip.empty()) return;
1365 bv_->text->InsertStringB(bv_, clip);
1367 bv_->text->InsertStringA(bv_, clip);
1369 update(BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
1373 void BufferView::Pimpl::stuffClipboard(string const & stuff) const
1375 workarea_->putClipboard(stuff);