2 * \file BufferView_pimpl.C
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Asger Alstrup
7 * \author Alfredo Braustein
8 * \author Lars Gullik Bjønnes
9 * \author Jean-Marc Lasgouttes
10 * \author Angus Leeming
12 * \author André Pönitz
14 * \author Jürgen Vigna
16 * Full author contact details are available in file CREDITS.
21 #include "BufferView_pimpl.h"
23 #include "buffer_funcs.h"
24 #include "bufferlist.h"
25 #include "bufferparams.h"
28 #include "dispatchresult.h"
30 #include "FloatList.h"
31 #include "funcrequest.h"
34 #include "iterators.h"
35 #include "lyx_cb.h" // added for Dispatch functions
41 #include "lastfiles.h"
42 #include "paragraph.h"
43 #include "paragraph_funcs.h"
44 #include "ParagraphParameters.h"
48 #include "insets/insetfloatlist.h"
49 #include "insets/insetref.h"
51 #include "frontends/Alert.h"
52 #include "frontends/Dialogs.h"
53 #include "frontends/FileDialog.h"
54 #include "frontends/LyXView.h"
55 #include "frontends/LyXScreenFactory.h"
56 #include "frontends/screen.h"
57 #include "frontends/WorkArea.h"
58 #include "frontends/WorkAreaFactory.h"
60 #include "graphics/Previews.h"
62 #include "mathed/formulabase.h"
64 #include "support/filetools.h"
65 #include "support/globbing.h"
66 #include "support/path_defines.h"
67 #include "support/tostr.h"
69 #include <boost/bind.hpp>
71 using bv_funcs::currentState;
75 using lyx::support::AddPath;
76 using lyx::support::bformat;
77 using lyx::support::FileFilterList;
78 using lyx::support::FileSearch;
79 using lyx::support::IsDirWriteable;
80 using lyx::support::MakeDisplayPath;
81 using lyx::support::strToUnsignedInt;
82 using lyx::support::system_lyxdir;
90 extern BufferList bufferlist;
95 unsigned int const saved_positions_num = 20;
97 // All the below connection objects are needed because of a bug in some
98 // versions of GCC (<=2.96 are on the suspects list.) By having and assigning
99 // to these connections we avoid a segfault upon startup, and also at exit.
102 boost::signals::connection dispatchcon;
103 boost::signals::connection timecon;
104 boost::signals::connection doccon;
105 boost::signals::connection resizecon;
106 boost::signals::connection kpresscon;
107 boost::signals::connection selectioncon;
108 boost::signals::connection lostcon;
114 BufferView::Pimpl::Pimpl(BufferView & bv, LyXView * owner,
115 int xpos, int ypos, int width, int height)
116 : bv_(&bv), owner_(owner), buffer_(0), cursor_timeout(400),
117 using_xterm_cursor(false), cursor_(bv)
119 xsel_cache_.set = false;
121 workarea_.reset(WorkAreaFactory::create(xpos, ypos, width, height));
122 screen_.reset(LyXScreenFactory::create(workarea()));
125 doccon = workarea().scrollDocView
126 .connect(boost::bind(&BufferView::Pimpl::scrollDocView, this, _1));
127 resizecon = workarea().workAreaResize
128 .connect(boost::bind(&BufferView::Pimpl::workAreaResize, this));
129 dispatchcon = workarea().dispatch
130 .connect(boost::bind(&BufferView::Pimpl::workAreaDispatch, this, _1));
131 kpresscon = workarea().workAreaKeyPress
132 .connect(boost::bind(&BufferView::Pimpl::workAreaKeyPress, this, _1, _2));
133 selectioncon = workarea().selectionRequested
134 .connect(boost::bind(&BufferView::Pimpl::selectionRequested, this));
135 lostcon = workarea().selectionLost
136 .connect(boost::bind(&BufferView::Pimpl::selectionLost, this));
138 timecon = cursor_timeout.timeout
139 .connect(boost::bind(&BufferView::Pimpl::cursorToggle, this));
140 cursor_timeout.start();
141 saved_positions.resize(saved_positions_num);
145 void BufferView::Pimpl::addError(ErrorItem const & ei)
147 errorlist_.push_back(ei);
151 void BufferView::Pimpl::showReadonly(bool)
153 owner_->updateWindowTitle();
154 owner_->getDialogs().updateBufferDependent(false);
158 void BufferView::Pimpl::connectBuffer(Buffer & buf)
160 if (errorConnection_.connected())
164 buf.error.connect(boost::bind(&BufferView::Pimpl::addError, this, _1));
166 buf.message.connect(boost::bind(&LyXView::message, owner_, _1));
168 buf.busy.connect(boost::bind(&LyXView::busy, owner_, _1));
170 buf.updateTitles.connect(boost::bind(&LyXView::updateWindowTitle, owner_));
172 buf.resetAutosaveTimers.connect(boost::bind(&LyXView::resetAutosaveTimer, owner_));
173 readonlyConnection_ =
174 buf.readonly.connect(boost::bind(&BufferView::Pimpl::showReadonly, this, _1));
176 buf.closing.connect(boost::bind(&BufferView::Pimpl::buffer, this, (Buffer *)0));
180 void BufferView::Pimpl::disconnectBuffer()
182 errorConnection_.disconnect();
183 messageConnection_.disconnect();
184 busyConnection_.disconnect();
185 titleConnection_.disconnect();
186 timerConnection_.disconnect();
187 readonlyConnection_.disconnect();
188 closingConnection_.disconnect();
192 bool BufferView::Pimpl::newFile(string const & filename,
193 string const & tname,
196 Buffer * b = ::newFile(filename, tname, isNamed);
202 bool BufferView::Pimpl::loadLyXFile(string const & filename, bool tolastfiles)
204 // get absolute path of file and add ".lyx" to the filename if
206 string s = FileSearch(string(), filename, "lyx");
208 bool const found = !s.empty();
213 // file already open?
214 if (bufferlist.exists(s)) {
215 string const file = MakeDisplayPath(s, 20);
216 string text = bformat(_("The document %1$s is already "
217 "loaded.\n\nDo you want to revert "
218 "to the saved version?"), file);
219 int const ret = Alert::prompt(_("Revert to saved document?"),
220 text, 0, 1, _("&Revert"), _("&Switch to document"));
223 buffer(bufferlist.getBuffer(s));
226 // FIXME: should be LFUN_REVERT
227 if (!bufferlist.close(bufferlist.getBuffer(s), false))
229 // Fall through to new load. (Asger)
236 b = bufferlist.newBuffer(s);
238 if (!::loadLyXFile(b, s)) {
239 bufferlist.release(b);
243 string text = bformat(_("The document %1$s does not yet "
244 "exist.\n\nDo you want to create "
245 "a new document?"), s);
246 int const ret = Alert::prompt(_("Create new document?"),
247 text, 0, 1, _("&Create"), _("Cancel"));
250 b = ::newFile(s, string(), true);
256 bv_->showErrorList(_("Parse"));
259 LyX::ref().lastfiles().newFile(b->fileName());
265 WorkArea & BufferView::Pimpl::workarea() const
267 return *workarea_.get();
271 LyXScreen & BufferView::Pimpl::screen() const
273 return *screen_.get();
277 Painter & BufferView::Pimpl::painter() const
279 return workarea().getPainter();
283 void BufferView::Pimpl::top_y(int y)
289 int BufferView::Pimpl::top_y() const
295 void BufferView::Pimpl::buffer(Buffer * b)
297 lyxerr[Debug::INFO] << "Setting buffer in BufferView ("
301 //delete bv_->text();
305 // set current buffer
310 // if we're quitting lyx, don't bother updating stuff
314 // if we are closing the buffer, use the first buffer as current
316 buffer_ = bufferlist.first();
319 lyxerr[Debug::INFO] << "Buffer addr: " << buffer_ << endl;
320 connectBuffer(*buffer_);
322 buffer_->text().init(bv_);
323 buffer_->text().textwidth_ = workarea().workWidth();
324 buffer_->text().fullRebreak();
326 // If we don't have a text object for this, we make one
327 if (bv_->text() == 0)
328 resizeCurrentBuffer();
330 // FIXME: needed when ?
333 // Buffer-dependent dialogs should be updated or
334 // hidden. This should go here because some dialogs (eg ToC)
335 // require bv_->text.
336 owner_->getDialogs().updateBufferDependent(true);
338 lyxerr[Debug::INFO] << " No Buffer!" << endl;
339 owner_->getDialogs().hideBufferDependent();
344 owner_->updateMenubar();
345 owner_->updateToolbar();
346 owner_->updateLayoutChoice();
347 owner_->updateWindowTitle();
349 // Don't forget to update the Layout
351 owner_->setLayout(bv_->text()->cursorPar()->layout()->name());
353 if (lyx::graphics::Previews::activated() && buffer_)
354 lyx::graphics::Previews::get().generateBufferPreviews(*buffer_);
358 bool BufferView::Pimpl::fitCursor()
360 lyxerr << "BufferView::Pimpl::fitCursor." << endl;
361 if (screen().fitCursor(bv_)) {
369 void BufferView::Pimpl::redoCurrentBuffer()
371 lyxerr[Debug::INFO] << "BufferView::redoCurrentBuffer" << endl;
372 if (buffer_ && bv_->text()) {
373 resizeCurrentBuffer();
375 owner_->updateLayoutChoice();
380 void BufferView::Pimpl::resizeCurrentBuffer()
382 lyxerr[Debug::INFO] << "resizeCurrentBuffer" << endl;
385 int selstartpar = -1;
389 pos_type selstartpos = 0;
390 pos_type selendpos = 0;
392 bool mark_set = false;
396 owner_->message(_("Formatting document..."));
398 LyXText * text = bv_->text();
399 lyxerr << "### resizeCurrentBuffer: text " << text << endl;
403 par = bv_->cursor().par();
404 pos = bv_->cursor().pos();
405 selstartpar = bv_->selStart().par();
406 selstartpos = bv_->selStart().pos();
407 selendpar = bv_->selEnd().par();
408 selendpos = bv_->selEnd().pos();
409 sel = bv_->selection().set();
410 mark_set = bv_->selection().mark();
411 text->textwidth_ = bv_->workWidth();
416 bv_->selection().set(true);
417 // At this point just to avoid the Delete-Empty-Paragraph-
418 // Mechanism when setting the cursor.
419 bv_->selection().mark(mark_set);
421 text->setCursor(selstartpar, selstartpos);
423 text->setCursor(selendpar, selendpos);
425 text->setCursor(par, pos);
427 text->setCursor(par, pos);
429 bv_->selection().set(false);
438 // reset the "Formatting..." message
439 owner_->clearMessage();
445 void BufferView::Pimpl::updateScrollbar()
448 lyxerr[Debug::GUI] << "no text in updateScrollbar" << endl;
449 workarea().setScrollbarParams(0, 0, 0);
453 LyXText const & t = *bv_->text();
455 lyxerr[Debug::GUI] << "Updating scrollbar: h " << t.height << ", top_y() "
456 << top_y() << ", default height " << defaultRowHeight() << endl;
458 workarea().setScrollbarParams(t.height, top_y(), defaultRowHeight());
462 void BufferView::Pimpl::scrollDocView(int value)
464 lyxerr[Debug::GUI] << "scrollDocView of " << value << endl;
469 screen().hideCursor();
472 screen().redraw(*bv_);
474 if (!lyxrc.cursor_follows_scrollbar)
477 int const height = defaultRowHeight();
478 int const first = top_y() + height;
479 int const last = top_y() + workarea().workHeight() - height;
481 LyXText * text = bv_->text();
482 if (text->cursorY() < first)
483 text->setCursorFromCoordinates(0, first);
484 else if (text->cursorY() > last)
485 text->setCursorFromCoordinates(0, last);
487 owner_->updateLayoutChoice();
491 void BufferView::Pimpl::scroll(int lines)
496 LyXText const * t = bv_->text();
497 int const line_height = defaultRowHeight();
499 // The new absolute coordinate
500 int new_top_y = top_y() + lines * line_height;
502 // Restrict to a valid value
503 new_top_y = std::min(t->height - 4 * line_height, new_top_y);
504 new_top_y = std::max(0, new_top_y);
506 scrollDocView(new_top_y);
508 // Update the scrollbar.
509 workarea().setScrollbarParams(t->height, top_y(), defaultRowHeight());
513 void BufferView::Pimpl::workAreaKeyPress(LyXKeySymPtr key,
514 key_modifier::state state)
516 bv_->owner()->getLyXFunc().processKeySym(key, state);
518 /* This is perhaps a bit of a hack. When we move
519 * around, or type, it's nice to be able to see
520 * the cursor immediately after the keypress. So
521 * we reset the toggle timeout and force the visibility
522 * of the cursor. Note we cannot do this inside
523 * dispatch() itself, because that's called recursively.
526 cursor_timeout.restart();
527 screen().showCursor(*bv_);
532 void BufferView::Pimpl::selectionRequested()
539 LyXText * text = bv_->getLyXText();
541 if (!bv_->selection().set()) {
542 xsel_cache_.set = false;
546 if (!xsel_cache_.set ||
547 bv_->cursor() != xsel_cache_.cursor ||
548 bv_->anchor() != xsel_cache_.anchor)
550 xsel_cache_.cursor = bv_->cursor();
551 xsel_cache_.anchor = bv_->anchor();
552 xsel_cache_.set = bv_->selection().set();
553 sel = text->selectionAsString(*bv_->buffer(), false);
555 workarea().putClipboard(sel);
560 void BufferView::Pimpl::selectionLost()
563 screen().hideCursor();
564 bv_->clearSelection();
565 xsel_cache_.set = false;
570 void BufferView::Pimpl::workAreaResize()
572 static int work_area_width;
573 static int work_area_height;
575 bool const widthChange = workarea().workWidth() != work_area_width;
576 bool const heightChange = workarea().workHeight() != work_area_height;
578 // update from work area
579 work_area_width = workarea().workWidth();
580 work_area_height = workarea().workHeight();
584 // The visible LyXView need a resize
585 resizeCurrentBuffer();
589 if (widthChange || heightChange)
592 // always make sure that the scrollbar is sane.
594 owner_->updateLayoutChoice();
598 void BufferView::Pimpl::update()
600 //lyxerr << "BufferView::update()" << endl;
601 // fix cursor coordinate cache in case something went wrong
603 // check needed to survive LyX startup
604 if (bv_->getLyXText()) {
605 // update all 'visible' paragraphs
606 ParagraphList::iterator beg;
607 ParagraphList::iterator end;
608 getParsInRange(buffer_->paragraphs(),
609 top_y(), top_y() + workarea().workHeight(),
611 bv_->text()->redoParagraphs(beg, end);
614 screen().redraw(*bv_);
618 // Callback for cursor timer
619 void BufferView::Pimpl::cursorToggle()
622 cursor_timeout.restart();
626 screen().toggleCursor(*bv_);
627 cursor_timeout.restart();
631 bool BufferView::Pimpl::available() const
633 return buffer_ && bv_->text();
637 Change const BufferView::Pimpl::getCurrentChange()
639 if (!bv_->buffer()->params().tracking_changes)
640 return Change(Change::UNCHANGED);
642 LyXText * text = bv_->getLyXText();
644 if (!bv_->selection().set())
645 return Change(Change::UNCHANGED);
647 return text->getPar(bv_->selStart())
648 ->lookupChangeFull(bv_->selStart().pos());
652 void BufferView::Pimpl::savePosition(unsigned int i)
654 if (i >= saved_positions_num)
656 saved_positions[i] = Position(buffer_->fileName(),
657 bv_->text()->cursorPar()->id(),
658 bv_->text()->cursor().pos());
660 owner_->message(bformat(_("Saved bookmark %1$s"), tostr(i)));
664 void BufferView::Pimpl::restorePosition(unsigned int i)
666 if (i >= saved_positions_num)
669 string const fname = saved_positions[i].filename;
671 bv_->clearSelection();
673 if (fname != buffer_->fileName()) {
675 if (bufferlist.exists(fname))
676 b = bufferlist.getBuffer(fname);
678 b = bufferlist.newBuffer(fname);
679 ::loadLyXFile(b, fname); // don't ask, just load it
685 ParIterator par = buffer_->getParFromID(saved_positions[i].par_id);
686 if (par == buffer_->par_iterator_end())
689 bv_->text()->setCursor(par.pit(),
690 min(par->size(), saved_positions[i].par_pos));
693 owner_->message(bformat(_("Moved to bookmark %1$s"), tostr(i)));
697 bool BufferView::Pimpl::isSavedPosition(unsigned int i)
699 return i < saved_positions_num && !saved_positions[i].filename.empty();
703 void BufferView::Pimpl::switchKeyMap()
705 if (!lyxrc.rtl_support)
708 Intl & intl = owner_->getIntl();
709 if (bv_->getLyXText()->real_current_font.isRightToLeft()) {
710 if (intl.keymap == Intl::PRIMARY)
713 if (intl.keymap == Intl::SECONDARY)
719 void BufferView::Pimpl::center()
721 LyXText * text = bv_->text();
723 bv_->clearSelection();
724 int const half_height = workarea().workHeight() / 2;
725 int new_y = std::max(0, text->cursorY() - half_height);
727 // FIXME: look at this comment again ...
728 // This updates top_y() but means the fitCursor() call
729 // from the update(FITCUR) doesn't realise that we might
730 // have moved (e.g. from GOTOPARAGRAPH), so doesn't cause
731 // the scrollbar to be updated as it should, so we have
732 // to do it manually. Any operation that does a center()
733 // and also might have moved top_y() must make sure to call
734 // updateScrollbar() currently. Never mind that this is a
735 // pretty obfuscated way of updating t->top_y()
740 void BufferView::Pimpl::stuffClipboard(string const & stuff) const
742 workarea().putClipboard(stuff);
746 InsetOld * BufferView::Pimpl::getInsetByCode(InsetOld::Code code)
749 CursorSlice cursor = bv_->getLyXText()->cursor;
750 Buffer::inset_iterator it =
751 find_if(Buffer::inset_iterator(
752 cursorPar(), cursor().pos()),
753 buffer_->inset_iterator_end(),
754 lyx::compare_memfun(&Inset::lyxCode, code));
755 return it != buffer_->inset_iterator_end() ? (*it) : 0;
757 // Ok, this is a little bit too brute force but it
758 // should work for now. Better infrastructure is coming. (Lgb)
760 Buffer * b = bv_->buffer();
761 LyXText * text = bv_->getLyXText();
763 Buffer::inset_iterator beg = b->inset_iterator_begin();
764 Buffer::inset_iterator end = b->inset_iterator_end();
766 bool cursor_par_seen = false;
768 for (; beg != end; ++beg) {
769 if (beg.getPar() == text->cursorPar()) {
770 cursor_par_seen = true;
772 if (cursor_par_seen) {
773 if (beg.getPar() == text->cursorPar()
774 && beg.getPos() >= text->cursor().pos()) {
776 } else if (beg.getPar() != text->cursorPar()) {
783 // Now find the first inset that matches code.
784 for (; beg != end; ++beg) {
785 if (beg->lyxCode() == code) {
795 void BufferView::Pimpl::MenuInsertLyXFile(string const & filen)
797 string filename = filen;
799 if (filename.empty()) {
800 // Launch a file browser
801 string initpath = lyxrc.document_path;
804 string const trypath = owner_->buffer()->filePath();
805 // If directory is writeable, use this as default.
806 if (IsDirWriteable(trypath))
810 FileDialog fileDlg(_("Select LyX document to insert"),
812 make_pair(string(_("Documents|#o#O")),
813 string(lyxrc.document_path)),
814 make_pair(string(_("Examples|#E#e")),
815 string(AddPath(system_lyxdir(), "examples"))));
817 FileDialog::Result result =
818 fileDlg.open(initpath,
819 FileFilterList(_("LyX Documents (*.lyx)")),
822 if (result.first == FileDialog::Later)
825 filename = result.second;
827 // check selected filename
828 if (filename.empty()) {
829 owner_->message(_("Canceled."));
834 // get absolute path of file and add ".lyx" to the filename if
836 filename = FileSearch(string(), filename, "lyx");
838 string const disp_fn = MakeDisplayPath(filename);
839 owner_->message(bformat(_("Inserting document %1$s..."), disp_fn));
840 if (bv_->insertLyXFile(filename))
841 owner_->message(bformat(_("Document %1$s inserted."),
844 owner_->message(bformat(_("Could not insert document %1$s"),
849 void BufferView::Pimpl::trackChanges()
851 Buffer * buf(bv_->buffer());
852 bool const tracking(buf->params().tracking_changes);
855 ParIterator const end = buf->par_iterator_end();
856 for (ParIterator it = buf->par_iterator_begin(); it != end; ++it)
858 buf->params().tracking_changes = true;
860 // we cannot allow undos beyond the freeze point
861 buf->undostack().clear();
864 bv_->text()->setCursor(0, 0);
865 #warning changes FIXME
866 bool found = lyx::find::findNextChange(bv_);
868 owner_->getDialogs().show("changes");
872 ParIterator const end = buf->par_iterator_end();
873 for (ParIterator it = buf->par_iterator_begin(); it != end; ++it)
874 it->untrackChanges();
875 buf->params().tracking_changes = false;
878 buf->redostack().clear();
882 LCursor theTempCursor;
886 InsetOld * insetFromCoords(BufferView * bv, int x, int y)
888 lyxerr << "insetFromCoords" << endl;
889 LyXText * text = bv->text();
890 InsetOld * inset = 0;
891 theTempCursor = LCursor(*bv);
893 InsetOld * const inset_hit = text->checkInsetHit(x, y);
895 lyxerr << "no further inset hit" << endl;
899 if (!inset->descendable()) {
900 lyxerr << "not descendable" << endl;
903 int const cell = inset->getCell(x, y + bv->top_y());
906 text = inset_hit->getText(cell);
907 lyxerr << "Hit inset: " << inset << " at x: " << x
908 << " text: " << text << " y: " << y << endl;
909 theTempCursor.push(static_cast<UpdatableInset*>(inset));
911 lyxerr << "theTempCursor: " << theTempCursor << endl;
918 bool BufferView::Pimpl::workAreaDispatch(FuncRequest const & cmd)
920 switch (cmd.action) {
921 case LFUN_MOUSE_MOTION: {
924 FuncRequest cmd1 = cmd;
925 UpdatableInset * inset = bv_->fullCursor().innerInset();
928 cmd1.x -= inset->x();
929 cmd1.y -= inset->y();
930 res = inset->dispatch(*bv_, cmd1);
932 cmd1.y += bv_->top_y();
933 res = bv_->fullCursor().innerText()->dispatch(*bv_, cmd1);
936 if (bv_->fitCursor() || res.update()) {
938 bv_->fullCursor().updatePos();
944 case LFUN_MOUSE_PRESS:
945 case LFUN_MOUSE_RELEASE:
946 case LFUN_MOUSE_DOUBLE:
947 case LFUN_MOUSE_TRIPLE: {
948 // We pass those directly to the Bufferview, since
949 // otherwise selection handling breaks down
951 // Doesn't go through lyxfunc, so we need to update
952 // the layout choice etc. ourselves
954 // e.g. Qt mouse press when no buffer
958 screen().hideCursor();
960 // either the inset under the cursor or the surrounding LyXText will
961 // handle this event.
963 // built temporary path to inset
964 InsetOld * inset = insetFromCoords(bv_, cmd.x, cmd.y);
967 // try to dispatch to that inset
969 FuncRequest cmd2 = cmd;
970 lyxerr << "dispatching action " << cmd2.action
971 << " to inset " << inset << endl;
972 cmd2.x -= inset->x();
973 cmd2.y -= inset->y();
974 res = inset->dispatch(*bv_, cmd2);
977 bv_->fullCursor().updatePos();
986 bv_->fullCursor(theTempCursor);
987 bv_->fullCursor().innerText()
988 ->setCursorFromCoordinates(cmd.x, top_y() + cmd.y);
989 if (bv_->fitCursor())
993 lyxerr << "not dispatched by inner inset val: " << res.val() << endl;
998 // otherwise set cursor to surrounding LyXText
999 if (!res.dispatched()) {
1000 lyxerr << "temp cursor is: " << theTempCursor << endl;
1001 lyxerr << "dispatching " << cmd
1002 << " to surrounding LyXText "
1003 << theTempCursor.innerText() << endl;
1004 bv_->fullCursor(theTempCursor);
1005 FuncRequest cmd1 = cmd;
1006 cmd1.y += bv_->top_y();
1007 res = bv_->fullCursor().innerText()->dispatch(*bv_, cmd1);
1008 if (bv_->fitCursor() || res.update())
1011 //return DispatchResult(true, true);
1013 // see workAreaKeyPress
1014 cursor_timeout.restart();
1015 screen().showCursor(*bv_);
1017 // skip these when selecting
1018 if (cmd.action != LFUN_MOUSE_MOTION) {
1019 owner_->updateLayoutChoice();
1020 owner_->updateToolbar();
1023 // slight hack: this is only called currently when we
1024 // clicked somewhere, so we force through the display
1025 // of the new status here.
1026 owner_->clearMessage();
1031 owner_->dispatch(cmd);
1037 bool BufferView::Pimpl::dispatch(FuncRequest const & ev)
1039 // Make sure that the cached BufferView is correct.
1040 lyxerr[Debug::ACTION] << "BufferView::Pimpl::Dispatch:"
1041 << " action[" << ev.action << ']'
1042 << " arg[" << ev.argument << ']'
1043 << " x[" << ev.x << ']'
1044 << " y[" << ev.y << ']'
1045 << " button[" << ev.button() << ']'
1048 LyXTextClass const & tclass = buffer_->params().getLyXTextClass();
1050 switch (ev.action) {
1052 case LFUN_SCROLL_INSET:
1053 // this is not handled here as this function is only active
1054 // if we have a locking_inset and that one is (or contains)
1058 case LFUN_FILE_INSERT:
1059 MenuInsertLyXFile(ev.argument);
1062 case LFUN_FILE_INSERT_ASCII_PARA:
1063 InsertAsciiFile(bv_, ev.argument, true);
1066 case LFUN_FILE_INSERT_ASCII:
1067 InsertAsciiFile(bv_, ev.argument, false);
1070 case LFUN_FONT_STATE:
1071 owner_->getLyXFunc().setMessage(currentState(bv_));
1074 case LFUN_INSERT_LABEL: {
1075 // Try and generate a valid label
1076 string const contents = ev.argument.empty() ?
1077 getPossibleLabel(*bv_) : ev.argument;
1078 InsetCommandParams icp("label", contents);
1079 string data = InsetCommandMailer::params2string("label", icp);
1080 owner_->getDialogs().show("label", data, 0);
1084 case LFUN_BOOKMARK_SAVE:
1085 savePosition(strToUnsignedInt(ev.argument));
1088 case LFUN_BOOKMARK_GOTO:
1089 restorePosition(strToUnsignedInt(ev.argument));
1092 case LFUN_REF_GOTO: {
1093 string label = ev.argument;
1094 if (label.empty()) {
1096 static_cast<InsetRef*>(getInsetByCode(InsetOld::REF_CODE));
1098 label = inset->getContents();
1104 bv_->gotoLabel(label);
1108 // --- accented characters ---------------------------
1111 case LFUN_CIRCUMFLEX:
1121 case LFUN_SPECIAL_CARON:
1124 case LFUN_HUNG_UMLAUT:
1127 if (ev.argument.empty()) {
1129 owner_->getLyXFunc().handleKeyFunc(ev.action);
1131 owner_->getLyXFunc().handleKeyFunc(ev.action);
1132 owner_->getIntl().getTransManager()
1133 .TranslateAndInsert(ev.argument[0], bv_->getLyXText());
1138 case LFUN_MATH_MACRO:
1139 case LFUN_MATH_DELIM:
1140 case LFUN_INSERT_MATRIX:
1141 case LFUN_INSERT_MATH:
1142 case LFUN_MATH_IMPORT_SELECTION: // Imports LaTeX from the X selection
1143 case LFUN_MATH_DISPLAY: // Open or create a displayed math inset
1144 case LFUN_MATH_MODE: // Open or create an inlined math inset
1145 mathDispatch(*bv_, ev);
1148 case LFUN_INSET_INSERT: {
1150 BOOST_ASSERT(false);
1151 InsetOld * inset = createInset(bv_, ev);
1152 if (!inset || !insertInset(inset))
1157 case LFUN_FLOAT_LIST:
1158 if (tclass.floats().typeExist(ev.argument)) {
1159 InsetOld * inset = new InsetFloatList(ev.argument);
1160 if (!insertInset(inset, tclass.defaultLayoutName()))
1163 lyxerr << "Non-existent float type: "
1164 << ev.argument << endl;
1168 case LFUN_LAYOUT_PARAGRAPH: {
1170 params2string(*bv_->getLyXText()->cursorPar(), data);
1171 data = "show\n" + data;
1172 bv_->owner()->getDialogs().show("paragraph", data);
1176 case LFUN_PARAGRAPH_UPDATE:
1177 updateParagraphDialog();
1180 case LFUN_PARAGRAPH_APPLY:
1181 setParagraphParams(*bv_, ev.argument);
1184 case LFUN_THESAURUS_ENTRY: {
1185 string arg = ev.argument;
1188 arg = bv_->getLyXText()->selectionAsString(*buffer_,
1192 if (arg.size() > 100 || arg.empty()) {
1193 // Get word or selection
1194 bv_->getLyXText()->selectWordWhenUnderCursor(lyx::WHOLE_WORD);
1195 arg = bv_->getLyXText()->selectionAsString(*buffer_, false);
1196 // FIXME: where is getLyXText()->unselect(bv_) ?
1200 bv_->owner()->getDialogs().show("thesaurus", arg);
1204 case LFUN_TRACK_CHANGES:
1208 case LFUN_MERGE_CHANGES:
1209 owner_->getDialogs().show("changes");
1212 case LFUN_ACCEPT_ALL_CHANGES: {
1213 bv_->text()->setCursor(0, 0);
1214 #warning FIXME changes
1215 while (lyx::find::findNextChange(bv_))
1216 bv_->getLyXText()->acceptChange();
1221 case LFUN_REJECT_ALL_CHANGES: {
1222 bv_->text()->setCursor(0, 0);
1223 #warning FIXME changes
1224 while (lyx::find::findNextChange(bv_))
1225 bv_->getLyXText()->rejectChange();
1230 case LFUN_ACCEPT_CHANGE: {
1231 bv_->getLyXText()->acceptChange();
1236 case LFUN_REJECT_CHANGE: {
1237 bv_->getLyXText()->rejectChange();
1242 case LFUN_WORD_FIND:
1243 lyx::find::find(bv_, ev);
1246 case LFUN_WORD_REPLACE:
1247 lyx::find::replace(bv_, ev);
1251 bv_->clearSelection();
1254 ev.message(N_("Mark off"));
1258 bv_->clearSelection();
1259 bv_->selection().mark(true);
1262 ev.message(N_("Mark on"));
1266 bv_->clearSelection();
1267 if (bv_->selection().mark()) {
1268 ev.message(N_("Mark removed"));
1270 bv_->selection().mark(true);
1271 ev.message(N_("Mark set"));
1277 case LFUN_UNKNOWN_ACTION:
1278 ev.errorMessage(N_("Unknown function!"));
1282 return bv_->getLyXText()->dispatch(*bv_, ev).dispatched();
1289 bool BufferView::Pimpl::insertInset(InsetOld * inset, string const & lout)
1291 // not quite sure if we want this...
1292 bv_->text()->recUndo(bv_->text()->cursor().par());
1295 bv_->clearSelection();
1296 if (!lout.empty()) {
1297 bv_->text()->breakParagraph(bv_->buffer()->paragraphs());
1299 if (!bv_->text()->cursorPar()->empty()) {
1300 bv_->text()->cursorLeft(bv_);
1301 bv_->text()->breakParagraph(bv_->buffer()->paragraphs());
1305 LyXTextClass const & tclass = buffer_->params().getLyXTextClass();
1306 bool hasLayout = tclass.hasLayout(lres);
1308 bv_->text()->setLayout(hasLayout ? lres : tclass.defaultLayoutName());
1309 bv_->text()->setParagraph(Spacing(), LYX_ALIGN_LAYOUT, string(), 0);
1311 bv_->fullCursor().innerText()->insertInset(inset);
1317 bool BufferView::Pimpl::ChangeInsets(InsetOld::Code code,
1318 string const & from, string const & to)
1320 bool need_update = false;
1321 CursorSlice cur = bv_->text()->cursor();
1323 ParIterator end = bv_->buffer()->par_iterator_end();
1324 for (ParIterator it = bv_->buffer()->par_iterator_begin();
1326 bool changed_inset = false;
1327 for (InsetList::iterator it2 = it->insetlist.begin();
1328 it2 != it->insetlist.end(); ++it2) {
1329 if (it2->inset->lyxCode() == code) {
1330 InsetCommand * inset = static_cast<InsetCommand *>(it2->inset);
1331 if (inset->getContents() == from) {
1332 inset->setContents(to);
1333 changed_inset = true;
1337 if (changed_inset) {
1342 // The test it.size() == 1 was needed to prevent crashes.
1343 // How to set the cursor correctly when it.size() > 1 ??
1344 if (it.size() == 1) {
1345 bv_->text()->setCursorIntern(bv_->text()->parOffset(it.pit()), 0);
1346 bv_->text()->redoParagraph(bv_->text()->cursorPar());
1350 bv_->text()->setCursorIntern(cur.par(), cur.pos());
1355 void BufferView::Pimpl::updateParagraphDialog()
1357 if (!bv_->owner()->getDialogs().visible("paragraph"))
1359 Paragraph const & par = *bv_->getLyXText()->cursorPar();
1361 params2string(par, data);
1363 // Will the paragraph accept changes from the dialog?
1364 InsetOld * const inset = par.inInset();
1366 !(inset && inset->forceDefaultParagraphs(inset));
1368 data = "update " + tostr(accept) + '\n' + data;
1369 bv_->owner()->getDialogs().update("paragraph", data);