X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBufferView.cpp;h=390b62e01d88a96a02455291e8caea33bbb50c34;hb=2ee5c3a1fbb330d626622c6c50c1ffca634391db;hp=5770e6926dbf9c5262086a1afc353f100b8260be;hpb=32871c1284f15265f652ff01c438e539a7c8181f;p=lyx.git diff --git a/src/BufferView.cpp b/src/BufferView.cpp index 5770e6926d..390b62e01d 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -39,9 +39,9 @@ #include "LyX.h" #include "lyxfind.h" #include "LyXFunc.h" -#include "LyXLayout.h" -#include "LyXText.h" -#include "LyXTextClass.h" +#include "Layout.h" +#include "Text.h" +#include "TextClass.h" #include "LyXRC.h" #include "Session.h" #include "Paragraph.h" @@ -78,6 +78,16 @@ #include #include +using std::distance; +using std::endl; +using std::istringstream; +using std::make_pair; +using std::min; +using std::max; +using std::mem_fun_ref; +using std::string; +using std::vector; + namespace lyx { @@ -91,16 +101,6 @@ using support::isFileReadable; using support::makeDisplayPath; using support::package; -using std::distance; -using std::endl; -using std::istringstream; -using std::make_pair; -using std::min; -using std::max; -using std::mem_fun_ref; -using std::string; -using std::vector; - namespace Alert = frontend::Alert; namespace { @@ -143,12 +143,14 @@ Buffer * BufferView::buffer() const } -void BufferView::setBuffer(Buffer * b) +Buffer * BufferView::setBuffer(Buffer * b) { LYXERR(Debug::INFO) << BOOST_CURRENT_FUNCTION << "[ b = " << b << "]" << endl; if (buffer_) { + // Save the current selection if any + cap::saveSelection(cursor_); // Save the actual cursor position and anchor inside the // buffer so that it can be restored in case we rechange // to this buffer later on. @@ -180,17 +182,23 @@ void BufferView::setBuffer(Buffer * b) // If we're quitting lyx, don't bother updating stuff if (quitting) { buffer_ = 0; - return; + return 0; } + //FIXME Fix for bug 3440 is here. // If we are closing current buffer, switch to the first in // buffer list. if (!b) { LYXERR(Debug::INFO) << BOOST_CURRENT_FUNCTION << " No Buffer!" << endl; // We are closing the buffer, use the first buffer as current + //FIXME 3440 + // if (last_buffer_) buffer_ = last_buffer_; + // also check that this is in theBufferList()? buffer_ = theBufferList().first(); } else { + //FIXME 3440 + // last_buffer = buffer_; // Set current buffer buffer_ = b; } @@ -200,100 +208,42 @@ void BufferView::setBuffer(Buffer * b) anchor_ref_ = 0; offset_ref_ = 0; - if (buffer_) { - LYXERR(Debug::INFO) << BOOST_CURRENT_FUNCTION - << "Buffer addr: " << buffer_ << endl; - cursor_.push(buffer_->inset()); - cursor_.resetAnchor(); - buffer_->text().setCurrentFont(cursor_); - if (buffer_->getCursor().size() > 0 && - buffer_->getAnchor().size() > 0) - { - cursor_.setCursor(buffer_->getAnchor().asDocIterator(&(buffer_->inset()))); - cursor_.resetAnchor(); - cursor_.setCursor(buffer_->getCursor().asDocIterator(&(buffer_->inset()))); - cursor_.setSelection(); - // do not set selection to the new buffer because we - // only paste recent selection. - } - } - - if (buffer_) - updateMetrics(false); - - if (buffer_ && graphics::Previews::status() != LyXRC::PREVIEW_OFF) - graphics::Previews::get().generateBufferPreviews(*buffer_); -} - + if (!buffer_) + return 0; -bool BufferView::loadLyXFile(FileName const & filename, bool tolastfiles) -{ - // File already open? - if (theBufferList().exists(filename.absFilename())) { - docstring const file = makeDisplayPath(filename.absFilename(), 20); - docstring text = bformat(_("The document %1$s is already " - "loaded.\n\nDo you want to revert " - "to the saved version?"), file); - int const ret = Alert::prompt(_("Revert to saved document?"), - text, 0, 1, _("&Revert"), _("&Switch to document")); - - if (ret != 0) { - setBuffer(theBufferList().getBuffer(filename.absFilename())); - return true; - } - // FIXME: should be LFUN_REVERT - if (!theBufferList().close(theBufferList().getBuffer(filename.absFilename()), false)) - return false; - // Fall through to new load. (Asger) - buffer_ = 0; - } + LYXERR(Debug::INFO) << BOOST_CURRENT_FUNCTION + << "Buffer addr: " << buffer_ << endl; + cursor_.push(buffer_->inset()); + cursor_.resetAnchor(); + buffer_->text().setCurrentFont(cursor_); - Buffer * b = 0; + // Update the metrics now that we have a proper Cursor. + updateMetrics(false); - if (isFileReadable(filename)) { - b = theBufferList().newBuffer(filename.absFilename()); - if (!lyx::loadLyXFile(b, filename)) { - theBufferList().release(b); - return false; - } - } else { - docstring text = bformat(_("The document %1$s does not yet " - "exist.\n\nDo you want to create " - "a new document?"), from_utf8(filename.absFilename())); - int const ret = Alert::prompt(_("Create new document?"), - text, 0, 1, _("&Create"), _("Cancel")); - - if (ret == 0) { - b = newFile(filename.absFilename(), string(), true); - if (!b) - return false; - } else - return false; - } + // FIXME: This code won't be needed once we switch to + // "one Buffer" / "one BufferView". + if (buffer_->getCursor().size() > 0 && + buffer_->getAnchor().size() > 0) + { + cursor_.setCursor(buffer_->getAnchor().asDocIterator(&(buffer_->inset()))); + cursor_.resetAnchor(); + cursor_.setCursor(buffer_->getCursor().asDocIterator(&(buffer_->inset()))); + cursor_.setSelection(); + // do not set selection to the new buffer because we + // only paste recent selection. - setBuffer(b); - // Send the "errors" signal in case of parsing errors - b->errors("Parse"); + // Make sure that the restored cursor is not broken. This can happen for + // example if this Buffer has been modified by another view. + cursor_.fixIfBroken(); - // Update the labels and section numbering. - updateLabels(*buffer_); - // scroll to the position when the file was last closed - if (lyxrc.use_lastfilepos) { - pit_type pit; - pos_type pos; - boost::tie(pit, pos) = LyX::ref().session().lastFilePos().load(filename); - // if successfully move to pit (returned par_id is not zero), update metrics and reset font - if (moveToPosition(pit, pos, 0, 0).get<1>()) { - if (fitCursor()) - updateMetrics(false); - buffer_->text().setCurrentFont(cursor_); - } + if (fitCursor()) + // Update the metrics if the cursor new position was off screen. + updateMetrics(false); } - if (tolastfiles) - LyX::ref().session().lastFiles().add(FileName(b->fileName())); - - return true; + if (graphics::Previews::status() != LyXRC::PREVIEW_OFF) + graphics::Previews::get().generateBufferPreviews(*buffer_); + return buffer_; } @@ -305,7 +255,6 @@ void BufferView::resize() LYXERR(Debug::DEBUG) << BOOST_CURRENT_FUNCTION << endl; updateMetrics(false); - switchKeyMap(); } @@ -376,7 +325,7 @@ bool BufferView::update(Update::flags flags) return true; } - if (flags == Update::FitCursor + if (flags == Update::FitCursor || flags == (Update::Decoration | Update::FitCursor)) { bool const fit_cursor = fitCursor(); // tell the frontend to update the screen if needed. @@ -417,7 +366,7 @@ void BufferView::updateScrollbar() return; } - LyXText & t = buffer_->text(); + Text & t = buffer_->text(); TextMetrics & tm = text_metrics_[&t]; int const parsize = int(t.paragraphs().size() - 1); @@ -485,7 +434,7 @@ void BufferView::scrollDocView(int value) if (!buffer_) return; - LyXText & t = buffer_->text(); + Text & t = buffer_->text(); TextMetrics & tm = text_metrics_[&t]; float const bar = value / float(wh_ * t.paragraphs().size()); @@ -506,7 +455,7 @@ void BufferView::setCursorFromScrollbar() if (!buffer_) return; - LyXText & t = buffer_->text(); + Text & t = buffer_->text(); int const height = 2 * defaultRowHeight(); int const first = height; @@ -555,7 +504,7 @@ void BufferView::saveBookmark(unsigned int idx) { // tenatively save bookmark, id and pos will be used to // acturately locate a bookmark in a 'live' lyx session. - // pit and pos will be updated with bottom level pit/pos + // pit and pos will be updated with bottom level pit/pos // when lyx exits. LyX::ref().session().bookmarks().save( FileName(buffer_->fileName()), @@ -583,9 +532,12 @@ boost::tuple BufferView::moveToPosition(pit_type bottom ParIterator par = buffer_->getParFromID(top_id); if (par != buffer_->par_iterator_end()) { DocIterator dit = makeDocIterator(par, min(par->size(), top_pos)); - // Some slices of the iterator may not be reachable (e.g. closed collapsable inset) - // so the dociterator may need to be shortened. Otherwise, setCursor may - // crash lyx when the cursor can not be set to these insets. + // Some slices of the iterator may not be + // reachable (e.g. closed collapsable inset) + // so the dociterator may need to be + // shortened. Otherwise, setCursor may crash + // lyx when the cursor can not be set to these + // insets. size_t const n = dit.depth(); for (size_t i = 0; i < n; ++i) if (dit[i].inset().editable() != Inset::HIGHLY_EDITABLE) { @@ -598,38 +550,37 @@ boost::tuple BufferView::moveToPosition(pit_type bottom } } // if top_id == 0, or searching through top_id failed - // This is the case for a 'restored' bookmark when only bottom + // This is the case for a 'restored' bookmark when only bottom // (document level) pit was saved. Because of this, bookmark // restoration is inaccurate. If a bookmark was within an inset, // it will be restored to the left of the outmost inset that contains // the bookmark. if (static_cast(bottom_pit) < buffer_->paragraphs().size()) { - ParIterator it = buffer_->par_iterator_begin(); - ParIterator const end = buffer_->par_iterator_end(); - for (; it != end; ++it) - if (it.pit() == bottom_pit) { - // restored pos may be bigger than it->size - setCursor(makeDocIterator(it, min(bottom_pos, it->size()))); - return boost::make_tuple(bottom_pit, bottom_pos, it->id()); - } + DocIterator it = doc_iterator_begin(buffer_->inset()); + it.pit() = bottom_pit; + it.pos() = min(bottom_pos, it.paragraph().size()); + setCursor(it); + return boost::make_tuple(it.pit(), it.pos(), + it.paragraph().id()); } // both methods fail return boost::make_tuple(pit_type(0), pos_type(0), 0); } -void BufferView::switchKeyMap() +void BufferView::translateAndInsert(char_type c, Text * t, Cursor & cur) { - if (!lyxrc.rtl_support) - return; - - if (cursor_.innerText()->real_current_font.isRightToLeft()) { - if (intl_->keymap == Intl::PRIMARY) - intl_->keyMapSec(); - } else { - if (intl_->keymap == Intl::SECONDARY) - intl_->keyMapPrim(); + if (lyxrc.rtl_support) { + if (cursor_.innerText()->real_current_font.isRightToLeft()) { + if (intl_->keymap == Intl::PRIMARY) + intl_->keyMapSec(); + } else { + if (intl_->keymap == Intl::SECONDARY) + intl_->keyMapPrim(); + } } + + intl_->getTransManager().translateAndInsert(c, t, cur); } @@ -656,6 +607,8 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) { FuncStatus flag; + Cursor & cur = cursor_; + switch (cmd.action) { case LFUN_UNDO: @@ -668,8 +621,8 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) case LFUN_FILE_INSERT_PLAINTEXT_PARA: case LFUN_FILE_INSERT_PLAINTEXT: case LFUN_BOOKMARK_SAVE: - // FIXME: Actually, these LFUNS should be moved to LyXText - flag.enabled(cursor_.inTexted()); + // FIXME: Actually, these LFUNS should be moved to Text + flag.enabled(cur.inTexted()); break; case LFUN_FONT_STATE: case LFUN_LABEL_INSERT: @@ -696,7 +649,7 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) case LFUN_LABEL_GOTO: { flag.enabled(!cmd.argument().empty() - || getInsetByCode(cursor_, Inset::REF_CODE)); + || getInsetByCode(cur, Inset::REF_CODE)); break; } @@ -706,7 +659,7 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) break; case LFUN_CHANGES_OUTPUT: - flag.enabled(buffer_ && LaTeXFeatures::isAvailable("dvipost")); + flag.enabled(buffer_); flag.setOnOff(buffer_->params().outputChanges); break; @@ -765,7 +718,6 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) cur.message(_("No further undo information")); updateFlags = Update::None; } - switchKeyMap(); break; case LFUN_REDO: @@ -775,7 +727,6 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) cur.message(_("No further redo information")); updateFlags = Update::None; } - switchKeyMap(); break; case LFUN_FILE_INSERT: @@ -837,7 +788,6 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) if (b == buffer_) { // Set the cursor setCursor(makeDocIterator(par, 0)); - switchKeyMap(); } else { // Switch to other buffer view and resend cmd theLyXFunc().dispatch(FuncRequest( @@ -889,6 +839,25 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) case LFUN_CHANGES_OUTPUT: buffer_->params().outputChanges = !buffer_->params().outputChanges; + if (buffer_->params().outputChanges) { + bool dvipost = LaTeXFeatures::isAvailable("dvipost"); + bool xcolorsoul = LaTeXFeatures::isAvailable("soul") && + LaTeXFeatures::isAvailable("xcolor"); + + if (!dvipost && !xcolorsoul) { + Alert::warning(_("Changes not shown in LaTeX output"), + _("Changes will not be highlighted in LaTeX output, " + "because neither dvipost nor xcolor/soul are installed.\n" + "Please install these packages or redefine " + "\\lyxadded and \\lyxdeleted in the LaTeX preamble.")); + } else if (!xcolorsoul) { + Alert::warning(_("Changes not shown in LaTeX output"), + _("Changes will not be highlighted in LaTeX output " + "when using pdflatex, because xcolor and soul are not installed.\n" + "Please install both packages or redefine " + "\\lyxadded and \\lyxdeleted in the LaTeX preamble.")); + } + } break; case LFUN_CHANGE_NEXT: @@ -906,7 +875,7 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) cursor_.selHandle(true); buffer_->text().cursorBottom(cursor_); // accept everything in a single step to support atomic undo - buffer_->text().acceptOrRejectChanges(cursor_, LyXText::ACCEPT); + buffer_->text().acceptOrRejectChanges(cursor_, Text::ACCEPT); break; case LFUN_ALL_CHANGES_REJECT: @@ -916,16 +885,28 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) buffer_->text().cursorBottom(cursor_); // reject everything in a single step to support atomic undo // Note: reject does not work recursively; the user may have to repeat the operation - buffer_->text().acceptOrRejectChanges(cursor_, LyXText::REJECT); + buffer_->text().acceptOrRejectChanges(cursor_, Text::REJECT); break; case LFUN_WORD_FIND: find(this, cmd); break; - case LFUN_WORD_REPLACE: - replace(this, cmd); + case LFUN_WORD_REPLACE: { + bool has_deleted = false; + if (cur.selection()) { + DocIterator beg = cur.selectionBegin(); + DocIterator end = cur.selectionEnd(); + if (beg.pit() == end.pit()) { + for (pos_type p = beg.pos() ; p < end.pos() ; ++p) { + if (cur.paragraph().isDeleted(p)) + has_deleted = true; + } + } + } + replace(this, cmd, has_deleted); break; + } case LFUN_MARK_OFF: cur.clearSelection(); @@ -1102,7 +1083,7 @@ void BufferView::workAreaResize(int width, int height) } -Inset const * BufferView::getCoveringInset(LyXText const & text, int x, int y) +Inset const * BufferView::getCoveringInset(Text const & text, int x, int y) { pit_type pit = text.getPitNearY(*this, y); BOOST_ASSERT(pit != -1); @@ -1120,17 +1101,17 @@ Inset const * BufferView::getCoveringInset(LyXText const & text, int x, int y) Inset * const inset = iit->inset; if (inset->covers(*this, x, y)) { if (!inset->descendable()) - // No need to go further down if the inset is not + // No need to go further down if the inset is not // descendable. return inset; size_t cell_number = inset->nargs(); // Check all the inner cell. for (size_t i = 0; i != cell_number; ++i) { - LyXText const * inner_text = inset->getText(i); + Text const * inner_text = inset->getText(i); if (inner_text) { // Try deeper. - Inset const * inset_deeper = + Inset const * inset_deeper = getCoveringInset(*inner_text, x, y); if (inset_deeper) return inset_deeper; @@ -1167,15 +1148,15 @@ bool BufferView::workAreaDispatch(FuncRequest const & cmd0) cur.selection() = cursor_.selection(); // Either the inset under the cursor or the - // surrounding LyXText will handle this event. + // surrounding Text will handle this event. // make sure we stay within the screen... cmd.y = min(max(cmd.y, -1), height_); - + if (cmd.action == LFUN_MOUSE_MOTION && cmd.button() == mouse_button::none) { - + // Get inset under mouse, if there is one. - Inset const * covering_inset = + Inset const * covering_inset = getCoveringInset(buffer_->text(), cmd.x, cmd.y); if (covering_inset == last_inset_) // Same inset, no need to do anything... @@ -1196,9 +1177,9 @@ bool BufferView::workAreaDispatch(FuncRequest const & cmd0) // not expose the button for redraw. We adjust here the metrics dimension // to enable a full redraw. // FIXME: It is possible to redraw only the area around the button! - if (need_redraw + if (need_redraw && metrics_info_.update_strategy == SingleParUpdate) { - // FIXME: It should be possible to redraw only the area around + // FIXME: It should be possible to redraw only the area around // the button by doing this: // //metrics_info_.singlepar = false; @@ -1209,7 +1190,7 @@ bool BufferView::workAreaDispatch(FuncRequest const & cmd0) // between background updates and text updates. So we use the hammer // solution for now. We could also avoid the updateMetrics() below // by using the first and last pit of the CoordCache. Have a look - // at LyXText::getPitNearY() to see what I mean. + // at Text::getPitNearY() to see what I mean. // //metrics_info_.pit1 = first pit of CoordCache; //metrics_info_.pit2 = last pit of CoordCache; @@ -1224,7 +1205,7 @@ bool BufferView::workAreaDispatch(FuncRequest const & cmd0) // This should be changed if it is further utilized. return need_redraw; } - + // Build temporary cursor. Inset * inset = buffer_->text().editXY(cur, cmd.x, cmd.y); @@ -1257,7 +1238,7 @@ void BufferView::scroll(int /*lines*/) // if (!buffer_) // return; // -// LyXText const * t = &buffer_->text(); +// Text const * t = &buffer_->text(); // int const line_height = defaultRowHeight(); // // // The new absolute coordinate @@ -1279,6 +1260,7 @@ void BufferView::setCursorFromRow(int row) buffer_->texrow().getIdFromRow(row, tmpid, tmppos); + cursor_.reset(buffer_->inset()); if (tmpid == -1) buffer_->text().setCursor(cursor_, 0, 0); else @@ -1300,24 +1282,24 @@ void BufferView::gotoLabel(docstring const & label) } -TextMetrics const & BufferView::textMetrics(LyXText const * t) const +TextMetrics const & BufferView::textMetrics(Text const * t) const { return const_cast(this)->textMetrics(t); } -TextMetrics & BufferView::textMetrics(LyXText const * t) +TextMetrics & BufferView::textMetrics(Text const * t) { TextMetricsCache::iterator tmc_it = text_metrics_.find(t); if (tmc_it == text_metrics_.end()) { tmc_it = text_metrics_.insert( - make_pair(t, TextMetrics(this, const_cast(t)))).first; - } + make_pair(t, TextMetrics(this, const_cast(t)))).first; + } return tmc_it->second; } -ParagraphMetrics const & BufferView::parMetrics(LyXText const * t, +ParagraphMetrics const & BufferView::parMetrics(Text const * t, pit_type pit) const { return textMetrics(t).parMetrics(pit); @@ -1353,7 +1335,7 @@ bool BufferView::checkDepm(Cursor & cur, Cursor & old) if (need_anchor_change) cur.resetAnchor(); - + if (!changed) return false; @@ -1369,11 +1351,15 @@ bool BufferView::mouseSetCursor(Cursor & cur) { BOOST_ASSERT(&cur.bv() == this); + // this event will clear selection so we save selection for + // persistent selection + cap::saveSelection(cursor()); + // Has the cursor just left the inset? bool badcursor = false; bool leftinset = (&cursor_.inset() != &cur.inset()); if (leftinset) - badcursor = cursor_.inset().notifyCursorLeaves(cursor_); + badcursor = notifyCursorLeaves(cursor_, cur); // do the dEPM magic if needed // FIXME: (1) move this to InsetText::notifyCursorLeaves? @@ -1387,8 +1373,8 @@ bool BufferView::mouseSetCursor(Cursor & cur) // position is in the nucleus of the inset, notifyCursorLeaves // will kill the script inset itself. So we check all the // elements of the cursor to make sure that they are correct. - // For an example, see bug 2933: - // http://bugzilla.lyx.org/show_bug.cgi?id=2933 + // For an example, see bug 2933: + // http://bugzilla.lyx.org/show_bug.cgi?id=2933 // The code below could maybe be moved to a DocIterator method. //lyxerr << "cur before " << cur <text(); + Text & buftext = buffer_->text(); TextMetrics & tm = textMetrics(&buftext); pit_type size = int(buftext.paragraphs().size()); @@ -1460,7 +1446,16 @@ void BufferView::updateMetrics(bool singlepar) anchor_ref_ = int(buftext.paragraphs().size() - 1); offset_ref_ = 0; } + + if (!singlepar) { + // Clear out the position cache in case of full screen redraw, + coord_cache_.clear(); + // Clear out paragraph metrics to avoid having invalid metrics + // in the cache from paragraphs not relayouted below + tm.clear(); + } + // If the paragraph metrics has changed, we can not // use the singlepar optimisation. if (singlepar @@ -1479,10 +1474,6 @@ void BufferView::updateMetrics(bool singlepar) // Rebreak anchor paragraph. if (!singlepar) tm.redoParagraph(pit); - - // Clear out the position cache in case of full screen redraw. - if (!singlepar) - coord_cache_.clear(); int y0 = tm.parMetrics(pit).ascent() - offset_ref_; @@ -1557,7 +1548,7 @@ void BufferView::updateMetrics(bool singlepar) << "size: " << size << endl; - metrics_info_ = ViewMetricsInfo(pit1, pit2, y1, y2, + metrics_info_ = ViewMetricsInfo(pit1, pit2, y1, y2, singlepar? SingleParUpdate: FullScreenUpdate, size); if (lyxerr.debugging(Debug::WORKAREA)) { @@ -1588,7 +1579,9 @@ void BufferView::menuInsertLyXFile(string const & filenm) FileDialog fileDlg(_("Select LyX document to insert"), LFUN_FILE_INSERT, make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)), - make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support().absFilename(), "examples")))); + make_pair(_("Examples|#E#e"), + from_utf8(addPath(package().system_support().absFilename(), + "examples")))); FileDialog::Result result = fileDlg.open(from_utf8(initpath),