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"
25 #include "lyx_gui_misc.h"
32 /* the selection possible is needed, that only motion events are
33 * used, where the bottom press event was on the drawing area too */
34 bool selection_possible = false;
36 extern BufferList bufferlist;
37 extern char ascii_type;
39 extern void sigchldhandler(pid_t pid, int * status);
40 extern int bibitemMaxWidth(Painter &, LyXFont const &);
41 extern void FreeUpdateTimer();
44 void C_BufferView_CursorToggleCB(FL_OBJECT * ob, long buf);
50 XSync(fl_get_display(), 0);
55 void SetXtermCursor(Window win)
58 static bool cursor_undefined = true;
59 if (cursor_undefined){
60 cursor = XCreateFontCursor(fl_display, XC_xterm);
62 cursor_undefined = false;
64 XDefineCursor(fl_display, win, cursor);
69 BufferView::Pimpl::Pimpl(BufferView * b, LyXView * o,
70 int xpos, int ypos, int width, int height)
74 workarea = new WorkArea(bv_, xpos, ypos, width, height);
78 current_scrollbar_value = 0;
79 fl_set_timer(timer_cursor, 0.4);
81 work_area_focus = true;
83 using_xterm_cursor = false;
87 Painter & BufferView::Pimpl::painter()
89 return workarea->getPainter();
93 void BufferView::Pimpl::buffer(Buffer * b)
95 lyxerr[Debug::INFO] << "Setting buffer in BufferView ("
99 buffer_->delUser(bv_);
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
105 textcache.add(bv_->text);
106 if (lyxerr.debugging())
107 textcache.show(lyxerr, "BufferView::buffer");
112 // Set current buffer
115 if (bufferlist.getState() == BufferList::CLOSING) return;
118 // screen is always deleted when the buffer is changed.
122 // If we are closing the buffer, use the first buffer as current
124 buffer_ = bufferlist.first();
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
133 resizeCurrentBuffer();
138 screen->first = screen->TopCursorVisible();
140 updateAllVisibleBufferRelatedPopups();
143 lyxerr[Debug::INFO] << " No Buffer!" << endl;
144 owner_->getMenus()->hideMenus();
148 // Also remove all remaining text's from the testcache.
149 // (there should not be any!) (if there is any it is a
151 if (lyxerr.debugging())
152 textcache.show(lyxerr, "buffer delete all");
155 // should update layoutchoice even if we don't have a buffer.
156 owner_->updateLayoutChoice();
157 owner_->getMiniBuffer()->Init();
158 owner_->updateWindowTitle();
161 void BufferView::Pimpl::resize(int xpos, int ypos, int width, int height)
163 workarea->resize(xpos, ypos, width, height);
169 void BufferView::Pimpl::resize()
171 // This will resize the buffer. (Asger)
173 resizeCurrentBuffer();
177 void BufferView::Pimpl::redraw()
179 lyxerr[Debug::INFO] << "BufferView::redraw()" << endl;
184 void BufferView::Pimpl::fitCursor()
186 if (screen) screen->FitCursor();
191 void BufferView::Pimpl::redoCurrentBuffer()
193 lyxerr[Debug::INFO] << "BufferView::redoCurrentBuffer" << endl;
194 if (buffer_ && bv_->text) {
196 owner_->updateLayoutChoice();
201 int BufferView::Pimpl::resizeCurrentBuffer()
203 lyxerr[Debug::INFO] << "resizeCurrentBuffer" << endl;
205 LyXParagraph * par = 0;
206 LyXParagraph * selstartpar = 0;
207 LyXParagraph * selendpar = 0;
216 owner_->getMiniBuffer()->Set(_("Formatting document..."));
219 par = bv_->text->cursor.par;
220 pos = bv_->text->cursor.pos;
221 selstartpar = bv_->text->sel_start_cursor.par;
222 selstartpos = bv_->text->sel_start_cursor.pos;
223 selendpar = bv_->text->sel_end_cursor.par;
224 selendpos = bv_->text->sel_end_cursor.pos;
225 selection = bv_->text->selection;
226 mark_set = bv_->text->mark_set;
228 bv_->text = new LyXText(bv_, workarea->workWidth(), buffer_);
230 // See if we have a text in TextCache that fits
231 // the new buffer_ with the correct width.
232 bv_->text = textcache.findFit(buffer_, workarea->workWidth());
234 if (lyxerr.debugging()) {
235 lyxerr << "Found a LyXText that fits:\n";
236 textcache.show(lyxerr, bv_->text);
238 // Set the owner of the newly found text
239 bv_->text->owner(bv_);
240 if (lyxerr.debugging())
241 textcache.show(lyxerr, "resizeCurrentBuffer");
243 bv_->text = new LyXText(bv_, workarea->workWidth(), buffer_);
249 bv_->text->selection = true;
250 /* at this point just to avoid the Delete-Empty-Paragraph
251 * Mechanism when setting the cursor */
252 bv_->text->mark_set = mark_set;
254 bv_->text->SetCursor(selstartpar, selstartpos);
255 bv_->text->sel_cursor = bv_->text->cursor;
256 bv_->text->SetCursor(selendpar, selendpos);
257 bv_->text->SetSelection();
258 bv_->text->SetCursor(par, pos);
260 bv_->text->SetCursor(par, pos);
261 bv_->text->sel_cursor = bv_->text->cursor;
262 bv_->text->selection = false;
265 screen->first = screen->TopCursorVisible(); /* this will scroll the
266 * screen such that the
271 owner_->getMiniBuffer()->Init();
275 // Now if the title form still exist kill it
282 void BufferView::Pimpl::gotoError()
287 screen->HideCursor();
292 if (!bv_->text->GotoNextError()) {
293 if (bv_->text->cursor.pos
294 || bv_->text->cursor.par != bv_->text->FirstParagraph()) {
295 tmp = bv_->text->cursor;
296 bv_->text->cursor.par = bv_->text->FirstParagraph();
297 bv_->text->cursor.pos = 0;
298 if (!bv_->text->GotoNextError()) {
299 bv_->text->cursor = tmp;
300 owner_->getMiniBuffer()
301 ->Set(_("No more errors"));
305 owner_->getMiniBuffer()->Set(_("No more errors"));
310 bv_->text->sel_cursor = bv_->text->cursor;
314 void BufferView::Pimpl::updateScreen()
316 // Regenerate the screen.
318 screen = new LyXScreen(*workarea, bv_->text);
322 void BufferView::Pimpl::create_view()
331 timer_cursor = obj = fl_add_timer(FL_HIDDEN_TIMER,
332 0, 0, 0, 0, "Timer");
333 fl_set_object_callback(obj, C_BufferView_CursorToggleCB, 0);
338 void BufferView::Pimpl::updateScrollbar()
340 /* If the text is smaller than the working area, the scrollbar
341 * maximum must be the working area height. No scrolling will
345 workarea->setScrollbar(0, 1.0);
349 static long max2 = 0;
350 static long height2 = 0;
356 cbth = bv_->text->height;
358 cbsf = screen->first;
360 // check if anything has changed.
362 height2 == workarea->height() &&
363 current_scrollbar_value == cbsf)
366 height2 = workarea->height();
367 current_scrollbar_value = cbsf;
369 if (cbth <= height2) { // text is smaller than screen
370 workarea->setScrollbar(0, 1.0); // right?
374 long maximum_height = workarea->height() * 3 / 4 + cbth;
378 double hfloat = workarea->height();
379 double maxfloat = maximum_height;
381 float slider_size = 0.0;
382 int slider_value = value;
384 workarea->setScrollbarBounds(0, bv_->text->height - workarea->height());
385 double lineh = bv_->text->DefaultHeight();
386 workarea->setScrollbarIncrements(lineh);
387 if (maxfloat > 0.0) {
388 if ((hfloat / maxfloat) * float(height2) < 3)
389 slider_size = 3.0/float(height2);
391 slider_size = hfloat / maxfloat;
393 slider_size = hfloat;
395 workarea->setScrollbar(slider_value, slider_size / workarea->height());
399 // Callback for scrollbar slider
400 void BufferView::Pimpl::scrollCB(double value)
402 extern bool cursor_follows_scrollbar;
404 if (buffer_ == 0) return;
406 current_scrollbar_value = long(value);
407 if (current_scrollbar_value < 0)
408 current_scrollbar_value = 0;
413 screen->Draw(current_scrollbar_value);
415 if (cursor_follows_scrollbar) {
416 LyXText * vbt = bv_->text;
417 int height = vbt->DefaultHeight();
419 if (vbt->cursor.y < screen->first + height) {
420 vbt->SetCursorFromCoordinates(0,
423 } else if (vbt->cursor.y >
424 screen->first + workarea->height() - height) {
425 vbt->SetCursorFromCoordinates(0,
435 // Callback for scrollbar down button
436 void BufferView::Pimpl::downCB(long time, int button)
438 if (buffer_ == 0) return;
454 int BufferView::Pimpl::scrollUp(long time)
456 if (buffer_ == 0) return 0;
457 if (!screen) return 0;
459 double value = workarea->getScrollbarValue();
461 if (value == 0) return 0;
463 float add_value = (bv_->text->DefaultHeight()
464 + float(time) * float(time) * 0.125);
466 if (add_value > workarea->height())
467 add_value = float(workarea->height() -
468 bv_->text->DefaultHeight());
475 workarea->setScrollbarValue(value);
477 bv_->scrollCB(value);
482 int BufferView::Pimpl::scrollDown(long time)
484 if (buffer_ == 0) return 0;
485 if (!screen) return 0;
487 double value= workarea->getScrollbarValue();
488 pair<double, double> p = workarea->getScrollbarBounds();
489 double max = p.second;
491 if (value == max) return 0;
493 float add_value = (bv_->text->DefaultHeight()
494 + float(time) * float(time) * 0.125);
496 if (add_value > workarea->height())
497 add_value = float(workarea->height() -
498 bv_->text->DefaultHeight());
505 workarea->setScrollbarValue(value);
507 bv_->scrollCB(value);
512 void BufferView::Pimpl::scrollUpOnePage()
514 if (buffer_ == 0) return;
517 long y = screen->first;
521 Row * row = bv_->text->GetRowNearY(y);
523 y = y - workarea->height() + row->height;
525 workarea->setScrollbarValue(y);
531 void BufferView::Pimpl::scrollDownOnePage()
533 if (buffer_ == 0) return;
536 long y = screen->first;
538 if (y > bv_->text->height - workarea->height())
541 y += workarea->height();
542 bv_->text->GetRowNearY(y);
544 workarea->setScrollbarValue(y);
550 void BufferView::Pimpl::workAreaMotionNotify(int x, int y, unsigned int state)
552 if (buffer_ == 0 || !screen) return;
554 // Only use motion with button 1
555 if (!state & Button1MotionMask)
558 // Check for inset locking
559 if (bv_->the_locking_inset) {
560 LyXCursor cursor = bv_->text->cursor;
561 bv_->the_locking_inset->
562 InsetMotionNotify(bv_,
569 /* The selection possible is needed, that only motion events are
570 * used, where the bottom press event was on the drawing area too */
571 if (selection_possible) {
572 screen->HideCursor();
574 bv_->text->SetCursorFromCoordinates(x, y + screen->first);
576 if (!bv_->text->selection)
577 update(-3); // Maybe an empty line was deleted
579 bv_->text->SetSelection();
580 screen->ToggleToggle();
581 if (screen->FitCursor())
583 screen->ShowCursor();
589 // Single-click on work area
590 void BufferView::Pimpl::workAreaButtonPress(int xpos, int ypos,
596 if (buffer_ == 0 || !screen) return;
598 Inset * inset_hit = checkInsetHit(xpos, ypos, button);
600 // ok ok, this is a hack.
601 if (button == 4 || button == 5) {
604 scrollUp(100); // This number is only temporary
612 if (bv_->the_locking_inset) {
613 // We are in inset locking mode
615 /* Check whether the inset was hit. If not reset mode,
616 otherwise give the event to the inset */
617 if (inset_hit == bv_->the_locking_inset) {
618 bv_->the_locking_inset->
619 InsetButtonPress(bv_,
624 bv_->unlockInset(bv_->the_locking_inset);
629 selection_possible = true;
630 screen->HideCursor();
632 // Right button mouse click on a table
634 (bv_->text->cursor.par->table ||
635 bv_->text->MouseHitInTable(xpos, ypos + screen->first))) {
636 // Set the cursor to the press-position
637 bv_->text->SetCursorFromCoordinates(xpos, ypos + screen->first);
640 // Only show the table popup if the hit is in
642 if (!bv_->text->HitInTable(bv_->text->cursor.row, xpos))
645 // Hit above or below the table?
647 if (!bv_->text->selection) {
648 screen->ToggleSelection();
649 bv_->text->ClearSelection();
650 bv_->text->FullRebreak();
654 // Popup table popup when on a table.
655 // This is obviously temporary, since we
656 // should be able to popup various
657 // context-sensitive-menus with the
658 // the right mouse. So this should be done more
659 // general in the future. Matthias.
660 selection_possible = false;
662 ->Dispatch(LFUN_LAYOUT_TABLE,
668 int screen_first = screen->first;
670 // Middle button press pastes if we have a selection
671 bool paste_internally = false;
673 && bv_->text->selection) {
674 owner_->getLyXFunc()->Dispatch(LFUN_COPY);
675 paste_internally = true;
678 // Clear the selection
679 screen->ToggleSelection();
680 bv_->text->ClearSelection();
681 bv_->text->FullRebreak();
685 // Single left click in math inset?
686 if ((inset_hit != 0) &&
687 (inset_hit->Editable()==Inset::HIGHLY_EDITABLE)) {
688 // Highly editable inset, like math
689 UpdatableInset * inset = static_cast<UpdatableInset *>(inset_hit);
690 selection_possible = false;
691 owner_->updateLayoutChoice();
692 owner_->getMiniBuffer()->Set(inset->EditMessage());
693 inset->InsetButtonPress(bv_, xpos, ypos, button);
694 inset->Edit(bv_, xpos, ypos, button);
698 // Right click on a footnote flag opens float menu
700 selection_possible = false;
704 if (!inset_hit) // otherwise it was already set in checkInsetHit(...)
705 bv_->text->SetCursorFromCoordinates(xpos, ypos + screen_first);
706 bv_->text->FinishUndo();
707 bv_->text->sel_cursor = bv_->text->cursor;
708 bv_->text->cursor.x_fix = bv_->text->cursor.x;
710 owner_->updateLayoutChoice();
711 if (screen->FitCursor()){
713 selection_possible = false;
716 // Insert primary selection with middle mouse
717 // if there is a local selection in the current buffer,
720 if (paste_internally)
721 owner_->getLyXFunc()->Dispatch(LFUN_PASTE);
723 owner_->getLyXFunc()->Dispatch(LFUN_PASTESELECTION,
725 selection_possible = false;
731 void BufferView::Pimpl::doubleClick(int /*x*/, int /*y*/, unsigned int button)
734 if (buffer_ && !bv_->the_locking_inset) {
735 if (screen && button == 1) {
736 screen->HideCursor();
737 screen->ToggleSelection();
738 bv_->text->SelectWord();
739 screen->ToggleSelection(false);
740 /* This will fit the cursor on the screen
748 void BufferView::Pimpl::tripleClick(int /*x*/, int /*y*/, unsigned int button)
751 if (buffer_ && screen && button == 1) {
752 screen->HideCursor();
753 screen->ToggleSelection();
754 bv_->text->CursorHome();
755 bv_->text->sel_cursor = bv_->text->cursor;
756 bv_->text->CursorEnd();
757 bv_->text->SetSelection();
758 screen->ToggleSelection(false);
759 /* This will fit the cursor on the screen
766 void BufferView::Pimpl::enterView()
768 if (active() && available()) {
769 SetXtermCursor(workarea->getWin());
770 using_xterm_cursor = true;
775 void BufferView::Pimpl::leaveView()
777 if (using_xterm_cursor) {
778 XUndefineCursor(fl_display, workarea->getWin());
779 using_xterm_cursor = false;
784 void BufferView::Pimpl::workAreaButtonRelease(int x, int y, unsigned int button)
786 if (buffer_ == 0 || screen == 0) return;
788 // If we hit an inset, we have the inset coordinates in these
789 // and inset_hit points to the inset. If we do not hit an
790 // inset, inset_hit is 0, and inset_x == x, inset_y == y.
791 Inset * inset_hit = checkInsetHit(x, y, button);
793 if (bv_->the_locking_inset) {
794 // We are in inset locking mode.
796 /* LyX does a kind of work-area grabbing for insets.
797 Only a ButtonPress Event outside the inset will
798 force a InsetUnlock. */
799 bv_->the_locking_inset->
800 InsetButtonRelease(bv_, x, y, button);
804 selection_possible = false;
805 if (bv_->text->cursor.par->table) {
806 int cell = bv_->text->
807 NumberOfCell(bv_->text->cursor.par,
808 bv_->text->cursor.pos);
809 if (bv_->text->cursor.par->table->IsContRow(cell) &&
810 bv_->text->cursor.par->table->
811 CellHasContRow(bv_->text->cursor.par->table->
812 GetCellAbove(cell))<0) {
813 bv_->text->CursorUp();
817 if (button >= 2) return;
820 owner_->getMiniBuffer()->Set(CurrentState(bv_));
822 // Did we hit an editable inset?
823 if (inset_hit != 0) {
824 // Inset like error, notes and figures
825 selection_possible = false;
827 #warning fix this proper in 0.13
829 // Following a ref shouldn't issue
830 // a push on the undo-stack
831 // anylonger, now that we have
832 // keybindings for following
833 // references and returning from
834 // references. IMHO though, it
835 // should be the inset's own business
836 // to push or not push on the undo
837 // stack. They don't *have* to
838 // alter the document...
840 // ...or maybe the SetCursorParUndo()
841 // below isn't necessary at all anylonger?
842 if (inset_hit->LyxCode() == Inset::REF_CODE) {
843 bv_->text->SetCursorParUndo();
846 owner_->getMiniBuffer()->Set(inset_hit->EditMessage());
847 if (inset_hit->Editable()==Inset::HIGHLY_EDITABLE) {
848 // Highly editable inset, like math
849 UpdatableInset *inset = (UpdatableInset *)inset_hit;
850 inset->InsetButtonRelease(bv_, x, y, button);
852 inset_hit->InsetButtonRelease(bv_, x, y, button);
853 inset_hit->Edit(bv_, x, y, button);
858 // check whether we want to open a float
862 if (bv_->text->cursor.pos <
863 bv_->text->cursor.par->Last()) {
864 c = bv_->text->cursor.par->
865 GetChar(bv_->text->cursor.pos);
867 if (c == LyXParagraph::META_FOOTNOTE
868 || c == LyXParagraph::META_MARGIN
869 || c == LyXParagraph::META_FIG
870 || c == LyXParagraph::META_TAB
871 || c == LyXParagraph::META_WIDE_FIG
872 || c == LyXParagraph::META_WIDE_TAB
873 || c == LyXParagraph::META_ALGORITHM){
875 } else if (bv_->text->cursor.pos - 1 >= 0) {
876 c = bv_->text->cursor.par->
877 GetChar(bv_->text->cursor.pos - 1);
878 if (c == LyXParagraph::META_FOOTNOTE
879 || c == LyXParagraph::META_MARGIN
880 || c == LyXParagraph::META_FIG
881 || c == LyXParagraph::META_TAB
882 || c == LyXParagraph::META_WIDE_FIG
883 || c == LyXParagraph::META_WIDE_TAB
884 || c == LyXParagraph::META_ALGORITHM){
885 // We are one step too far to the right
886 bv_->text->CursorLeft();
892 selection_possible = false;
897 // Do we want to close a float? (click on the float-label)
898 if (bv_->text->cursor.row->par->footnoteflag ==
899 LyXParagraph::OPEN_FOOTNOTE
900 //&& text->cursor.pos == 0
901 && bv_->text->cursor.row->previous &&
902 bv_->text->cursor.row->previous->par->
903 footnoteflag != LyXParagraph::OPEN_FOOTNOTE){
904 LyXFont font(LyXFont::ALL_SANE);
905 font.setSize(LyXFont::SIZE_FOOTNOTE);
907 int box_x = 20; // LYX_PAPER_MARGIN;
908 box_x += lyxfont::width(" wide-tab ", font);
910 int screen_first = screen->first;
913 && y + screen_first > bv_->text->cursor.y -
914 bv_->text->cursor.row->baseline
915 && y + screen_first < bv_->text->cursor.y -
916 bv_->text->cursor.row->baseline
917 + lyxfont::maxAscent(font) * 1.2 + lyxfont::maxDescent(font) * 1.2) {
919 selection_possible = false;
924 // Maybe we want to edit a bibitem ale970302
925 if (bv_->text->cursor.par->bibkey && x < 20 +
926 bibitemMaxWidth(bv_->painter(),
929 params.textclass).defaultfont())) {
930 bv_->text->cursor.par->bibkey->Edit(bv_, 0, 0, 0);
938 * Returns an inset if inset was hit. 0 otherwise.
939 * If hit, the coordinates are changed relative to the inset.
940 * Otherwise coordinates are not changed, and false is returned.
942 Inset * BufferView::Pimpl::checkInsetHit(int & x, int & y, unsigned int /* button */)
947 int y_tmp = y + screen->first;
950 bv_->text->SetCursorFromCoordinates(cursor, x, y_tmp);
951 bool move_cursor = ((cursor.par != bv_->text->cursor.par) ||
952 (cursor.pos != bv_->text->cursor.pos));
954 if (cursor.pos < cursor.par->Last()
955 && cursor.par->GetChar(cursor.pos) == LyXParagraph::META_INSET
956 && cursor.par->GetInset(cursor.pos)
957 && cursor.par->GetInset(cursor.pos)->Editable()) {
959 // Check whether the inset really was hit
960 Inset * tmpinset = cursor.par->GetInset(cursor.pos);
961 LyXFont font = bv_->text->GetFont(cursor.par, cursor.pos);
962 bool is_rtl = font.isVisibleRightToLeft();
966 start_x = cursor.x - tmpinset->width(bv_->painter(), font);
970 end_x = cursor.x + tmpinset->width(bv_->painter(), font);
973 if (x > start_x && x < end_x
974 && y_tmp > cursor.y - tmpinset->ascent(bv_->painter(), font)
975 && y_tmp < cursor.y + tmpinset->descent(bv_->painter(), font)) {
976 if (move_cursor && (tmpinset != bv_->the_locking_inset))
977 bv_->text->SetCursor(cursor.par,cursor.pos,true);
979 // The origin of an inset is on the baseline
980 y = y_tmp - (bv_->text->cursor.y);
985 if ((cursor.pos - 1 >= 0) &&
986 (cursor.par->GetChar(cursor.pos-1) == LyXParagraph::META_INSET) &&
987 (cursor.par->GetInset(cursor.pos - 1)) &&
988 (cursor.par->GetInset(cursor.pos - 1)->Editable())) {
989 Inset * tmpinset = cursor.par->GetInset(cursor.pos-1);
990 LyXFont font = bv_->text->GetFont(cursor.par, cursor.pos-1);
991 bool is_rtl = font.isVisibleRightToLeft();
995 start_x = cursor.x - tmpinset->width(bv_->painter(), font);
999 end_x = cursor.x + tmpinset->width(bv_->painter(), font);
1001 if (x > start_x && x < end_x
1002 && y_tmp > cursor.y - tmpinset->ascent(bv_->painter(), font)
1003 && y_tmp < cursor.y + tmpinset->descent(bv_->painter(), font)) {
1004 if (move_cursor && (tmpinset != bv_->the_locking_inset))
1005 bv_->text->SetCursor(cursor.par,cursor.pos,true);
1007 // The origin of an inset is on the baseline
1008 y = y_tmp - (bv_->text->cursor.y);
1016 void BufferView::Pimpl::workAreaExpose()
1018 // this is a hack to ensure that we only call this through
1019 // BufferView::redraw().
1024 static int work_area_width = -1;
1025 static int work_area_height = -1;
1027 bool widthChange = workarea->workWidth() != work_area_width;
1028 bool heightChange = workarea->height() != work_area_height;
1030 // update from work area
1031 work_area_width = workarea->workWidth();
1032 work_area_height = workarea->height();
1035 // All buffers need a resize
1036 bufferlist.resize();
1038 // Remove all texts from the textcache
1039 // This is not _really_ what we want to do. What
1040 // we really want to do is to delete in textcache
1041 // that does not have a BufferView with matching
1042 // width, but as long as we have only one BufferView
1043 // deleting all gives the same result.
1044 if (lyxerr.debugging())
1045 textcache.show(lyxerr, "Expose delete all");
1047 } else if (heightChange) {
1048 // Rebuild image of current screen
1050 // fitCursor() ensures we don't jump back
1051 // to the start of the document on vertical
1055 // The main window size has changed, repaint most stuff
1057 // ...including the minibuffer
1058 owner_->getMiniBuffer()->Init();
1060 } else if (screen) screen->Redraw();
1062 // Grey box when we don't have a buffer
1063 workarea->greyOut();
1066 // always make sure that the scrollbar is sane.
1068 owner_->updateLayoutChoice();
1074 string fromClipboard(Window win, XEvent * event)
1077 if (event->xselection.type == XA_STRING
1078 && event->xselection.property) {
1082 unsigned char * uc = 0;
1084 if (XGetWindowProperty(
1085 event->xselection.display, // display
1087 event->xselection.property, // property
1091 XA_STRING, // req_type
1092 &tmpatom, // actual_type_return
1093 &tmpint, // actual_format_return
1104 if (XGetWindowProperty(
1105 event->xselection.display, // display
1107 event->xselection.property, // property
1109 ul2/4+1, // long_length
1111 XA_STRING, // req_type
1112 &tmpatom, // actual_type_return
1113 &tmpint, // actual_format_return
1114 &ul1, // nitems_return
1115 &ul2, // bytes_after_return
1116 &uc // prop_return */
1121 strret = reinterpret_cast<char*>(uc);
1122 free(uc); // yes free!
1130 void BufferView::Pimpl::workAreaSelectionNotify(Window win, XEvent * event)
1132 if (buffer_ == 0) return;
1134 screen->HideCursor();
1135 bv_->beforeChange();
1136 string clb = fromClipboard(win, event);
1139 bv_->text->InsertStringA(clb);
1141 bv_->text->InsertStringB(clb);
1148 void BufferView::Pimpl::update()
1150 if (screen) screen->Update();
1154 void BufferView::Pimpl::update(signed char f)
1156 owner_->updateLayoutChoice();
1158 if (!bv_->text->selection && f > -3)
1159 bv_->text->sel_cursor = bv_->text->cursor;
1162 bv_->text->FullRebreak();
1166 if (f != 3 && f != -3) {
1171 if (f == 1 || f == -1) {
1172 if (buffer_->isLyxClean()) {
1173 buffer_->markDirty();
1174 owner_->getMiniBuffer()->setTimer(4);
1176 buffer_->markDirty();
1182 void BufferView::Pimpl::smallUpdate(signed char f)
1184 screen->SmallUpdate();
1185 if (screen->TopCursorVisible()
1194 if (!bv_->text->selection)
1195 bv_->text->sel_cursor = bv_->text->cursor;
1197 if (f == 1 || f == -1) {
1198 if (buffer_->isLyxClean()) {
1199 buffer_->markDirty();
1200 owner_->getMiniBuffer()->setTimer(4);
1202 buffer_->markDirty();
1208 // Callback for cursor timer
1209 void BufferView::Pimpl::cursorToggle()
1211 // Quite a nice place for asyncron Inset updating, isn't it?
1212 // Actually no! This is run even if no buffer exist... so (Lgb)
1214 goto set_timer_and_return;
1218 // On my quest to solve the gs render hangups I am now
1219 // disabling the SIGHUP completely, and will do a wait
1220 // now and then instead. If the guess that xforms somehow
1221 // destroys something is true, this is likely (hopefully)
1222 // to solve the problem...at least I hope so. Lgb
1224 // ...Ok this seems to work...at least it does not make things
1225 // worse so far. However I still see gs processes that hangs.
1226 // I would really like to know _why_ they are hanging. Anyway
1227 // the solution without the SIGCHLD handler seems to be easier
1230 // When attaching gdb to a a running gs that hangs it shows
1231 // that it is waiting for input(?) Is it possible for us to
1232 // provide that input somehow? Or figure what it is expecing
1235 // One solution is to, after some time, look if there are some
1236 // old gs processes still running and if there are: kill them
1239 // Another solution is to provide the user an option to rerender
1240 // a picture. This would, for the picture in question, check if
1241 // there is a gs running for it, if so kill it, and start a new
1242 // rendering process.
1244 // these comments posted to lyx@via
1247 int pid = waitpid(static_cast<pid_t>(0), &status, WNOHANG);
1248 if (pid == -1) // error find out what is wrong
1249 ; // ignore it for now.
1251 sigchldhandler(pid, &status);
1254 updatelist.update(bv_);
1257 goto set_timer_and_return;
1260 if (lyx_focus && work_area_focus) {
1261 if (!bv_->the_locking_inset) {
1262 screen->CursorToggle();
1264 bv_->the_locking_inset->
1265 ToggleInsetCursor(bv_);
1267 goto set_timer_and_return;
1269 // Make sure that the cursor is visible.
1270 if (!bv_->the_locking_inset) {
1271 screen->ShowCursor();
1273 if (!bv_->the_locking_inset->isCursorVisible())
1274 bv_->the_locking_inset->
1275 ToggleInsetCursor(bv_);
1277 // This is only run when work_area_focus or lyx_focus is false.
1280 XGetInputFocus(fl_display, &tmpwin, &tmp);
1281 // Commenting this out, we have not had problems with this
1282 // for a long time. We will probably work on this code later
1283 // and we can reenable this debug code then. Now it only
1284 // anoying when debugging. (Lgb)
1285 //if (lyxerr.debugging(Debug::INFO)) {
1286 // lyxerr << "tmpwin: " << tmpwin
1287 // << "\nwindow: " << view->owner_->getForm()->window
1288 // << "\nwork_area_focus: " << view->work_area_focus
1289 // << "\nlyx_focus : " << view->lyx_focus
1292 if (tmpwin != owner_->getForm()->window) {
1297 if (!work_area_focus)
1300 goto set_timer_and_return;
1304 set_timer_and_return:
1305 fl_set_timer(timer_cursor, 0.4);
1311 void BufferView::Pimpl::cursorPrevious()
1313 if (!bv_->text->cursor.row->previous) return;
1315 long y = screen->first;
1316 Row * cursorrow = bv_->text->cursor.row;
1317 bv_->text->SetCursorFromCoordinates(bv_->text->cursor.x_fix, y);
1318 bv_->text->FinishUndo();
1319 // This is to allow jumping over large insets
1320 if ((cursorrow == bv_->text->cursor.row))
1321 bv_->text->CursorUp();
1323 if (bv_->text->cursor.row->height < workarea->height())
1324 screen->Draw(bv_->text->cursor.y
1325 - bv_->text->cursor.row->baseline
1326 + bv_->text->cursor.row->height
1327 - workarea->height() + 1 );
1331 void BufferView::Pimpl::cursorNext()
1333 if (!bv_->text->cursor.row->next) return;
1335 long y = screen->first;
1336 bv_->text->GetRowNearY(y);
1337 Row * cursorrow = bv_->text->cursor.row;
1338 bv_->text->SetCursorFromCoordinates(bv_->text->cursor.x_fix, y
1339 + workarea->height());
1340 bv_->text->FinishUndo();
1341 // This is to allow jumping over large insets
1342 if ((cursorrow == bv_->text->cursor.row))
1343 bv_->text->CursorDown();
1345 if (bv_->text->cursor.row->height < workarea->height())
1346 screen->Draw(bv_->text->cursor.y
1347 - bv_->text->cursor.row->baseline);
1351 bool BufferView::Pimpl::available() const
1353 if (buffer_ && bv_->text) return true;
1358 void BufferView::Pimpl::beforeChange()
1361 bv_->text->ClearSelection();
1366 void BufferView::Pimpl::savePosition()
1368 backstack.push(buffer_->fileName(),
1369 bv_->text->cursor.x,
1370 bv_->text->cursor.y);
1374 void BufferView::Pimpl::restorePosition()
1376 if (backstack.empty()) return;
1379 string fname = backstack.pop(&x, &y);
1382 Buffer * b = bufferlist.exists(fname) ?
1383 bufferlist.getBuffer(fname) :
1384 bufferlist.loadLyXFile(fname); // don't ask, just load it
1386 bv_->text->SetCursorFromCoordinates(x, y);
1391 void BufferView::Pimpl::setState()
1393 if (!lyxrc.rtl_support)
1396 if (bv_->text->real_current_font.isRightToLeft() &&
1397 bv_->text->real_current_font.latex() != LyXFont::ON) {
1398 if (owner_->getIntl()->primarykeymap)
1399 owner_->getIntl()->KeyMapSec();
1401 if (!owner_->getIntl()->primarykeymap)
1402 owner_->getIntl()->KeyMapPrim();
1407 void BufferView::Pimpl::insetSleep()
1409 if (bv_->the_locking_inset && !bv_->inset_slept) {
1410 bv_->the_locking_inset->GetCursorPos(bv_->slx, bv_->sly);
1411 bv_->the_locking_inset->InsetUnlock(bv_);
1412 bv_->inset_slept = true;
1417 void BufferView::Pimpl::insetWakeup()
1419 if (bv_->the_locking_inset && bv_->inset_slept) {
1420 bv_->the_locking_inset->Edit(bv_, bv_->slx, bv_->sly, 0);
1421 bv_->inset_slept = false;
1426 void BufferView::Pimpl::insetUnlock()
1428 if (bv_->the_locking_inset) {
1429 if (!bv_->inset_slept) bv_->the_locking_inset->InsetUnlock(bv_);
1430 bv_->the_locking_inset = 0;
1431 bv_->text->FinishUndo();
1432 bv_->inset_slept = false;
1437 bool BufferView::Pimpl::focus() const
1439 return workarea->hasFocus();
1443 void BufferView::Pimpl::focus(bool f)
1445 if (f) workarea->setFocus();
1449 bool BufferView::Pimpl::active() const
1451 return workarea->active();
1455 bool BufferView::Pimpl::belowMouse() const
1457 return workarea->belowMouse();
1461 void BufferView::Pimpl::showCursor()
1464 screen->ShowCursor();
1468 void BufferView::Pimpl::hideCursor()
1471 screen->HideCursor();
1475 void BufferView::Pimpl::toggleSelection(bool b)
1478 screen->ToggleSelection(b);
1482 void BufferView::Pimpl::toggleToggle()
1485 screen->ToggleToggle();
1489 void BufferView::Pimpl::center()
1492 if (bv_->text->cursor.y > workarea->height() / 2) {
1493 screen->Draw(bv_->text->cursor.y - workarea->height() / 2);