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"
28 #include "support/LAssert.h"
34 /* the selection possible is needed, that only motion events are
35 * used, where the bottom press event was on the drawing area too */
36 bool selection_possible = false;
38 extern BufferList bufferlist;
39 extern char ascii_type;
41 extern void sigchldhandler(pid_t pid, int * status);
42 extern int bibitemMaxWidth(Painter &, LyXFont const &);
43 extern void FreeUpdateTimer();
46 void C_BufferView_CursorToggleCB(FL_OBJECT * ob, long buf);
52 XSync(fl_get_display(), 0);
57 void SetXtermCursor(Window win)
60 static bool cursor_undefined = true;
61 if (cursor_undefined){
62 cursor = XCreateFontCursor(fl_display, XC_xterm);
64 cursor_undefined = false;
66 XDefineCursor(fl_display, win, cursor);
71 BufferView::Pimpl::Pimpl(BufferView * b, LyXView * o,
72 int xpos, int ypos, int width, int height)
76 workarea = new WorkArea(bv_, xpos, ypos, width, height);
80 current_scrollbar_value = 0;
81 fl_set_timer(timer_cursor, 0.4);
83 work_area_focus = true;
85 using_xterm_cursor = false;
89 Painter & BufferView::Pimpl::painter()
91 return workarea->getPainter();
95 void BufferView::Pimpl::buffer(Buffer * b)
97 lyxerr[Debug::INFO] << "Setting buffer in BufferView ("
101 buffer_->delUser(bv_);
103 // Put the old text into the TextCache, but
104 // only if the buffer is still loaded.
105 // Also set the owner of the test to 0
107 textcache.add(bv_->text);
108 if (lyxerr.debugging())
109 textcache.show(lyxerr, "BufferView::buffer");
114 // Set current buffer
117 if (bufferlist.getState() == BufferList::CLOSING) return;
120 // screen is always deleted when the buffer is changed.
124 // If we are closing the buffer, use the first buffer as current
126 buffer_ = bufferlist.first();
130 lyxerr[Debug::INFO] << "Buffer addr: " << buffer_ << endl;
131 buffer_->addUser(bv_);
132 owner_->getMenus()->showMenus();
133 // If we don't have a text object for this, we make one
135 resizeCurrentBuffer();
140 screen->first = screen->TopCursorVisible();
142 updateAllVisibleBufferRelatedPopups();
145 lyxerr[Debug::INFO] << " No Buffer!" << endl;
146 owner_->getMenus()->hideMenus();
150 // Also remove all remaining text's from the testcache.
151 // (there should not be any!) (if there is any it is a
153 if (lyxerr.debugging())
154 textcache.show(lyxerr, "buffer delete all");
157 // should update layoutchoice even if we don't have a buffer.
158 owner_->updateLayoutChoice();
159 owner_->getMiniBuffer()->Init();
160 owner_->updateWindowTitle();
164 void BufferView::Pimpl::resize(int xpos, int ypos, int width, int height)
166 workarea->resize(xpos, ypos, width, height);
172 void BufferView::Pimpl::resize()
174 // This will resize the buffer. (Asger)
176 resizeCurrentBuffer();
180 void BufferView::Pimpl::redraw()
182 lyxerr[Debug::INFO] << "BufferView::redraw()" << endl;
187 bool BufferView::Pimpl::fitCursor()
189 Assert(screen); // it is a programming error to call fitCursor
190 // without a valid screen.
191 bool ret = screen->FitCursor();
192 if (ret) updateScrollbar();
197 void BufferView::Pimpl::redoCurrentBuffer()
199 lyxerr[Debug::INFO] << "BufferView::redoCurrentBuffer" << endl;
200 if (buffer_ && bv_->text) {
202 owner_->updateLayoutChoice();
207 int BufferView::Pimpl::resizeCurrentBuffer()
209 lyxerr[Debug::INFO] << "resizeCurrentBuffer" << endl;
211 LyXParagraph * par = 0;
212 LyXParagraph * selstartpar = 0;
213 LyXParagraph * selendpar = 0;
222 owner_->getMiniBuffer()->Set(_("Formatting document..."));
225 par = bv_->text->cursor.par;
226 pos = bv_->text->cursor.pos;
227 selstartpar = bv_->text->sel_start_cursor.par;
228 selstartpos = bv_->text->sel_start_cursor.pos;
229 selendpar = bv_->text->sel_end_cursor.par;
230 selendpos = bv_->text->sel_end_cursor.pos;
231 selection = bv_->text->selection;
232 mark_set = bv_->text->mark_set;
234 bv_->text = new LyXText(bv_, workarea->workWidth(), buffer_);
236 // See if we have a text in TextCache that fits
237 // the new buffer_ with the correct width.
238 bv_->text = textcache.findFit(buffer_, workarea->workWidth());
240 if (lyxerr.debugging()) {
241 lyxerr << "Found a LyXText that fits:\n";
242 textcache.show(lyxerr, bv_->text);
244 // Set the owner of the newly found text
245 bv_->text->owner(bv_);
246 if (lyxerr.debugging())
247 textcache.show(lyxerr, "resizeCurrentBuffer");
249 bv_->text = new LyXText(bv_, workarea->workWidth(), buffer_);
255 bv_->text->selection = true;
256 /* at this point just to avoid the Delete-Empty-Paragraph
257 * Mechanism when setting the cursor */
258 bv_->text->mark_set = mark_set;
260 bv_->text->SetCursor(selstartpar, selstartpos);
261 bv_->text->sel_cursor = bv_->text->cursor;
262 bv_->text->SetCursor(selendpar, selendpos);
263 bv_->text->SetSelection();
264 bv_->text->SetCursor(par, pos);
266 bv_->text->SetCursor(par, pos);
267 bv_->text->sel_cursor = bv_->text->cursor;
268 bv_->text->selection = false;
271 screen->first = screen->TopCursorVisible(); /* this will scroll the
272 * screen such that the
277 owner_->getMiniBuffer()->Init();
281 // Now if the title form still exist kill it
288 void BufferView::Pimpl::gotoError()
293 screen->HideCursor();
298 if (!bv_->text->GotoNextError()) {
299 if (bv_->text->cursor.pos
300 || bv_->text->cursor.par != bv_->text->FirstParagraph()) {
301 tmp = bv_->text->cursor;
302 bv_->text->cursor.par = bv_->text->FirstParagraph();
303 bv_->text->cursor.pos = 0;
304 if (!bv_->text->GotoNextError()) {
305 bv_->text->cursor = tmp;
306 owner_->getMiniBuffer()
307 ->Set(_("No more errors"));
311 owner_->getMiniBuffer()->Set(_("No more errors"));
316 bv_->text->sel_cursor = bv_->text->cursor;
320 void BufferView::Pimpl::updateScreen()
322 // Regenerate the screen.
324 screen = new LyXScreen(*workarea, bv_->text);
328 void BufferView::Pimpl::create_view()
337 timer_cursor = obj = fl_add_timer(FL_HIDDEN_TIMER,
338 0, 0, 0, 0, "Timer");
339 fl_set_object_callback(obj, C_BufferView_CursorToggleCB, 0);
344 void BufferView::Pimpl::updateScrollbar()
346 /* If the text is smaller than the working area, the scrollbar
347 * maximum must be the working area height. No scrolling will
351 workarea->setScrollbar(0, 1.0);
355 static unsigned long max2 = 0;
356 static unsigned long height2 = 0;
358 unsigned long cbth = 0;
362 cbth = bv_->text->height;
364 cbsf = screen->first;
366 // check if anything has changed.
368 height2 == workarea->height() &&
369 current_scrollbar_value == cbsf)
372 height2 = workarea->height();
373 current_scrollbar_value = cbsf;
375 if (cbth <= height2) { // text is smaller than screen
376 workarea->setScrollbar(0, 1.0); // right?
380 long maximum_height = workarea->height() * 3 / 4 + cbth;
384 double hfloat = workarea->height();
385 double maxfloat = maximum_height;
387 float slider_size = 0.0;
388 int slider_value = value;
390 workarea->setScrollbarBounds(0, bv_->text->height - workarea->height());
391 double lineh = bv_->text->DefaultHeight();
392 workarea->setScrollbarIncrements(lineh);
393 if (maxfloat > 0.0) {
394 if ((hfloat / maxfloat) * float(height2) < 3)
395 slider_size = 3.0/float(height2);
397 slider_size = hfloat / maxfloat;
399 slider_size = hfloat;
401 workarea->setScrollbar(slider_value, slider_size / workarea->height());
405 // Callback for scrollbar slider
406 void BufferView::Pimpl::scrollCB(double value)
408 extern bool cursor_follows_scrollbar;
410 if (buffer_ == 0) return;
412 current_scrollbar_value = long(value);
413 if (current_scrollbar_value < 0)
414 current_scrollbar_value = 0;
419 screen->Draw(current_scrollbar_value);
421 if (cursor_follows_scrollbar) {
422 LyXText * vbt = bv_->text;
423 unsigned int height = vbt->DefaultHeight();
425 if (vbt->cursor.y < screen->first + height) {
426 vbt->SetCursorFromCoordinates(0,
429 } else if (vbt->cursor.y >
430 screen->first + workarea->height() - height) {
431 vbt->SetCursorFromCoordinates(0,
441 int BufferView::Pimpl::scrollUp(long time)
443 if (buffer_ == 0) return 0;
444 if (!screen) return 0;
446 double value = workarea->getScrollbarValue();
448 if (value == 0) return 0;
450 float add_value = (bv_->text->DefaultHeight()
451 + float(time) * float(time) * 0.125);
453 if (add_value > workarea->height())
454 add_value = float(workarea->height() -
455 bv_->text->DefaultHeight());
462 workarea->setScrollbarValue(value);
464 bv_->scrollCB(value);
469 int BufferView::Pimpl::scrollDown(long time)
471 if (buffer_ == 0) return 0;
472 if (!screen) return 0;
474 double value= workarea->getScrollbarValue();
475 pair<float, float> p = workarea->getScrollbarBounds();
476 double max = p.second;
478 if (value == max) return 0;
480 float add_value = (bv_->text->DefaultHeight()
481 + float(time) * float(time) * 0.125);
483 if (add_value > workarea->height())
484 add_value = float(workarea->height() -
485 bv_->text->DefaultHeight());
492 workarea->setScrollbarValue(value);
494 bv_->scrollCB(value);
499 void BufferView::Pimpl::workAreaMotionNotify(int x, int y, unsigned int state)
501 // Only use motion with button 1
502 if (!(state & Button1MotionMask))
505 if (buffer_ == 0 || !screen) return;
507 // Check for inset locking
508 if (bv_->the_locking_inset) {
509 LyXCursor cursor = bv_->text->cursor;
510 bv_->the_locking_inset->
511 InsetMotionNotify(bv_,
513 y - cursor.y + screen->first,
518 /* The selection possible is needed, that only motion events are
519 * used, where the bottom press event was on the drawing area too */
520 if (selection_possible) {
521 screen->HideCursor();
523 bv_->text->SetCursorFromCoordinates(x, y + screen->first);
525 if (!bv_->text->selection)
526 update(-3); // Maybe an empty line was deleted
528 bv_->text->SetSelection();
529 screen->ToggleToggle();
531 screen->ShowCursor();
537 // Single-click on work area
538 void BufferView::Pimpl::workAreaButtonPress(int xpos, int ypos,
544 if (buffer_ == 0 || !screen) return;
546 Inset * inset_hit = checkInsetHit(xpos, ypos, button);
548 // ok ok, this is a hack.
549 if (button == 4 || button == 5) {
552 scrollUp(100); // This number is only temporary
560 if (bv_->the_locking_inset) {
561 // We are in inset locking mode
563 /* Check whether the inset was hit. If not reset mode,
564 otherwise give the event to the inset */
565 if (inset_hit == bv_->the_locking_inset) {
566 bv_->the_locking_inset->
567 InsetButtonPress(bv_,
572 bv_->unlockInset(bv_->the_locking_inset);
577 selection_possible = true;
578 screen->HideCursor();
580 // Right button mouse click on a table
582 (bv_->text->cursor.par->table ||
583 bv_->text->MouseHitInTable(xpos, ypos + screen->first))) {
584 // Set the cursor to the press-position
585 bv_->text->SetCursorFromCoordinates(xpos, ypos + screen->first);
588 // Only show the table popup if the hit is in
590 if (!bv_->text->HitInTable(bv_->text->cursor.row, xpos))
593 // Hit above or below the table?
595 if (!bv_->text->selection) {
596 screen->ToggleSelection();
597 bv_->text->ClearSelection();
598 bv_->text->FullRebreak();
602 // Popup table popup when on a table.
603 // This is obviously temporary, since we
604 // should be able to popup various
605 // context-sensitive-menus with the
606 // the right mouse. So this should be done more
607 // general in the future. Matthias.
608 selection_possible = false;
610 ->Dispatch(LFUN_LAYOUT_TABLE,
616 int screen_first = screen->first;
618 // Middle button press pastes if we have a selection
619 bool paste_internally = false;
621 && bv_->text->selection) {
622 owner_->getLyXFunc()->Dispatch(LFUN_COPY);
623 paste_internally = true;
626 // Clear the selection
627 screen->ToggleSelection();
628 bv_->text->ClearSelection();
629 bv_->text->FullRebreak();
633 // Single left click in math inset?
634 if ((inset_hit != 0) &&
635 (inset_hit->Editable()==Inset::HIGHLY_EDITABLE)) {
636 // Highly editable inset, like math
637 UpdatableInset * inset = static_cast<UpdatableInset *>(inset_hit);
638 selection_possible = false;
639 owner_->updateLayoutChoice();
640 owner_->getMiniBuffer()->Set(inset->EditMessage());
641 inset->InsetButtonPress(bv_, xpos, ypos, button);
642 inset->Edit(bv_, xpos, ypos, button);
646 // Right click on a footnote flag opens float menu
648 selection_possible = false;
652 if (!inset_hit) // otherwise it was already set in checkInsetHit(...)
653 bv_->text->SetCursorFromCoordinates(xpos, ypos + screen_first);
654 bv_->text->FinishUndo();
655 bv_->text->sel_cursor = bv_->text->cursor;
656 bv_->text->cursor.x_fix = bv_->text->cursor.x;
658 owner_->updateLayoutChoice();
660 selection_possible = false;
663 // Insert primary selection with middle mouse
664 // if there is a local selection in the current buffer,
667 if (paste_internally)
668 owner_->getLyXFunc()->Dispatch(LFUN_PASTE);
670 owner_->getLyXFunc()->Dispatch(LFUN_PASTESELECTION,
672 selection_possible = false;
678 void BufferView::Pimpl::doubleClick(int /*x*/, int /*y*/, unsigned int button)
681 if (buffer_ && !bv_->the_locking_inset) {
682 if (screen && button == 1) {
683 screen->HideCursor();
684 screen->ToggleSelection();
685 bv_->text->SelectWord();
686 screen->ToggleSelection(false);
687 /* This will fit the cursor on the screen
695 void BufferView::Pimpl::tripleClick(int /*x*/, int /*y*/, unsigned int button)
698 if (buffer_ && screen && button == 1) {
699 screen->HideCursor();
700 screen->ToggleSelection();
701 bv_->text->CursorHome();
702 bv_->text->sel_cursor = bv_->text->cursor;
703 bv_->text->CursorEnd();
704 bv_->text->SetSelection();
705 screen->ToggleSelection(false);
706 /* This will fit the cursor on the screen
713 void BufferView::Pimpl::enterView()
715 if (active() && available()) {
716 SetXtermCursor(workarea->getWin());
717 using_xterm_cursor = true;
722 void BufferView::Pimpl::leaveView()
724 if (using_xterm_cursor) {
725 XUndefineCursor(fl_display, workarea->getWin());
726 using_xterm_cursor = false;
731 void BufferView::Pimpl::workAreaButtonRelease(int x, int y,
734 if (buffer_ == 0 || screen == 0) return;
736 // If we hit an inset, we have the inset coordinates in these
737 // and inset_hit points to the inset. If we do not hit an
738 // inset, inset_hit is 0, and inset_x == x, inset_y == y.
739 Inset * inset_hit = checkInsetHit(x, y, button);
741 if (bv_->the_locking_inset) {
742 // We are in inset locking mode.
744 /* LyX does a kind of work-area grabbing for insets.
745 Only a ButtonPress Event outside the inset will
746 force a InsetUnlock. */
747 bv_->the_locking_inset->
748 InsetButtonRelease(bv_, x, y, button);
752 selection_possible = false;
753 if (bv_->text->cursor.par->table) {
754 int cell = bv_->text->
755 NumberOfCell(bv_->text->cursor.par,
756 bv_->text->cursor.pos);
757 if (bv_->text->cursor.par->table->IsContRow(cell) &&
758 bv_->text->cursor.par->table->
759 CellHasContRow(bv_->text->cursor.par->table->
760 GetCellAbove(cell))<0) {
761 bv_->text->CursorUp();
765 if (button >= 2) return;
768 owner_->getMiniBuffer()->Set(CurrentState(bv_));
770 // Did we hit an editable inset?
771 if (inset_hit != 0) {
772 // Inset like error, notes and figures
773 selection_possible = false;
775 // CHECK fix this proper in 0.13
777 // Following a ref shouldn't issue
778 // a push on the undo-stack
779 // anylonger, now that we have
780 // keybindings for following
781 // references and returning from
782 // references. IMHO though, it
783 // should be the inset's own business
784 // to push or not push on the undo
785 // stack. They don't *have* to
786 // alter the document...
788 // ...or maybe the SetCursorParUndo()
789 // below isn't necessary at all anylonger?
790 if (inset_hit->LyxCode() == Inset::REF_CODE) {
791 bv_->text->SetCursorParUndo();
794 owner_->getMiniBuffer()->Set(inset_hit->EditMessage());
795 if (inset_hit->Editable()==Inset::HIGHLY_EDITABLE) {
796 // Highly editable inset, like math
797 UpdatableInset *inset = (UpdatableInset *)inset_hit;
798 inset->InsetButtonRelease(bv_, x, y, button);
800 inset_hit->InsetButtonRelease(bv_, x, y, button);
801 inset_hit->Edit(bv_, x, y, button);
806 // check whether we want to open a float
810 if (bv_->text->cursor.pos <
811 bv_->text->cursor.par->Last()) {
812 c = bv_->text->cursor.par->
813 GetChar(bv_->text->cursor.pos);
815 if (c == LyXParagraph::META_FOOTNOTE
816 || c == LyXParagraph::META_MARGIN
817 || c == LyXParagraph::META_FIG
818 || c == LyXParagraph::META_TAB
819 || c == LyXParagraph::META_WIDE_FIG
820 || c == LyXParagraph::META_WIDE_TAB
821 || c == LyXParagraph::META_ALGORITHM){
823 } else if (bv_->text->cursor.pos - 1 >= 0) {
824 c = bv_->text->cursor.par->
825 GetChar(bv_->text->cursor.pos - 1);
826 if (c == LyXParagraph::META_FOOTNOTE
827 || c == LyXParagraph::META_MARGIN
828 || c == LyXParagraph::META_FIG
829 || c == LyXParagraph::META_TAB
830 || c == LyXParagraph::META_WIDE_FIG
831 || c == LyXParagraph::META_WIDE_TAB
832 || c == LyXParagraph::META_ALGORITHM){
833 // We are one step too far to the right
834 bv_->text->CursorLeft();
840 selection_possible = false;
845 // Do we want to close a float? (click on the float-label)
846 if (bv_->text->cursor.row->par->footnoteflag ==
847 LyXParagraph::OPEN_FOOTNOTE
848 && bv_->text->cursor.row->previous &&
849 bv_->text->cursor.row->previous->par->
850 footnoteflag != LyXParagraph::OPEN_FOOTNOTE){
851 LyXFont font(LyXFont::ALL_SANE);
852 font.setSize(LyXFont::SIZE_FOOTNOTE);
854 int box_x = 20; // LYX_PAPER_MARGIN;
855 box_x += lyxfont::width(" wide-tab ", font);
857 unsigned int screen_first = screen->first;
860 && y + screen_first > bv_->text->cursor.y -
861 bv_->text->cursor.row->baseline
862 && y + screen_first < bv_->text->cursor.y -
863 bv_->text->cursor.row->baseline
864 + lyxfont::maxAscent(font) * 1.2 + lyxfont::maxDescent(font) * 1.2) {
866 selection_possible = false;
871 // Maybe we want to edit a bibitem ale970302
872 if (bv_->text->cursor.par->bibkey && x < 20 +
873 bibitemMaxWidth(bv_->painter(),
876 params.textclass).defaultfont())) {
877 bv_->text->cursor.par->bibkey->Edit(bv_, 0, 0, 0);
885 * Returns an inset if inset was hit. 0 otherwise.
886 * If hit, the coordinates are changed relative to the inset.
887 * Otherwise coordinates are not changed, and false is returned.
889 Inset * BufferView::Pimpl::checkInsetHit(int & x, int & y,
890 unsigned int /* button */)
895 unsigned int y_tmp = y + screen->first;
898 bv_->text->SetCursorFromCoordinates(cursor, x, y_tmp);
899 #if 0 // Are you planning to use this Jürgen? (Lgb)
900 bool move_cursor = ((cursor.par != bv_->text->cursor.par) ||
901 (cursor.pos != bv_->text->cursor.pos));
903 if (cursor.pos < cursor.par->Last()
904 && cursor.par->GetChar(cursor.pos) == LyXParagraph::META_INSET
905 && cursor.par->GetInset(cursor.pos)
906 && cursor.par->GetInset(cursor.pos)->Editable()) {
908 // Check whether the inset really was hit
909 Inset * tmpinset = cursor.par->GetInset(cursor.pos);
910 LyXFont font = bv_->text->GetFont(cursor.par, cursor.pos);
911 bool is_rtl = font.isVisibleRightToLeft();
915 start_x = cursor.x - tmpinset->width(bv_->painter(), font);
919 end_x = cursor.x + tmpinset->width(bv_->painter(), font);
922 if (x > start_x && x < end_x
923 && y_tmp > cursor.y - tmpinset->ascent(bv_->painter(), font)
924 && y_tmp < cursor.y + tmpinset->descent(bv_->painter(), font)) {
926 if (move_cursor && (tmpinset != bv_->the_locking_inset))
928 bv_->text->SetCursor(cursor.par,cursor.pos,true);
930 // The origin of an inset is on the baseline
931 y = y_tmp - (bv_->text->cursor.y);
936 if ((cursor.pos - 1 >= 0) &&
937 (cursor.par->GetChar(cursor.pos-1) == LyXParagraph::META_INSET) &&
938 (cursor.par->GetInset(cursor.pos - 1)) &&
939 (cursor.par->GetInset(cursor.pos - 1)->Editable())) {
940 Inset * tmpinset = cursor.par->GetInset(cursor.pos-1);
941 LyXFont font = bv_->text->GetFont(cursor.par, cursor.pos-1);
942 bool is_rtl = font.isVisibleRightToLeft();
946 start_x = cursor.x - tmpinset->width(bv_->painter(), font);
950 end_x = cursor.x + tmpinset->width(bv_->painter(), font);
952 if (x > start_x && x < end_x
953 && y_tmp > cursor.y - tmpinset->ascent(bv_->painter(), font)
954 && y_tmp < cursor.y + tmpinset->descent(bv_->painter(), font)) {
956 if (move_cursor && (tmpinset != bv_->the_locking_inset))
958 bv_->text->SetCursor(cursor.par,cursor.pos-1,true);
960 // The origin of an inset is on the baseline
961 y = y_tmp - (bv_->text->cursor.y);
969 void BufferView::Pimpl::workAreaExpose()
971 // this is a hack to ensure that we only call this through
972 // BufferView::redraw().
977 static unsigned int work_area_width = 0;
978 static unsigned int work_area_height = 0;
980 bool widthChange = workarea->workWidth() != work_area_width;
981 bool heightChange = workarea->height() != work_area_height;
983 // update from work area
984 work_area_width = workarea->workWidth();
985 work_area_height = workarea->height();
988 // All buffers need a resize
991 // Remove all texts from the textcache
992 // This is not _really_ what we want to do. What
993 // we really want to do is to delete in textcache
994 // that does not have a BufferView with matching
995 // width, but as long as we have only one BufferView
996 // deleting all gives the same result.
997 if (lyxerr.debugging())
998 textcache.show(lyxerr, "Expose delete all");
1000 } else if (heightChange) {
1001 // Rebuild image of current screen
1003 // fitCursor() ensures we don't jump back
1004 // to the start of the document on vertical
1008 // The main window size has changed, repaint most stuff
1010 // ...including the minibuffer
1011 owner_->getMiniBuffer()->Init();
1013 } else if (screen) screen->Redraw();
1015 // Grey box when we don't have a buffer
1016 workarea->greyOut();
1019 // always make sure that the scrollbar is sane.
1021 owner_->updateLayoutChoice();
1026 #ifndef XFORMS_CLIPBOARD
1028 string fromClipboard(Window win, XEvent * event)
1031 if (event->xselection.type == XA_STRING
1032 && event->xselection.property) {
1036 unsigned char * uc = 0;
1038 if (XGetWindowProperty(
1039 event->xselection.display, // display
1041 event->xselection.property, // property
1045 XA_STRING, // req_type
1046 &tmpatom, // actual_type_return
1047 &tmpint, // actual_format_return
1058 if (XGetWindowProperty(
1059 event->xselection.display, // display
1061 event->xselection.property, // property
1063 ul2/4+1, // long_length
1065 XA_STRING, // req_type
1066 &tmpatom, // actual_type_return
1067 &tmpint, // actual_format_return
1068 &ul1, // nitems_return
1069 &ul2, // bytes_after_return
1070 &uc // prop_return */
1075 strret = reinterpret_cast<char*>(uc);
1076 free(uc); // yes free!
1084 void BufferView::Pimpl::workAreaSelectionNotify(Window win, XEvent * event)
1086 if (buffer_ == 0) return;
1088 screen->HideCursor();
1089 bv_->beforeChange();
1090 string clb = fromClipboard(win, event);
1093 bv_->text->InsertStringA(clb);
1095 bv_->text->InsertStringB(clb);
1103 void BufferView::Pimpl::update()
1105 if (screen) screen->Update();
1108 // Values used when calling update:
1110 // -2 - update, move sel_cursor if selection, fitcursor
1111 // -1 - update, move sel_cursor if selection, fitcursor, mark dirty
1112 // 0 - update, move sel_cursor if selection, fitcursor
1113 // 1 - update, move sel_cursor if selection, fitcursor, mark dirty
1114 // 3 - update, move sel_cursor if selection
1117 // a simple redraw of the parts that need refresh
1119 // move sel_cursor if selection -
1120 // the text's sel_cursor is moved if there is selection is progress
1123 // fitCursor() is called and the scrollbar updated
1126 // the buffer is marked dirty.
1135 // UPDATE_ONLY = UPDATE;
1136 // UPDATE_SELECT = UPDATE | SELECT;
1137 // UPDATE_SELECT_MOVE = UPDATE | SELECT | FITCUR;
1138 // UPDATE_SELECT_MOVE_AFTER_CHANGE = UPDATE | SELECT | FITCUR | CHANGE;
1140 // update(-3) -> update(0) -> update(0) -> update(UPDATE)
1141 // update(-2) -> update(1 + 2) -> update(3) -> update(SELECT|FITCUR)
1142 // update(-1) -> update(1 + 2 + 4) -> update(7) -> update(SELECT|FITCUR|CHANGE)
1143 // update(1) -> update(1 + 2 + 4) -> update(7) -> update(SELECT|FITCUR|CHANGE)
1144 // update(3) -> update(1) -> update(1) -> update(SELECT)
1145 void BufferView::Pimpl::update(signed char f)
1148 owner_->updateLayoutChoice();
1150 if (!bv_->text->selection && f > -3)
1151 bv_->text->sel_cursor = bv_->text->cursor;
1154 bv_->text->FullRebreak();
1158 if (f != 3 && f != -3) {
1160 //updateScrollbar();
1163 if (f == 1 || f == -1) {
1164 if (buffer_->isLyxClean()) {
1165 buffer_->markDirty();
1166 owner_->getMiniBuffer()->setTimer(4);
1168 buffer_->markDirty();
1172 owner_->updateLayoutChoice();
1174 if (!bv_->text->selection && (f & SELECT))
1175 bv_->text->sel_cursor = bv_->text->cursor;
1178 bv_->text->FullRebreak();
1184 //updateScrollbar();
1188 if (buffer_->isLyxClean()) {
1189 buffer_->markDirty();
1190 owner_->getMiniBuffer()->setTimer(4);
1192 buffer_->markDirty();
1199 // Callback for cursor timer
1200 void BufferView::Pimpl::cursorToggle()
1202 // Quite a nice place for asyncron Inset updating, isn't it?
1203 // Actually no! This is run even if no buffer exist... so (Lgb)
1205 goto set_timer_and_return;
1209 // On my quest to solve the gs render hangups I am now
1210 // disabling the SIGHUP completely, and will do a wait
1211 // now and then instead. If the guess that xforms somehow
1212 // destroys something is true, this is likely (hopefully)
1213 // to solve the problem...at least I hope so. Lgb
1215 // ...Ok this seems to work...at least it does not make things
1216 // worse so far. However I still see gs processes that hangs.
1217 // I would really like to know _why_ they are hanging. Anyway
1218 // the solution without the SIGCHLD handler seems to be easier
1221 // When attaching gdb to a a running gs that hangs it shows
1222 // that it is waiting for input(?) Is it possible for us to
1223 // provide that input somehow? Or figure what it is expecing
1226 // One solution is to, after some time, look if there are some
1227 // old gs processes still running and if there are: kill them
1230 // Another solution is to provide the user an option to rerender
1231 // a picture. This would, for the picture in question, check if
1232 // there is a gs running for it, if so kill it, and start a new
1233 // rendering process.
1235 // these comments posted to lyx@via
1238 int pid = waitpid(static_cast<pid_t>(0), &status, WNOHANG);
1239 if (pid == -1) // error find out what is wrong
1240 ; // ignore it for now.
1242 sigchldhandler(pid, &status);
1245 updatelist.update(bv_);
1248 goto set_timer_and_return;
1251 if (lyx_focus && work_area_focus) {
1252 if (!bv_->the_locking_inset) {
1253 screen->CursorToggle();
1255 bv_->the_locking_inset->
1256 ToggleInsetCursor(bv_);
1258 goto set_timer_and_return;
1260 // Make sure that the cursor is visible.
1261 if (!bv_->the_locking_inset) {
1262 screen->ShowCursor();
1264 if (!bv_->the_locking_inset->isCursorVisible())
1265 bv_->the_locking_inset->
1266 ToggleInsetCursor(bv_);
1268 // This is only run when work_area_focus or lyx_focus is false.
1271 XGetInputFocus(fl_display, &tmpwin, &tmp);
1272 // Commenting this out, we have not had problems with this
1273 // for a long time. We will probably work on this code later
1274 // and we can reenable this debug code then. Now it only
1275 // anoying when debugging. (Lgb)
1276 //if (lyxerr.debugging(Debug::INFO)) {
1277 // lyxerr << "tmpwin: " << tmpwin
1278 // << "\nwindow: " << view->owner_->getForm()->window
1279 // << "\nwork_area_focus: " << view->work_area_focus
1280 // << "\nlyx_focus : " << view->lyx_focus
1283 if (tmpwin != owner_->getForm()->window) {
1288 if (!work_area_focus)
1291 goto set_timer_and_return;
1295 set_timer_and_return:
1296 fl_set_timer(timer_cursor, 0.4);
1302 void BufferView::Pimpl::cursorPrevious()
1304 if (!bv_->text->cursor.row->previous) return;
1306 long y = screen->first;
1307 Row * cursorrow = bv_->text->cursor.row;
1308 bv_->text->SetCursorFromCoordinates(bv_->text->cursor.x_fix, y);
1309 bv_->text->FinishUndo();
1310 // This is to allow jumping over large insets
1311 if ((cursorrow == bv_->text->cursor.row))
1312 bv_->text->CursorUp();
1314 if (bv_->text->cursor.row->height < workarea->height())
1315 screen->Draw(bv_->text->cursor.y
1316 - bv_->text->cursor.row->baseline
1317 + bv_->text->cursor.row->height
1318 - workarea->height() + 1 );
1323 void BufferView::Pimpl::cursorNext()
1325 if (!bv_->text->cursor.row->next) return;
1327 long y = screen->first;
1328 bv_->text->GetRowNearY(y);
1329 Row * cursorrow = bv_->text->cursor.row;
1330 bv_->text->SetCursorFromCoordinates(bv_->text->cursor.x_fix, y
1331 + workarea->height());
1332 bv_->text->FinishUndo();
1333 // This is to allow jumping over large insets
1334 if ((cursorrow == bv_->text->cursor.row))
1335 bv_->text->CursorDown();
1337 if (bv_->text->cursor.row->height < workarea->height())
1338 screen->Draw(bv_->text->cursor.y
1339 - bv_->text->cursor.row->baseline);
1344 bool BufferView::Pimpl::available() const
1346 if (buffer_ && bv_->text) return true;
1351 void BufferView::Pimpl::beforeChange()
1354 bv_->text->ClearSelection();
1359 void BufferView::Pimpl::savePosition()
1361 backstack.push(buffer_->fileName(),
1362 bv_->text->cursor.x,
1363 bv_->text->cursor.y);
1367 void BufferView::Pimpl::restorePosition()
1369 if (backstack.empty()) return;
1372 string fname = backstack.pop(&x, &y);
1375 Buffer * b = bufferlist.exists(fname) ?
1376 bufferlist.getBuffer(fname) :
1377 bufferlist.loadLyXFile(fname); // don't ask, just load it
1379 bv_->text->SetCursorFromCoordinates(x, y);
1384 void BufferView::Pimpl::setState()
1386 if (!lyxrc.rtl_support)
1389 if (bv_->text->real_current_font.isRightToLeft() &&
1390 bv_->text->real_current_font.latex() != LyXFont::ON) {
1391 if (owner_->getIntl()->primarykeymap)
1392 owner_->getIntl()->KeyMapSec();
1394 if (!owner_->getIntl()->primarykeymap)
1395 owner_->getIntl()->KeyMapPrim();
1400 void BufferView::Pimpl::insetSleep()
1402 if (bv_->the_locking_inset && !bv_->inset_slept) {
1403 bv_->the_locking_inset->GetCursorPos(bv_->slx, bv_->sly);
1404 bv_->the_locking_inset->InsetUnlock(bv_);
1405 bv_->inset_slept = true;
1410 void BufferView::Pimpl::insetWakeup()
1412 if (bv_->the_locking_inset && bv_->inset_slept) {
1413 bv_->the_locking_inset->Edit(bv_, bv_->slx, bv_->sly, 0);
1414 bv_->inset_slept = false;
1419 void BufferView::Pimpl::insetUnlock()
1421 if (bv_->the_locking_inset) {
1422 if (!bv_->inset_slept) bv_->the_locking_inset->InsetUnlock(bv_);
1423 bv_->the_locking_inset = 0;
1424 bv_->text->FinishUndo();
1425 bv_->inset_slept = false;
1430 bool BufferView::Pimpl::focus() const
1432 return workarea->hasFocus();
1436 void BufferView::Pimpl::focus(bool f)
1438 if (f) workarea->setFocus();
1442 bool BufferView::Pimpl::active() const
1444 return workarea->active();
1448 bool BufferView::Pimpl::belowMouse() const
1450 return workarea->belowMouse();
1454 void BufferView::Pimpl::showCursor()
1457 screen->ShowCursor();
1461 void BufferView::Pimpl::hideCursor()
1464 screen->HideCursor();
1468 void BufferView::Pimpl::toggleSelection(bool b)
1471 screen->ToggleSelection(b);
1475 void BufferView::Pimpl::toggleToggle()
1478 screen->ToggleToggle();
1482 void BufferView::Pimpl::center()
1485 if (bv_->text->cursor.y > workarea->height() / 2) {
1486 screen->Draw(bv_->text->cursor.y - workarea->height() / 2);
1495 #ifdef XFORMS_CLIPBOARD
1496 void BufferView::Pimpl::pasteClipboard(bool asPara)
1498 if (buffer_ == 0) return;
1500 screen->HideCursor();
1501 bv_->beforeChange();
1503 string clip(workarea->getClipboard());
1505 if (clip.empty()) return;
1508 bv_->text->InsertStringB(clip);
1510 bv_->text->InsertStringA(clip);
1516 void BufferView::Pimpl::stuffClipboard(string const & stuff) const
1518 workarea->putClipboard(stuff);