X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBufferView_pimpl.C;h=1c52d6e331492a1cd2398a980ab57fe763a184ee;hb=41ecabf5197a74719dd125e974b062184208c96b;hp=1fdc75a779953f43b533583a1e87b2f6d3787d7b;hpb=a13a346b0ef28ef152c382a73f356dfb144e022d;p=lyx.git diff --git a/src/BufferView_pimpl.C b/src/BufferView_pimpl.C index 1fdc75a779..1c52d6e331 100644 --- a/src/BufferView_pimpl.C +++ b/src/BufferView_pimpl.C @@ -23,6 +23,7 @@ #include "buffer_funcs.h" #include "bufferlist.h" #include "bufferparams.h" +#include "coordcache.h" #include "cursor.h" #include "debug.h" #include "dispatchresult.h" @@ -33,6 +34,7 @@ #include "gettext.h" #include "intl.h" #include "insetiterator.h" +#include "LaTeXFeatures.h" #include "lyx_cb.h" // added for Dispatch functions #include "lyx_main.h" #include "lyxfind.h" @@ -40,6 +42,7 @@ #include "lyxtext.h" #include "lyxrc.h" #include "lastfiles.h" +#include "metricsinfo.h" #include "paragraph.h" #include "paragraph_funcs.h" #include "ParagraphParameters.h" @@ -48,12 +51,14 @@ #include "undo.h" #include "vspace.h" +#include "insets/insetbibtex.h" #include "insets/insetref.h" #include "insets/insettext.h" #include "frontends/Alert.h" #include "frontends/Dialogs.h" #include "frontends/FileDialog.h" +#include "frontends/font_metrics.h" #include "frontends/LyXView.h" #include "frontends/LyXScreenFactory.h" #include "frontends/screen.h" @@ -62,14 +67,18 @@ #include "graphics/Previews.h" +#include "support/convert.h" +#include "support/filefilterlist.h" #include "support/filetools.h" #include "support/forkedcontr.h" -#include "support/globbing.h" -#include "support/path_defines.h" -#include "support/tostr.h" +#include "support/package.h" #include "support/types.h" #include +#include + +#include +#include using lyx::pos_type; @@ -80,14 +89,17 @@ using lyx::support::FileSearch; using lyx::support::ForkedcallsController; using lyx::support::IsDirWriteable; using lyx::support::MakeDisplayPath; -using lyx::support::strToUnsignedInt; -using lyx::support::system_lyxdir; +using lyx::support::MakeAbsPath; +using lyx::support::package; using std::endl; using std::istringstream; using std::make_pair; using std::min; +using std::max; using std::string; +using std::mem_fun_ref; +using std::vector; extern BufferList bufferlist; @@ -111,7 +123,7 @@ boost::signals::connection selectioncon; boost::signals::connection lostcon; -/// Get next inset of this class from current cursor position +/// Return an inset of this class if it exists at the current cursor position template T * getInsetByCode(LCursor & cur, InsetBase::Code code) { @@ -124,14 +136,14 @@ T * getInsetByCode(LCursor & cur, InsetBase::Code code) return inset; } - } // anon namespace BufferView::Pimpl::Pimpl(BufferView & bv, LyXView * owner, int width, int height) : bv_(&bv), owner_(owner), buffer_(0), cursor_timeout(400), - using_xterm_cursor(false), cursor_(bv) + using_xterm_cursor(false), cursor_(bv) , + anchor_ref_(0), offset_ref_(0) { xsel_cache_.set = false; @@ -228,8 +240,8 @@ void BufferView::Pimpl::newFile(string const & filename, string const & tname, bool BufferView::Pimpl::loadLyXFile(string const & filename, bool tolastfiles) { - // get absolute path of file and add ".lyx" to the filename if - // necessary + // Get absolute path of file and add ".lyx" + // to the filename if necessary string s = FileSearch(string(), filename, "lyx"); bool const found = !s.empty(); @@ -237,7 +249,7 @@ bool BufferView::Pimpl::loadLyXFile(string const & filename, bool tolastfiles) if (!found) s = filename; - // file already open? + // File already open? if (bufferlist.exists(s)) { string const file = MakeDisplayPath(s, 20); string text = bformat(_("The document %1$s is already " @@ -306,38 +318,40 @@ Painter & BufferView::Pimpl::painter() const } -void BufferView::Pimpl::top_y(int y) -{ - top_y_ = y; -} - - -int BufferView::Pimpl::top_y() const -{ - return top_y_; -} - - void BufferView::Pimpl::setBuffer(Buffer * b) { - lyxerr[Debug::INFO] << "Setting buffer in BufferView (" - << b << ')' << endl; + lyxerr[Debug::INFO] << BOOST_CURRENT_FUNCTION + << "[ b = " << b << "]" << endl; + if (buffer_) disconnectBuffer(); - // set current buffer - buffer_ = b; + // 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 + buffer_ = bufferlist.first(); + owner_->getDialogs().hideBufferDependent(); + } else { + // Set current buffer + buffer_ = b; + } - // reset old cursor - top_y_ = 0; + // Reset old cursor cursor_ = LCursor(*bv_); + anchor_ref_ = 0; + offset_ref_ = 0; + - // if we're quitting lyx, don't bother updating stuff + // If we're quitting lyx, don't bother updating stuff if (quitting) return; if (buffer_) { - lyxerr[Debug::INFO] << "Buffer addr: " << buffer_ << endl; + lyxerr[Debug::INFO] << BOOST_CURRENT_FUNCTION + << "Buffer addr: " << buffer_ << endl; connectBuffer(*buffer_); cursor_.push(buffer_->inset()); @@ -345,19 +359,10 @@ void BufferView::Pimpl::setBuffer(Buffer * b) buffer_->text().init(bv_); buffer_->text().setCurrentFont(cursor_); - // If we don't have a text object for this, we make one - //if (bv_->text() == 0) - // resizeCurrentBuffer(); - // Buffer-dependent dialogs should be updated or // hidden. This should go here because some dialogs (eg ToC) // require bv_->text. owner_->getDialogs().updateBufferDependent(true); - } else { - lyxerr[Debug::INFO] << " No Buffer!" << endl; - // we are closing the buffer, use the first buffer as current - buffer_ = bufferlist.first(); - owner_->getDialogs().hideBufferDependent(); } update(); @@ -376,35 +381,9 @@ void BufferView::Pimpl::setBuffer(Buffer * b) } -bool BufferView::Pimpl::fitCursor() -{ - // to get the correct y cursor info - lyxerr[Debug::DEBUG] << "BufferView::fitCursor" << std::endl; - lyx::par_type const pit = bv_->cursor().bottom().par(); - bv_->text()->redoParagraph(pit); - refreshPar(*bv_, *bv_->text(), pit); - - if (!screen().fitCursor(bv_)) - return false; - updateScrollbar(); - return true; -} - - -void BufferView::Pimpl::redoCurrentBuffer() -{ - lyxerr[Debug::DEBUG] << "BufferView::redoCurrentBuffer" << endl; - if (buffer_ && bv_->text()) { - resizeCurrentBuffer(); - updateScrollbar(); - owner_->updateLayoutChoice(); - } -} - - void BufferView::Pimpl::resizeCurrentBuffer() { - lyxerr[Debug::DEBUG] << "resizeCurrentBuffer" << endl; + lyxerr[Debug::DEBUG] << BOOST_CURRENT_FUNCTION << endl; owner_->busy(true); owner_->message(_("Formatting document...")); @@ -414,12 +393,11 @@ void BufferView::Pimpl::resizeCurrentBuffer() text->init(bv_); update(); - fitCursor(); switchKeyMap(); owner_->busy(false); - // reset the "Formatting..." message + // Reset the "Formatting..." message owner_->clearMessage(); updateScrollbar(); @@ -429,80 +407,112 @@ void BufferView::Pimpl::resizeCurrentBuffer() void BufferView::Pimpl::updateScrollbar() { if (!bv_->text()) { - lyxerr[Debug::DEBUG] << "no text in updateScrollbar" << endl; + lyxerr[Debug::DEBUG] << BOOST_CURRENT_FUNCTION + << " no text in updateScrollbar" << endl; workarea().setScrollbarParams(0, 0, 0); return; } - LyXText const & t = *bv_->text(); + LyXText & t = *bv_->text(); + if (anchor_ref_ > int(t.paragraphs().size()) - 1) { + anchor_ref_ = int(t.paragraphs().size()) - 1; + offset_ref_ = 0; + } lyxerr[Debug::GUI] - << "Updating scrollbar: height: " << t.height() - << " top_y: " << top_y() + << BOOST_CURRENT_FUNCTION + << " Updating scrollbar: height: " << t.paragraphs().size() + << " curr par: " << cursor_.bottom().pit() << " default height " << defaultRowHeight() << endl; - workarea().setScrollbarParams(t.height(), top_y(), defaultRowHeight()); + // It would be better to fix the scrollbar to understand + // values in [0..1] and divide everything by wh + int const wh = workarea().workHeight() / 4; + int const h = t.getPar(anchor_ref_).height(); + workarea().setScrollbarParams(t.paragraphs().size() * wh, anchor_ref_ * wh + int(offset_ref_ * wh / float(h)), int (wh * defaultRowHeight() / float(h))); +// workarea().setScrollbarParams(t.paragraphs().size(), anchor_ref_, 1); } void BufferView::Pimpl::scrollDocView(int value) { - lyxerr[Debug::GUI] << "scrollDocView of " << value << endl; + lyxerr[Debug::GUI] << BOOST_CURRENT_FUNCTION + << "[ value = " << value << "]" << endl; if (!buffer_) return; screen().hideCursor(); - top_y(value); - screen().redraw(*bv_); + int const wh = workarea().workHeight() / 4; + + LyXText & t = *bv_->text(); + + float const bar = value / float(wh * t.paragraphs().size()); + + anchor_ref_ = int(bar * t.paragraphs().size()); + t.redoParagraph(anchor_ref_); + int const h = t.getPar(anchor_ref_).height(); + offset_ref_ = int((bar * t.paragraphs().size() - anchor_ref_) * h); + update(); if (!lyxrc.cursor_follows_scrollbar) return; - int const height = defaultRowHeight(); - int const first = top_y() + height; - int const last = top_y() + workarea().workHeight() - height; + int const height = 2 * defaultRowHeight(); + int const first = height; + int const last = workarea().workHeight() - height; + LCursor & cur = cursor_; - bv_->cursor().reset(bv_->buffer()->inset()); - LyXText * text = bv_->text(); - int y = text->cursorY(bv_->cursor().front()); - if (y < first) - y = first; - if (y > last) - y = last; - text->setCursorFromCoordinates(bv_->cursor(), 0, y); + bv_funcs::CurStatus st = bv_funcs::status(bv_, cur); + switch (st) { + case bv_funcs::CUR_ABOVE: + t.setCursorFromCoordinates(cur, 0, first); + cur.clearSelection(); + break; + case bv_funcs::CUR_BELOW: + t.setCursorFromCoordinates(cur, 0, last); + cur.clearSelection(); + break; + case bv_funcs::CUR_INSIDE: + int const y = bv_funcs::getPos(cur, cur.boundary()).y_; + int const newy = min(last, max(y, first)); + if (y != newy) { + cur.reset(buffer_->inset()); + t.setCursorFromCoordinates(cur, 0, newy); + } + } owner_->updateLayoutChoice(); } -void BufferView::Pimpl::scroll(int lines) +void BufferView::Pimpl::scroll(int /*lines*/) { - if (!buffer_) - return; - - LyXText const * t = bv_->text(); - int const line_height = defaultRowHeight(); - - // The new absolute coordinate - int new_top_y = top_y() + lines * line_height; - - // Restrict to a valid value - new_top_y = std::min(t->height() - 4 * line_height, new_top_y); - new_top_y = std::max(0, new_top_y); - - scrollDocView(new_top_y); - - // Update the scrollbar. - workarea().setScrollbarParams(t->height(), top_y(), defaultRowHeight()); +// if (!buffer_) +// return; +// +// LyXText const * t = bv_->text(); +// int const line_height = defaultRowHeight(); +// +// // The new absolute coordinate +// int new_top_y = top_y() + lines * line_height; +// +// // Restrict to a valid value +// new_top_y = std::min(t->height() - 4 * line_height, new_top_y); +// new_top_y = std::max(0, new_top_y); +// +// scrollDocView(new_top_y); +// +// // Update the scrollbar. +// workarea().setScrollbarParams(t->height(), top_y(), defaultRowHeight()); } void BufferView::Pimpl::workAreaKeyPress(LyXKeySymPtr key, key_modifier::state state) { - bv_->owner()->getLyXFunc().processKeySym(key, state); + owner_->getLyXFunc().processKeySym(key, state); /* This is perhaps a bit of a hack. When we move * around, or type, it's nice to be able to see @@ -511,10 +521,8 @@ void BufferView::Pimpl::workAreaKeyPress(LyXKeySymPtr key, * of the cursor. Note we cannot do this inside * dispatch() itself, because that's called recursively. */ - if (available()) { - cursor_timeout.restart(); + if (available()) screen().showCursor(*bv_); - } } @@ -525,7 +533,7 @@ void BufferView::Pimpl::selectionRequested() if (!available()) return; - LCursor & cur = bv_->cursor(); + LCursor & cur = cursor_; if (!cur.selection()) { xsel_cache_.set = false; @@ -533,11 +541,11 @@ void BufferView::Pimpl::selectionRequested() } if (!xsel_cache_.set || - cur.back() != xsel_cache_.cursor || - cur.anchor_.back() != xsel_cache_.anchor) + cur.top() != xsel_cache_.cursor || + cur.anchor_.top() != xsel_cache_.anchor) { - xsel_cache_.cursor = cur.back(); - xsel_cache_.anchor = cur.anchor_.back(); + xsel_cache_.cursor = cur.top(); + xsel_cache_.anchor = cur.anchor_.top(); xsel_cache_.set = cur.selection(); sel = cur.selectionAsString(false); if (!sel.empty()) @@ -550,7 +558,7 @@ void BufferView::Pimpl::selectionLost() { if (available()) { screen().hideCursor(); - bv_->cursor().clearSelection(); + cursor_.clearSelection(); xsel_cache_.set = false; } } @@ -564,7 +572,7 @@ void BufferView::Pimpl::workAreaResize() bool const widthChange = workarea().workWidth() != work_area_width; bool const heightChange = workarea().workHeight() != work_area_height; - // update from work area + // Update from work area work_area_width = workarea().workWidth(); work_area_height = workarea().workHeight(); @@ -576,34 +584,71 @@ void BufferView::Pimpl::workAreaResize() if (widthChange || heightChange) update(); - // always make sure that the scrollbar is sane. + // Always make sure that the scrollbar is sane. updateScrollbar(); owner_->updateLayoutChoice(); } -void BufferView::Pimpl::update() +bool BufferView::Pimpl::fitCursor() { - //lyxerr << "BufferView::Pimpl::update(), buffer: " << buffer_ << endl; - // fix cursor coordinate cache in case something went wrong + if (bv_funcs::status(bv_, cursor_) == bv_funcs::CUR_INSIDE) { + LyXFont const font = cursor_.getFont(); + int const asc = font_metrics::maxAscent(font); + int const des = font_metrics::maxDescent(font); + Point const p = bv_funcs::getPos(cursor_, cursor_.boundary()); + if (p.y_ - asc >= 0 && p.y_ + des < workarea().workHeight()) + return false; + } + center(); + return true; +} + - // check needed to survive LyX startup +void BufferView::Pimpl::update(Update::flags flags) +{ + lyxerr[Debug::DEBUG] + << BOOST_CURRENT_FUNCTION + << "[fitcursor = " << (flags & Update::FitCursor) + << ", forceupdate = " << (flags & Update::Force) + << ", singlepar = " << (flags & Update::SinglePar) + << "] buffer: " << buffer_ << endl; + + // Check needed to survive LyX startup if (buffer_) { - // update macro store + // Update macro store buffer_->buildMacros(); - // update all 'visible' paragraphs - lyx::par_type beg, end; - getParsInRange(buffer_->paragraphs(), - top_y(), top_y() + workarea().workHeight(), - beg, end); - bv_->text()->redoParagraphs(beg, end); + CoordCache backup; + std::swap(theCoords, backup); - // and the scrollbar - updateScrollbar(); - } - screen().redraw(*bv_); - bv_->owner()->view_state_changed(); + // This, together with doneUpdating(), verifies (using + // asserts) that screen redraw is not called from + // within itself. + theCoords.startUpdating(); + + // First drawing step + ViewMetricsInfo vi = metrics(); + bool forceupdate(flags & Update::Force); + + if ((flags & Update::FitCursor) && fitCursor()) { + forceupdate = true; + vi = metrics(flags & Update::SinglePar); + } + if (forceupdate) { + // Second drawing step + screen().redraw(*bv_, vi); + } else { + // Abort updating of the coord + // cache - just restore the old one + std::swap(theCoords, backup); + } + } else + screen().greyOut(); + + // And the scrollbar + updateScrollbar(); + owner_->view_state_changed(); } @@ -617,8 +662,7 @@ void BufferView::Pimpl::cursorToggle() // have finished but are waiting to communicate this fact // to the rest of LyX. ForkedcallsController & fcc = ForkedcallsController::get(); - if (fcc.processesCompleted()) - fcc.handleCompletedProcesses(); + fcc.handleCompletedProcesses(); } cursor_timeout.restart(); @@ -633,16 +677,16 @@ bool BufferView::Pimpl::available() const Change const BufferView::Pimpl::getCurrentChange() { - if (!bv_->buffer()->params().tracking_changes) + if (!buffer_->params().tracking_changes) return Change(Change::UNCHANGED); LyXText * text = bv_->getLyXText(); - LCursor & cur = bv_->cursor(); + LCursor & cur = cursor_; if (!cur.selection()) return Change(Change::UNCHANGED); - return text->getPar(cur.selBegin().par()). + return text->getPar(cur.selBegin().pit()). lookupChangeFull(cur.selBegin().pos()); } @@ -651,12 +695,12 @@ void BufferView::Pimpl::savePosition(unsigned int i) { if (i >= saved_positions_num) return; - BOOST_ASSERT(bv_->cursor().inTexted()); + BOOST_ASSERT(cursor_.inTexted()); saved_positions[i] = Position(buffer_->fileName(), - bv_->cursor().paragraph().id(), - bv_->cursor().pos()); + cursor_.paragraph().id(), + cursor_.pos()); if (i > 0) - owner_->message(bformat(_("Saved bookmark %1$s"), tostr(i))); + owner_->message(bformat(_("Saved bookmark %1$d"), i)); } @@ -667,7 +711,7 @@ void BufferView::Pimpl::restorePosition(unsigned int i) string const fname = saved_positions[i].filename; - bv_->cursor().clearSelection(); + cursor_.clearSelection(); if (fname != buffer_->fileName()) { Buffer * b = 0; @@ -675,7 +719,8 @@ void BufferView::Pimpl::restorePosition(unsigned int i) b = bufferlist.getBuffer(fname); else { b = bufferlist.newBuffer(fname); - ::loadLyXFile(b, fname); // don't ask, just load it + // Don't ask, just load it + ::loadLyXFile(b, fname); } if (b) setBuffer(b); @@ -685,11 +730,10 @@ void BufferView::Pimpl::restorePosition(unsigned int i) if (par == buffer_->par_iterator_end()) return; - bv_->text()->setCursor(bv_->cursor(), par.pit(), - min(par->size(), saved_positions[i].par_pos)); + bv_->setCursor(makeDocIterator(par, min(par->size(), saved_positions[i].par_pos))); if (i > 0) - owner_->message(bformat(_("Moved to bookmark %1$s"), tostr(i))); + owner_->message(bformat(_("Moved to bookmark %1$d"), i)); } @@ -717,30 +761,19 @@ void BufferView::Pimpl::switchKeyMap() void BufferView::Pimpl::center() { - LyXText * text = bv_->text(); - - bv_->cursor().clearSelection(); - int const half_height = workarea().workHeight() / 2; - int new_y = text->cursorY(bv_->cursor().front()) - half_height; - if (new_y < 0) - new_y = 0; - - // FIXME: look at this comment again ... - // This updates top_y() but means the fitCursor() call - // from the update(FITCUR) doesn't realise that we might - // have moved (e.g. from GOTOPARAGRAPH), so doesn't cause - // the scrollbar to be updated as it should, so we have - // to do it manually. Any operation that does a center() - // and also might have moved top_y() must make sure to call - // updateScrollbar() currently. Never mind that this is a - // pretty obfuscated way of updating text->top_y() - top_y(new_y); + CursorSlice & bot = cursor_.bottom(); + lyx::pit_type const pit = bot.pit(); + bot.text()->redoParagraph(pit); + Paragraph const & par = bot.text()->paragraphs()[pit]; + anchor_ref_ = pit; + offset_ref_ = bv_funcs::coordOffset(cursor_, cursor_.boundary()).y_ + + par.ascent() - workarea().workHeight() / 2; } -void BufferView::Pimpl::stuffClipboard(string const & stuff) const +void BufferView::Pimpl::stuffClipboard(string const & content) const { - workarea().putClipboard(stuff); + workarea().putClipboard(content); } @@ -764,7 +797,7 @@ void BufferView::Pimpl::MenuInsertLyXFile(string const & filenm) make_pair(string(_("Documents|#o#O")), string(lyxrc.document_path)), make_pair(string(_("Examples|#E#e")), - string(AddPath(system_lyxdir(), "examples")))); + string(AddPath(package().system_support(), "examples")))); FileDialog::Result result = fileDlg.open(initpath, @@ -783,82 +816,88 @@ void BufferView::Pimpl::MenuInsertLyXFile(string const & filenm) } } - // get absolute path of file and add ".lyx" to the filename if - // necessary + // Get absolute path of file and add ".lyx" + // to the filename if necessary filename = FileSearch(string(), filename, "lyx"); string const disp_fn = MakeDisplayPath(filename); owner_->message(bformat(_("Inserting document %1$s..."), disp_fn)); - if (bv_->insertLyXFile(filename)) - owner_->message(bformat(_("Document %1$s inserted."), - disp_fn)); - else - owner_->message(bformat(_("Could not insert document %1$s"), - disp_fn)); + + cursor_.clearSelection(); + bv_->getLyXText()->breakParagraph(cursor_); + + BOOST_ASSERT(cursor_.inTexted()); + + string const fname = MakeAbsPath(filename); + bool const res = buffer_->readFile(fname, cursor_.pit()); + resizeCurrentBuffer(); + + string s = res ? _("Document %1$s inserted.") + : _("Could not insert document %1$s"); + owner_->message(bformat(s, disp_fn)); } void BufferView::Pimpl::trackChanges() { - Buffer * buf = bv_->buffer(); - bool const tracking = buf->params().tracking_changes; + bool const tracking = buffer_->params().tracking_changes; if (!tracking) { - ParIterator const end = buf->par_iterator_end(); - for (ParIterator it = buf->par_iterator_begin(); it != end; ++it) - it->trackChanges(); - buf->params().tracking_changes = true; + for_each(buffer_->par_iterator_begin(), + buffer_->par_iterator_end(), + bind(&Paragraph::trackChanges, _1, Change::UNCHANGED)); + buffer_->params().tracking_changes = true; - // we cannot allow undos beyond the freeze point - buf->undostack().clear(); + // We cannot allow undos beyond the freeze point + buffer_->undostack().clear(); } else { update(); - bv_->text()->setCursor(bv_->cursor(), 0, 0); + bv_->text()->setCursor(cursor_, 0, 0); #ifdef WITH_WARNINGS #warning changes FIXME #endif - bool found = lyx::find::findNextChange(bv_); + bool const found = lyx::find::findNextChange(bv_); if (found) { owner_->getDialogs().show("changes"); return; } - ParIterator const end = buf->par_iterator_end(); - for (ParIterator it = buf->par_iterator_begin(); it != end; ++it) - it->untrackChanges(); - buf->params().tracking_changes = false; + for_each(buffer_->par_iterator_begin(), + buffer_->par_iterator_end(), + mem_fun_ref(&Paragraph::untrackChanges)); + + buffer_->params().tracking_changes = false; } - buf->redostack().clear(); + buffer_->redostack().clear(); } bool BufferView::Pimpl::workAreaDispatch(FuncRequest const & cmd0) { - //lyxerr << "BufferView::Pimpl::workAreaDispatch: request: " - // << cmd << std::endl; - // this is only called for mouse related events including + //lyxerr << BOOST_CURRENT_FUNCTION << "[ cmd0 " << cmd0 << "]" << endl; + + // This is only called for mouse related events including // LFUN_FILE_OPEN generated by drag-and-drop. FuncRequest cmd = cmd0; - // handle drag&drop + // Handle drag&drop if (cmd.action == LFUN_FILE_OPEN) { owner_->dispatch(cmd); return true; } - cmd.y += bv_->top_y(); - if (!bv_->buffer()) + if (!buffer_) return false; LCursor cur(*bv_); - cur.push(bv_->buffer()->inset()); - cur.selection() = bv_->cursor().selection(); + cur.push(buffer_->inset()); + cur.selection() = cursor_.selection(); // Doesn't go through lyxfunc, so we need to update // the layout choice etc. ourselves - // e.g. Qt mouse press when no buffer + // E.g. Qt mouse press when no buffer if (!available()) return false; @@ -868,9 +907,12 @@ bool BufferView::Pimpl::workAreaDispatch(FuncRequest const & cmd0) // surrounding LyXText will handle this event. // Build temporary cursor. + cmd.y = min(max(cmd.y,-1), workarea().workHeight()); InsetBase * inset = bv_->text()->editXY(cur, cmd.x, cmd.y); - lyxerr << "hit inset at tip: " << inset << endl; - lyxerr << "created temp cursor:\n" << cur << endl; + //lyxerr << BOOST_CURRENT_FUNCTION + // << " * hit inset at tip: " << inset << endl; + //lyxerr << BOOST_CURRENT_FUNCTION + // << " * created temp cursor:" << cur << endl; // Put anchor at the same position. cur.resetAnchor(); @@ -878,7 +920,7 @@ bool BufferView::Pimpl::workAreaDispatch(FuncRequest const & cmd0) // Try to dispatch to an non-editable inset near this position // via the temp cursor. If the inset wishes to change the real // cursor it has to do so explicitly by using - // cur.bv().cursor() = cur; (or similar)' + // cur.bv().cursor() = cur; (or similar) if (inset) inset->dispatch(cur, cmd); @@ -889,21 +931,23 @@ bool BufferView::Pimpl::workAreaDispatch(FuncRequest const & cmd0) if (cur.result().dispatched()) { // Redraw if requested or necessary. - if (fitCursor() || cur.result().update()) + if (cur.result().update()) + update(Update::FitCursor | Update::Force); + else update(); } - // see workAreaKeyPress + // See workAreaKeyPress cursor_timeout.restart(); screen().showCursor(*bv_); - // skip these when selecting + // Skip these when selecting if (cmd.action != LFUN_MOUSE_MOTION) { owner_->updateLayoutChoice(); owner_->updateToolbars(); } - // slight hack: this is only called currently when we + // Slight hack: this is only called currently when we // clicked somewhere, so we force through the display // of the new status here. owner_->clearMessage(); @@ -913,17 +957,15 @@ bool BufferView::Pimpl::workAreaDispatch(FuncRequest const & cmd0) FuncStatus BufferView::Pimpl::getStatus(FuncRequest const & cmd) { - Buffer * buf = bv_->buffer(); - FuncStatus flag; switch (cmd.action) { case LFUN_UNDO: - flag.enabled(!buf->undostack().empty()); + flag.enabled(!buffer_->undostack().empty()); break; case LFUN_REDO: - flag.enabled(!buf->redostack().empty()); + flag.enabled(!buffer_->redostack().empty()); break; case LFUN_FILE_INSERT: case LFUN_FILE_INSERT_ASCII_PARA: @@ -931,33 +973,50 @@ FuncStatus BufferView::Pimpl::getStatus(FuncRequest const & cmd) case LFUN_FONT_STATE: case LFUN_INSERT_LABEL: case LFUN_BOOKMARK_SAVE: - case LFUN_REF_GOTO: + case LFUN_GOTO_PARAGRAPH: + case LFUN_GOTOERROR: + case LFUN_GOTONOTE: + case LFUN_REFERENCE_GOTO: case LFUN_WORD_FIND: case LFUN_WORD_REPLACE: case LFUN_MARK_OFF: case LFUN_MARK_ON: case LFUN_SETMARK: case LFUN_CENTER: - case LFUN_BEGINNINGBUF: - case LFUN_ENDBUF: - case LFUN_BEGINNINGBUFSEL: - case LFUN_ENDBUFSEL: + case LFUN_BIBDB_ADD: + case LFUN_BIBDB_DEL: + case LFUN_WORDS_COUNT: flag.enabled(true); break; + + case LFUN_LABEL_GOTO: { + flag.enabled(!cmd.argument.empty() + || getInsetByCode(cursor_, InsetBase::REF_CODE)); + break; + } + case LFUN_BOOKMARK_GOTO: - flag.enabled(bv_->isSavedPosition(strToUnsignedInt(cmd.argument))); + flag.enabled(isSavedPosition(convert(cmd.argument))); break; case LFUN_TRACK_CHANGES: flag.enabled(true); - flag.setOnOff(buf->params().tracking_changes); + flag.setOnOff(buffer_->params().tracking_changes); break; + case LFUN_OUTPUT_CHANGES: { + LaTeXFeatures features(*buffer_, buffer_->params(), false); + flag.enabled(buffer_ && buffer_->params().tracking_changes + && features.isAvailable("dvipost")); + flag.setOnOff(buffer_->params().output_changes); + break; + } + case LFUN_MERGE_CHANGES: case LFUN_ACCEPT_CHANGE: // what about these two case LFUN_REJECT_CHANGE: // what about these two case LFUN_ACCEPT_ALL_CHANGES: case LFUN_REJECT_ALL_CHANGES: - flag.enabled(buf && buf->params().tracking_changes); + flag.enabled(buffer_ && buffer_->params().tracking_changes); break; default: flag.enabled(false); @@ -970,9 +1029,11 @@ FuncStatus BufferView::Pimpl::getStatus(FuncRequest const & cmd) bool BufferView::Pimpl::dispatch(FuncRequest const & cmd) { - //lyxerr << "BufferView::Pimpl::dispatch cmd: " << cmd << std::endl; + //lyxerr << BOOST_CURRENT_FUNCTION + // << [ cmd = " << cmd << "]" << endl; + // Make sure that the cached BufferView is correct. - lyxerr[Debug::ACTION] << "BufferView::Pimpl::Dispatch:" + lyxerr[Debug::ACTION] << BOOST_CURRENT_FUNCTION << " action[" << cmd.action << ']' << " arg[" << cmd.argument << ']' << " x[" << cmd.x << ']' @@ -980,7 +1041,7 @@ bool BufferView::Pimpl::dispatch(FuncRequest const & cmd) << " button[" << cmd.button() << ']' << endl; - LCursor & cur = bv_->cursor(); + LCursor & cur = cursor_; switch (cmd.action) { @@ -1023,18 +1084,18 @@ bool BufferView::Pimpl::dispatch(FuncRequest const & cmd) break; case LFUN_BOOKMARK_SAVE: - savePosition(strToUnsignedInt(cmd.argument)); + savePosition(convert(cmd.argument)); break; case LFUN_BOOKMARK_GOTO: - restorePosition(strToUnsignedInt(cmd.argument)); + restorePosition(convert(cmd.argument)); break; - case LFUN_REF_GOTO: { + case LFUN_LABEL_GOTO: { string label = cmd.argument; if (label.empty()) { InsetRef * inset = - getInsetByCode(bv_->cursor(), + getInsetByCode(cursor_, InsetBase::REF_CODE); if (inset) { label = inset->getContents(); @@ -1047,32 +1108,74 @@ bool BufferView::Pimpl::dispatch(FuncRequest const & cmd) break; } + case LFUN_GOTO_PARAGRAPH: { + int const id = convert(cmd.argument); + ParIterator par = buffer_->getParFromID(id); + if (par == buffer_->par_iterator_end()) { + lyxerr[Debug::INFO] << "No matching paragraph found! [" + << id << ']' << endl; + break; + } else { + lyxerr[Debug::INFO] << "Paragraph " << par->id() + << " found." << endl; + } + + // Set the cursor + bv_->setCursor(makeDocIterator(par, 0)); + + update(); + switchKeyMap(); + break; + } + + case LFUN_GOTOERROR: + bv_funcs::gotoInset(bv_, InsetBase::ERROR_CODE, false); + break; + + case LFUN_GOTONOTE: + bv_funcs::gotoInset(bv_, InsetBase::NOTE_CODE, false); + break; + + case LFUN_REFERENCE_GOTO: { + vector tmp; + tmp.push_back(InsetBase::LABEL_CODE); + tmp.push_back(InsetBase::REF_CODE); + bv_funcs::gotoInset(bv_, tmp, true); + break; + } + case LFUN_TRACK_CHANGES: trackChanges(); break; + case LFUN_OUTPUT_CHANGES: { + bool const state = buffer_->params().output_changes; + buffer_->params().output_changes = !state; + break; + } + case LFUN_MERGE_CHANGES: owner_->getDialogs().show("changes"); break; case LFUN_ACCEPT_ALL_CHANGES: { - bv_->cursor().reset(bv_->buffer()->inset()); + cursor_.reset(buffer_->inset()); #ifdef WITH_WARNINGS #warning FIXME changes #endif while (lyx::find::findNextChange(bv_)) - bv_->getLyXText()->acceptChange(bv_->cursor()); + bv_->getLyXText()->acceptChange(cursor_); update(); break; } case LFUN_REJECT_ALL_CHANGES: { - bv_->cursor().reset(bv_->buffer()->inset()); + cursor_.reset(buffer_->inset()); #ifdef WITH_WARNINGS #warning FIXME changes #endif while (lyx::find::findNextChange(bv_)) - bv_->getLyXText()->rejectChange(bv_->cursor()); + bv_->getLyXText()->rejectChange(cursor_); break; } @@ -1110,28 +1213,142 @@ bool BufferView::Pimpl::dispatch(FuncRequest const & cmd) break; case LFUN_CENTER: - bv_->center(); + center(); break; - case LFUN_BEGINNINGBUFSEL: - bv_->cursor().reset(bv_->buffer()->inset()); - if (!cur.selection()) - cur.resetAnchor(); - bv_->text()->cursorTop(cur); - finishUndo(); + case LFUN_BIBDB_ADD: { + LCursor tmpcur = cursor_; + bv_funcs::findInset(tmpcur, InsetBase::BIBTEX_CODE, false); + InsetBibtex * inset = getInsetByCode(tmpcur, + InsetBase::BIBTEX_CODE); + if (inset) + inset->addDatabase(cmd.argument); break; + } - case LFUN_ENDBUFSEL: - bv_->cursor().reset(bv_->buffer()->inset()); - if (!cur.selection()) - cur.resetAnchor(); - bv_->text()->cursorBottom(cur); - finishUndo(); + case LFUN_BIBDB_DEL: { + LCursor tmpcur = cursor_; + bv_funcs::findInset(tmpcur, InsetBase::BIBTEX_CODE, false); + InsetBibtex * inset = getInsetByCode(tmpcur, + InsetBase::BIBTEX_CODE); + if (inset) + inset->delDatabase(cmd.argument); break; + } + case LFUN_WORDS_COUNT: { + DocIterator from, to; + if (cur.selection()) { + from = cur.selectionBegin(); + to = cur.selectionEnd(); + } else { + from = doc_iterator_begin(buffer_->inset()); + to = doc_iterator_end(buffer_->inset()); + } + int const count = countWords(from, to); + string message; + if (count != 1) { + if (cur.selection()) + message = bformat(_("%1$d words in selection."), + count); + else + message = bformat(_("%1$d words in document."), + count); + } + else { + if (cur.selection()) + message = _("One word in selection."); + else + message = _("One word in document."); + } + + Alert::information(_("Count words"), message); + } + break; default: return false; } return true; } + + +ViewMetricsInfo BufferView::Pimpl::metrics(bool singlepar) +{ + // Remove old position cache + theCoords.clear(); + BufferView & bv = *bv_; + LyXText * const text = bv.text(); + if (anchor_ref_ > int(text->paragraphs().size() - 1)) { + anchor_ref_ = int(text->paragraphs().size() - 1); + offset_ref_ = 0; + } + + lyx::pit_type const pit = anchor_ref_; + int pit1 = pit; + int pit2 = pit; + size_t const npit = text->paragraphs().size(); + + lyxerr[Debug::DEBUG] + << BOOST_CURRENT_FUNCTION + << " npit: " << npit + << " pit1: " << pit1 + << " pit2: " << pit2 + << endl; + + // Rebreak anchor par + text->redoParagraph(pit); + int y0 = text->getPar(pit1).ascent() - offset_ref_; + + // Redo paragraphs above cursor if necessary + int y1 = y0; + while (!singlepar && y1 > 0 && pit1 > 0) { + y1 -= text->getPar(pit1).ascent(); + --pit1; + text->redoParagraph(pit1); + y1 -= text->getPar(pit1).descent(); + } + + + // Take care of ascent of first line + y1 -= text->getPar(pit1).ascent(); + + // Normalize anchor for next time + anchor_ref_ = pit1; + offset_ref_ = -y1; + + // Grey at the beginning is ugly + if (pit1 == 0 && y1 > 0) { + y0 -= y1; + y1 = 0; + anchor_ref_ = 0; + } + + // Redo paragraphs below cursor if necessary + int y2 = y0; + while (!singlepar && y2 < bv.workHeight() && pit2 < int(npit) - 1) { + y2 += text->getPar(pit2).descent(); + ++pit2; + text->redoParagraph(pit2); + y2 += text->getPar(pit2).ascent(); + } + + // Take care of descent of last line + y2 += text->getPar(pit2).descent(); + + // The coordinates of all these paragraphs are correct, cache them + int y = y1; + for (lyx::pit_type pit = pit1; pit <= pit2; ++pit) { + y += text->getPar(pit).ascent(); + theCoords.parPos()[text][pit] = Point(0, y); + y += text->getPar(pit).descent(); + } + + lyxerr[Debug::DEBUG] + << BOOST_CURRENT_FUNCTION + << " y1: " << y1 + << " y2: " << y2 + << endl; + + return ViewMetricsInfo(pit1, pit2, y1, y2, singlepar); +}