X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBufferView.cpp;h=d5ee51739c26aa8ffddf2034d4a96cdb706857d8;hb=cf15bd840b71a01ccbb2fbbbabc7066237b4bfd2;hp=124303a1b6ae5fcea43a6b102e08f01ead62ca1b;hpb=e1ce2f92db49d50c688414a32944be8e718faa2e;p=lyx.git diff --git a/src/BufferView.cpp b/src/BufferView.cpp index 124303a1b6..d5ee51739c 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -23,7 +23,6 @@ #include "CoordCache.h" #include "Cursor.h" #include "CutAndPaste.h" -#include "debug.h" #include "DispatchResult.h" #include "EmbeddedFiles.h" #include "ErrorList.h" @@ -31,7 +30,6 @@ #include "FloatList.h" #include "FuncRequest.h" #include "FuncStatus.h" -#include "gettext.h" #include "Intl.h" #include "InsetIterator.h" #include "Language.h" @@ -60,8 +58,8 @@ #include "insets/InsetText.h" #include "frontends/alert.h" +#include "frontends/Application.h" #include "frontends/Delegates.h" -#include "frontends/FileDialog.h" #include "frontends/FontMetrics.h" #include "frontends/Painter.h" #include "frontends/Selection.h" @@ -69,43 +67,25 @@ #include "graphics/Previews.h" #include "support/convert.h" -#include "support/lstrings.h" -#include "support/FileFilterList.h" +#include "support/debug.h" +#include "support/ExceptionMessage.h" #include "support/filetools.h" +#include "support/gettext.h" +#include "support/lstrings.h" #include "support/Package.h" #include "support/types.h" -#include - #include #include #include #include #include -using std::distance; -using std::endl; -using std::ifstream; -using std::istringstream; -using std::istream_iterator; -using std::make_pair; -using std::min; -using std::max; -using std::mem_fun_ref; -using std::string; -using std::vector; +using namespace std; +using namespace lyx::support; namespace lyx { -using support::addPath; -using support::bformat; -using support::FileFilterList; -using support::FileName; -using support::fileSearch; -using support::makeDisplayPath; -using support::makeAbsPath; -using support::package; - namespace Alert = frontend::Alert; namespace { @@ -133,7 +113,7 @@ bool findNextInset(DocIterator & dit, vector const & codes, while (tmpdit) { Inset const * inset = tmpdit.nextInset(); if (inset - && find(codes.begin(), codes.end(), inset->lyxCode()) != codes.end() + && std::find(codes.begin(), codes.end(), inset->lyxCode()) != codes.end() && (contents.empty() || static_cast(inset)->getFirstNonOptParam() == contents)) { dit = tmpdit; @@ -159,7 +139,7 @@ bool findInset(DocIterator & dit, vector const & codes, if (same_content) { Inset const * inset = tmpdit.nextInset(); if (inset - && find(codes.begin(), codes.end(), inset->lyxCode()) != codes.end()) { + && std::find(codes.begin(), codes.end(), inset->lyxCode()) != codes.end()) { contents = static_cast(inset)->getFirstNonOptParam(); } } @@ -197,6 +177,7 @@ void gotoInset(BufferView * bv, vector const & codes, tmpcur.clearSelection(); bv->setCursor(tmpcur); + bv->showCursor(); } @@ -208,7 +189,14 @@ void gotoInset(BufferView * bv, InsetCode code, bool same_content) /// A map from a Text to the associated text metrics -typedef std::map TextMetricsCache; +typedef map TextMetricsCache; + +enum ScreenUpdateStrategy { + NoScreenUpdate, + SingleParUpdate, + FullScreenUpdate, + DecorationUpdate +}; } // anon namespace @@ -222,14 +210,14 @@ typedef std::map TextMetricsCache; struct BufferView::Private { Private(BufferView & bv): wh_(0), cursor_(bv), - anchor_ref_(0), offset_ref_(0), need_centering_(false), + anchor_pit_(0), anchor_ypos_(0), last_inset_(0), gui_(0) {} /// ScrollbarParameters scrollbarParameters_; /// - ViewMetricsInfo metrics_info_; + ScreenUpdateStrategy update_strategy_; /// CoordCache coord_cache_; @@ -244,11 +232,18 @@ struct BufferView::Private /// Cursor cursor_; /// - pit_type anchor_ref_; + pit_type anchor_pit_; + /// + int anchor_ypos_; /// - int offset_ref_; + vector par_height_; + + /// + DocIterator inlineCompletionPos; /// - bool need_centering_; + docstring inlineCompletion; + /// + size_t inlineCompletionUniqueChars; /// keyboard mapping object. Intl intl_; @@ -269,7 +264,7 @@ struct BufferView::Private BufferView::BufferView(Buffer & buf) - : width_(0), height_(0), buffer_(buf), d(new Private(*this)) + : width_(0), height_(0), full_screen_(false), buffer_(buf), d(new Private(*this)) { d->xsel_cache_.set = false; d->intl_.initKeyMapper(lyxrc.use_kbmap); @@ -299,6 +294,24 @@ BufferView::~BufferView() } +int BufferView::rightMargin() const +{ + // The additional test for the case the outliner is opened. + if (!full_screen_ || + !lyxrc.full_screen_limit || + width_ < lyxrc.full_screen_width + 20) + return 10; + + return (width_ - lyxrc.full_screen_width) / 2; +} + + +int BufferView::leftMargin() const +{ + return rightMargin(); +} + + Intl & BufferView::getIntl() { return d->intl_; @@ -346,7 +359,6 @@ bool BufferView::fitCursor() if (p.y_ - asc >= 0 && p.y_ + des < height_) return false; } - center(); return true; } @@ -364,9 +376,7 @@ void BufferView::processUpdateFlags(Update::flags flags) << ", singlepar = " << (flags & Update::SinglePar) << "] buffer: " << &buffer_); - // Update macro store - if (!(cursor().inMathed() && cursor().inMacroMode())) - buffer_.updateMacros(); + buffer_.updateMacros(); // Now do the first drawing step if needed. This consists on updating // the CoordCache in updateMetrics(). @@ -375,114 +385,112 @@ void BufferView::processUpdateFlags(Update::flags flags) // Case when no explicit update is requested. if (!flags) { // no need to redraw anything. - d->metrics_info_.update_strategy = NoScreenUpdate; + d->update_strategy_ = NoScreenUpdate; return; } if (flags == Update::Decoration) { - d->metrics_info_.update_strategy = DecorationUpdate; + d->update_strategy_ = DecorationUpdate; buffer_.changed(); return; } if (flags == Update::FitCursor || flags == (Update::Decoration | Update::FitCursor)) { - bool const fit_cursor = fitCursor(); // tell the frontend to update the screen if needed. - if (fit_cursor) { - updateMetrics(); - buffer_.changed(); + if (fitCursor()) { + showCursor(); return; } if (flags & Update::Decoration) { - d->metrics_info_.update_strategy = DecorationUpdate; + d->update_strategy_ = DecorationUpdate; buffer_.changed(); return; } // no screen update is needed. - d->metrics_info_.update_strategy = NoScreenUpdate; + d->update_strategy_ = NoScreenUpdate; return; } - bool const full_metrics = flags & Update::Force; + bool const full_metrics = flags & Update::Force || !singleParUpdate(); - if (full_metrics || !singleParUpdate()) + if (full_metrics) // We have to update the full screen metrics. updateMetrics(); if (!(flags & Update::FitCursor)) { + // Nothing to do anymore. Trigger a redraw and return buffer_.changed(); return; } - //FIXME: updateMetrics() does not update paragraph position + // updateMetrics() does not update paragraph position // This is done at draw() time. So we need a redraw! buffer_.changed(); - if (!fitCursor()) - // The screen has already been updated thanks to the - // 'buffer_.changed()' call three line above. So no need - // to redraw again. - return; - // The screen has been recentered around the cursor position so - // refresh it: - updateMetrics(); - buffer_.changed(); + if (fitCursor()) { + // The cursor is off screen so ensure it is visible. + // refresh it: + showCursor(); + } } void BufferView::updateScrollbar() { - Text & t = buffer_.text(); - TextMetrics & tm = d->text_metrics_[&t]; + if (height_ == 0) + return; - int const parsize = int(t.paragraphs().size() - 1); - if (d->anchor_ref_ > parsize) { - d->anchor_ref_ = parsize; - d->offset_ref_ = 0; - } + // We prefer fixed size line scrolling. + d->scrollbarParameters_.single_step = defaultRowHeight(); + // We prefer full screen page scrolling. + d->scrollbarParameters_.page_step = height_; + + Text & t = buffer_.text(); + TextMetrics & tm = d->text_metrics_[&t]; - LYXERR(Debug::GUI, BOOST_CURRENT_FUNCTION - << " Updating scrollbar: height: " << t.paragraphs().size() + LYXERR(Debug::GUI, " Updating scrollbar: height: " + << t.paragraphs().size() << " curr par: " << d->cursor_.bottom().pit() << " default height " << defaultRowHeight()); - // It would be better to fix the scrollbar to understand - // values in [0..1] and divide everything by wh - - // estimated average paragraph height: - if (d->wh_ == 0) - d->wh_ = height_ / 4; - - int h = tm.parMetrics(d->anchor_ref_).height(); - - // Normalize anchor/offset (MV): - while (d->offset_ref_ > h && d->anchor_ref_ < parsize) { - d->anchor_ref_++; - d->offset_ref_ -= h; - h = tm.parMetrics(d->anchor_ref_).height(); + size_t const parsize = t.paragraphs().size(); + if (d->par_height_.size() != parsize) { + d->par_height_.clear(); + // FIXME: We assume a default paragraph height of 2 rows. This + // should probably be pondered with the screen width. + d->par_height_.resize(parsize, defaultRowHeight() * 2); } + // Look at paragraph heights on-screen - int sumh = 0; - int nh = 0; - for (pit_type pit = d->anchor_ref_; pit <= parsize; ++pit) { - if (sumh > height_) - break; - int const h2 = tm.parMetrics(pit).height(); - sumh += h2; - nh++; + pair first = tm.first(); + pair last = tm.last(); + for (pit_type pit = first.first; pit <= last.first; ++pit) { + d->par_height_[pit] = tm.parMetrics(pit).height(); + LYXERR(Debug::SCROLLING, "storing height for pit " << pit << " : " + << d->par_height_[pit]); + } + + int top_pos = first.second->position() - first.second->ascent(); + int bottom_pos = last.second->position() + last.second->descent(); + bool first_visible = first.first == 0 && top_pos >= 0; + bool last_visible = last.first + 1 == int(parsize) && bottom_pos <= height_; + if (first_visible && last_visible) { + d->scrollbarParameters_.min = 0; + d->scrollbarParameters_.max = 0; + return; } - BOOST_ASSERT(nh); - int const hav = sumh / nh; - // More realistic average paragraph height - if (hav > d->wh_) - d->wh_ = hav; + d->scrollbarParameters_.min = top_pos; + for (size_t i = 0; i != size_t(first.first); ++i) + d->scrollbarParameters_.min -= d->par_height_[i]; + d->scrollbarParameters_.max = bottom_pos; + for (size_t i = last.first + 1; i != parsize; ++i) + d->scrollbarParameters_.max += d->par_height_[i]; - BOOST_ASSERT(h); - d->scrollbarParameters_.height = (parsize + 1) * d->wh_; - d->scrollbarParameters_.position = d->anchor_ref_ * d->wh_ + int(d->offset_ref_ * d->wh_ / float(h)); - d->scrollbarParameters_.lineScrollHeight = int(d->wh_ * defaultRowHeight() / float(h)); + d->scrollbarParameters_.position = 0; + // The reference is the top position so we remove one page. + d->scrollbarParameters_.max -= d->scrollbarParameters_.page_step; } @@ -492,27 +500,80 @@ ScrollbarParameters const & BufferView::scrollbarParameters() const } +docstring BufferView::toolTip(int x, int y) const +{ + // Get inset under mouse, if there is one. + Inset const * covering_inset = getCoveringInset(buffer_.text(), x, y); + if (!covering_inset) + // No inset, no tooltip... + return docstring(); + return covering_inset->toolTip(*this, x, y); +} + + +docstring BufferView::contextMenu(int x, int y) const +{ + // Get inset under mouse, if there is one. + Inset const * covering_inset = getCoveringInset(buffer_.text(), x, y); + if (covering_inset) + return covering_inset->contextMenu(*this, x, y); + + // FIXME: Do something more elaborate here. + return from_ascii("edit"); +} + + void BufferView::scrollDocView(int value) { - LYXERR(Debug::GUI, BOOST_CURRENT_FUNCTION << "[ value = " << value << "]"); + int const offset = value - d->scrollbarParameters_.position; + // If the offset is less than 2 screen height, prefer to scroll instead. + if (abs(offset) <= 2 * height_) { + scroll(offset); + return; + } - Text & t = buffer_.text(); - TextMetrics & tm = d->text_metrics_[&t]; + // cut off at the top + if (value <= d->scrollbarParameters_.min) { + DocIterator dit = doc_iterator_begin(buffer_.inset()); + showCursor(dit); + LYXERR(Debug::SCROLLING, "scroll to top"); + return; + } - float const bar = value / float(d->wh_ * t.paragraphs().size()); + // cut off at the bottom + if (value >= d->scrollbarParameters_.max) { + DocIterator dit = doc_iterator_end(buffer_.inset()); + dit.backwardPos(); + showCursor(dit); + LYXERR(Debug::SCROLLING, "scroll to bottom"); + return; + } - d->anchor_ref_ = int(bar * t.paragraphs().size()); - if (d->anchor_ref_ > int(t.paragraphs().size()) - 1) - d->anchor_ref_ = int(t.paragraphs().size()) - 1; + // find paragraph at target position + int par_pos = d->scrollbarParameters_.min; + pit_type i = 0; + for (; i != int(d->par_height_.size()); ++i) { + par_pos += d->par_height_[i]; + if (par_pos >= value) + break; + } - tm.redoParagraph(d->anchor_ref_); - int const h = tm.parMetrics(d->anchor_ref_).height(); - d->offset_ref_ = int((bar * t.paragraphs().size() - d->anchor_ref_) * h); - updateMetrics(); - buffer_.changed(); + if (par_pos < value) { + // It seems we didn't find the correct pit so stay on the safe side and + // scroll to bottom. + LYXERR0("scrolling position not found!"); + scrollDocView(d->scrollbarParameters_.max); + return; + } + + DocIterator dit = doc_iterator_begin(buffer_.inset()); + dit.pit() = i; + LYXERR(Debug::SCROLLING, "value = " << value << " -> scroll to pit " << i); + showCursor(dit); } +// FIXME: this method is not working well. void BufferView::setCursorFromScrollbar() { TextMetrics & tm = d->text_metrics_[&buffer_.text()]; @@ -595,7 +656,7 @@ bool BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos, int top_id, pos_type top_pos) { bool success = false; - DocIterator doc_it; + DocIterator dit; d->cursor_.clearSelection(); @@ -603,19 +664,19 @@ bool BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos, // This is the case for a 'live' bookmark when unique paragraph ID // is used to track bookmarks. if (top_id > 0) { - ParIterator par = buffer_.getParFromID(top_id); - if (par != buffer_.par_iterator_end()) { - doc_it = makeDocIterator(par, min(par->size(), top_pos)); + dit = buffer_.getParFromID(top_id); + if (!dit.atEnd()) { + dit.pos() = min(dit.paragraph().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. - size_t const n = doc_it.depth(); + size_t const n = dit.depth(); for (size_t i = 0; i < n; ++i) - if (doc_it[i].inset().editable() != Inset::HIGHLY_EDITABLE) { - doc_it.resize(i); + if (dit[i].inset().editable() != Inset::HIGHLY_EDITABLE) { + dit.resize(i); break; } success = true; @@ -628,20 +689,25 @@ bool BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos, // 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()) { - doc_it = doc_iterator_begin(buffer_.inset()); - doc_it.pit() = bottom_pit; - doc_it.pos() = min(bottom_pos, doc_it.paragraph().size()); + if (bottom_pit < int(buffer_.paragraphs().size())) { + dit = doc_iterator_begin(buffer_.inset()); + + dit.pit() = bottom_pit; + dit.pos() = min(bottom_pos, dit.paragraph().size()); success = true; } if (success) { // Note: only bottom (document) level pit is set. - setCursor(doc_it); + setCursor(dit); // set the current font. d->cursor_.setCurrentFont(); - // center the screen on this new position. - center(); + // To center the screen on this new position we need the + // paragraph position which is computed at draw() time. + // So we need a redraw! + buffer_.changed(); + if (fitCursor()) + showCursor(); } return success; @@ -670,31 +736,77 @@ int BufferView::workWidth() const } -void BufferView::updateOffsetRef() +void BufferView::showCursor() { - // No need to update d->offset_ref_ in this case. - if (!d->need_centering_) - return; + showCursor(d->cursor_); +} + +void BufferView::showCursor(DocIterator const & dit) +{ // We are not properly started yet, delay until resizing is // done. if (height_ == 0) return; - CursorSlice & bot = d->cursor_.bottom(); + LYXERR(Debug::SCROLLING, "recentering!"); + + CursorSlice const & bot = dit.bottom(); TextMetrics & tm = d->text_metrics_[bot.text()]; - ParagraphMetrics const & pm = tm.parMetrics(bot.pit()); - int y = coordOffset(d->cursor_, d->cursor_.boundary()).y_; - d->offset_ref_ = y + pm.ascent() - height_ / 2; - d->need_centering_ = false; -} + pos_type const max_pit = pos_type(bot.text()->paragraphs().size() - 1); + int bot_pit = bot.pit(); + if (bot_pit > max_pit) { + // FIXME: Why does this happen? + LYXERR0("bottom pit is greater that max pit: " + << bot_pit << " > " << max_pit); + bot_pit = max_pit; + } + if (bot_pit == tm.first().first - 1) + tm.newParMetricsUp(); + else if (bot_pit == tm.last().first + 1) + tm.newParMetricsDown(); -void BufferView::center() -{ - d->anchor_ref_ = d->cursor_.bottom().pit(); - d->need_centering_ = true; + if (tm.contains(bot_pit)) { + ParagraphMetrics const & pm = tm.parMetrics(bot_pit); + BOOST_ASSERT(!pm.rows().empty()); + // FIXME: smooth scrolling doesn't work in mathed. + CursorSlice const & cs = dit.innerTextSlice(); + int offset = coordOffset(dit, dit.boundary()).y_; + int ypos = pm.position() + offset; + Dimension const & row_dim = + pm.getRow(cs.pos(), dit.boundary()).dimension(); + if (ypos - row_dim.ascent() < 0) + scrollUp(- ypos + row_dim.ascent()); + else if (ypos + row_dim.descent() > height_) + scrollDown(ypos - height_ + row_dim.descent()); + // else, nothing to do, the cursor is already visible so we just return. + return; + } + + // fix inline completion position + if (d->inlineCompletionPos.fixIfBroken()) + d->inlineCompletionPos = DocIterator(); + + tm.redoParagraph(bot_pit); + ParagraphMetrics const & pm = tm.parMetrics(bot_pit); + int offset = coordOffset(dit, dit.boundary()).y_; + + d->anchor_pit_ = bot_pit; + CursorSlice const & cs = dit.innerTextSlice(); + Dimension const & row_dim = + pm.getRow(cs.pos(), dit.boundary()).dimension(); + + if (d->anchor_pit_ == 0) + d->anchor_ypos_ = offset + pm.ascent(); + else if (d->anchor_pit_ == max_pit) + d->anchor_ypos_ = height_ - offset - row_dim.descent(); + else + d->anchor_ypos_ = defaultRowHeight() * 2 - offset - row_dim.descent(); + + updateMetrics(); + buffer_.changed(); } @@ -733,7 +845,7 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) case LFUN_SCREEN_RECENTER: case LFUN_BIBTEX_DATABASE_ADD: case LFUN_BIBTEX_DATABASE_DEL: - case LFUN_WORDS_COUNT: + case LFUN_STATISTICS: case LFUN_NEXT_INSET_TOGGLE: flag.enabled(true); break; @@ -777,6 +889,7 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) case LFUN_SCREEN_UP: case LFUN_SCREEN_DOWN: + case LFUN_SCROLL: flag.enabled(true); break; @@ -793,7 +906,7 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) case LFUN_LAYOUT: case LFUN_LAYOUT_PARAGRAPH: - flag.enabled(cur.inset().forceDefaultParagraphs(cur.idx())); + flag.enabled(cur.inset().allowParagraphCustomization(cur.idx())); break; case LFUN_INSET_SETTINGS: { @@ -849,56 +962,37 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) } -Update::flags BufferView::dispatch(FuncRequest const & cmd) +bool BufferView::dispatch(FuncRequest const & cmd) { - //lyxerr << BOOST_CURRENT_FUNCTION - // << [ cmd = " << cmd << "]" << endl; + //lyxerr << [ cmd = " << cmd << "]" << endl; // Make sure that the cached BufferView is correct. - LYXERR(Debug::ACTION, BOOST_CURRENT_FUNCTION - << " action[" << cmd.action << ']' + LYXERR(Debug::ACTION, " action[" << cmd.action << ']' << " arg[" << to_utf8(cmd.argument()) << ']' << " x[" << cmd.x << ']' << " y[" << cmd.y << ']' << " button[" << cmd.button() << ']'); Cursor & cur = d->cursor_; - // Default Update flags. - Update::flags updateFlags = Update::Force | Update::FitCursor; switch (cmd.action) { case LFUN_UNDO: cur.message(_("Undo")); cur.clearSelection(); - if (!cur.textUndo()) { + if (!cur.textUndo()) cur.message(_("No further undo information")); - updateFlags = Update::None; - } + else + processUpdateFlags(Update::Force | Update::FitCursor); break; case LFUN_REDO: cur.message(_("Redo")); cur.clearSelection(); - if (!cur.textRedo()) { + if (!cur.textRedo()) cur.message(_("No further redo information")); - updateFlags = Update::None; - } - break; - - case LFUN_FILE_INSERT: - // FIXME UNICODE - menuInsertLyXFile(to_utf8(cmd.argument())); - break; - - case LFUN_FILE_INSERT_PLAINTEXT_PARA: - // FIXME UNICODE - insertPlaintextFile(to_utf8(cmd.argument()), true); - break; - - case LFUN_FILE_INSERT_PLAINTEXT: - // FIXME UNICODE - insertPlaintextFile(to_utf8(cmd.argument()), false); + else + processUpdateFlags(Update::Force | Update::FitCursor); break; case LFUN_FONT_STATE: @@ -933,27 +1027,27 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) for (Buffer * b = &buffer_; i == 0 || b != &buffer_; b = theBufferList().next(b)) { - ParIterator par = b->getParFromID(id); - if (par == b->par_iterator_end()) { + DocIterator dit = b->getParFromID(id); + if (dit.atEnd()) { LYXERR(Debug::INFO, "No matching paragraph found! [" << id << "]."); + ++i; + continue; + } + LYXERR(Debug::INFO, "Paragraph " << dit.paragraph().id() + << " found in buffer `" + << b->absFileName() << "'."); + + if (b == &buffer_) { + // Set the cursor + setCursor(dit); + processUpdateFlags(Update::Force | Update::FitCursor); } else { - LYXERR(Debug::INFO, "Paragraph " << par->id() - << " found in buffer `" - << b->absFileName() << "'."); - - if (b == &buffer_) { - // Set the cursor - setCursor(makeDocIterator(par, 0)); - } else { - // Switch to other buffer view and resend cmd - theLyXFunc().dispatch(FuncRequest( - LFUN_BUFFER_SWITCH, b->absFileName())); - theLyXFunc().dispatch(cmd); - updateFlags = Update::None; - } - break; + // Switch to other buffer view and resend cmd + theLyXFunc().dispatch(FuncRequest( + LFUN_BUFFER_SWITCH, b->absFileName())); + theLyXFunc().dispatch(cmd); } - ++i; + break; } break; } @@ -1026,7 +1120,10 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) break; case LFUN_WORD_FIND: - find(this, cmd); + if (find(this, cmd)) + showCursor(); + else + message(_("String not found!")); break; case LFUN_WORD_REPLACE: { @@ -1071,7 +1168,7 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) break; case LFUN_SCREEN_RECENTER: - center(); + showCursor(); break; case LFUN_BIBTEX_DATABASE_ADD: { @@ -1098,7 +1195,7 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) break; } - case LFUN_WORDS_COUNT: { + case LFUN_STATISTICS: { DocIterator from, to; if (cur.selection()) { from = cur.selectionBegin(); @@ -1107,24 +1204,33 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) from = doc_iterator_begin(buffer_.inset()); to = doc_iterator_end(buffer_.inset()); } - int const count = countWords(from, to); + int const words = countWords(from, to); + int const chars = countChars(from, to, false); + int const chars_blanks = countChars(from, to, true); docstring 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); + if (cur.selection()) + message = _("Statistics for the selection:"); + else + message = _("Statistics for the document:"); + message += "\n\n"; + if (words != 1) + message += bformat(_("%1$d words"), words); + else + message += _("One word"); + message += "\n"; + if (chars_blanks != 1) + message += bformat(_("%1$d characters (including blanks)"), + chars_blanks); + else + message += _("One character (including blanks)"); + message += "\n"; + if (chars != 1) + message += bformat(_("%1$d characters (excluding blanks)"), + chars); + else + message += _("One character (excluding blanks)"); + + Alert::information(_("Statistics"), message); } break; @@ -1133,10 +1239,15 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) buffer_.params().compressed = !buffer_.params().compressed; break; - case LFUN_BUFFER_TOGGLE_EMBEDDING: + case LFUN_BUFFER_TOGGLE_EMBEDDING: { // turn embedding on/off - buffer_.embeddedFiles().enable(!buffer_.params().embedded); + try { + buffer_.embeddedFiles().enable(!buffer_.params().embedded, buffer_); + } catch (ExceptionMessage const & message) { + Alert::error(message.title_, message.details_); + } break; + } case LFUN_NEXT_INSET_TOGGLE: { // this is the real function we want to invoke @@ -1160,9 +1271,11 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) if (!cur.result().dispatched()) cur.dispatch(tmpcmd); - if (cur.result().dispatched()) - cur.clearSelection(); - + if (!cur.result().dispatched()) + // It did not work too; no action needed. + break; + cur.clearSelection(); + processUpdateFlags(Update::SinglePar | Update::FitCursor); break; } @@ -1171,13 +1284,7 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) Point p = getPos(cur, cur.boundary()); if (p.y_ < 0 || p.y_ > height_) { // The cursor is off-screen so recenter before proceeding. - center(); - updateMetrics(); - //FIXME: updateMetrics() does not update paragraph position - // This is done at draw() time. So we need a redraw! - // But no screen update is needed. - d->metrics_info_.update_strategy = NoScreenUpdate; - buffer_.changed(); + showCursor(); p = getPos(cur, cur.boundary()); } scroll(cmd.action == LFUN_SCREEN_UP? - height_ : height_); @@ -1185,13 +1292,18 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) d->text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_); //FIXME: what to do with cur.x_target()? cur.finishUndo(); - // The metrics are already up to date. see scroll() - updateFlags = Update::None; break; } + case LFUN_SCROLL: + lfunScroll(cmd); + break; + case LFUN_SCREEN_UP_SELECT: case LFUN_SCREEN_DOWN_SELECT: { + // Those two are not ready yet for consumption. + return false; + cur.selHandle(true); size_t initial_depth = cur.depth(); Point const p = getPos(cur, cur.boundary()); @@ -1205,17 +1317,16 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) } // FIXME: we need to do a redraw again because of the selection // But no screen update is needed. - d->metrics_info_.update_strategy = NoScreenUpdate; + d->update_strategy_ = NoScreenUpdate; buffer_.changed(); - updateFlags = Update::Force | Update::FitCursor; break; } default: - updateFlags = Update::None; + return false; } - return updateFlags; + return true; } @@ -1261,11 +1372,15 @@ void BufferView::resize(int width, int height) width_ = width; height_ = height; + // Clear the paragraph height cache. + d->par_height_.clear(); + // Redo the metrics. updateMetrics(); } -Inset const * BufferView::getCoveringInset(Text const & text, int x, int y) +Inset const * BufferView::getCoveringInset(Text const & text, + int x, int y) const { TextMetrics & tm = d->text_metrics_[&text]; Inset * inset = tm.checkInsetHit(x, y); @@ -1296,12 +1411,13 @@ Inset const * BufferView::getCoveringInset(Text const & text, int x, int y) void BufferView::mouseEventDispatch(FuncRequest const & cmd0) { - //lyxerr << BOOST_CURRENT_FUNCTION << "[ cmd0 " << cmd0 << "]" << endl; + //lyxerr << "[ cmd0 " << cmd0 << "]" << endl; // This is only called for mouse related events including // LFUN_FILE_OPEN generated by drag-and-drop. FuncRequest cmd = cmd0; + Cursor old = cursor(); Cursor cur(*this); cur.push(buffer_.inset()); cur.selection() = d->cursor_.selection(); @@ -1334,23 +1450,10 @@ void BufferView::mouseEventDispatch(FuncRequest const & cmd0) if (!need_redraw) return; - // if last metrics update was in singlepar mode, WorkArea::redraw() will - // not expose the button for redraw. We adjust here the metrics dimension - // to enable a full redraw in any case as this is not costly. - TextMetrics & tm = d->text_metrics_[&buffer_.text()]; - std::pair firstpm = tm.first(); - std::pair lastpm = tm.last(); - int y1 = firstpm.second->position() - firstpm.second->ascent(); - int y2 = lastpm.second->position() + lastpm.second->descent(); - d->metrics_info_ = ViewMetricsInfo(firstpm.first, lastpm.first, y1, y2, - FullScreenUpdate, buffer_.text().paragraphs().size()); - // Reinitialize anchor to first pit. - d->anchor_ref_ = firstpm.first; - d->offset_ref_ = -y1; - LYXERR(Debug::PAINTING, - "Mouse hover detected at: (" << cmd.x << ", " << cmd.y << ")" - << "\nTriggering redraw: y1: " << y1 << " y2: " << y2 - << " pit1: " << firstpm.first << " pit2: " << lastpm.first); + LYXERR(Debug::PAINTING, "Mouse hover detected at: (" + << cmd.x << ", " << cmd.y << ")"); + + d->update_strategy_ = DecorationUpdate; // This event (moving without mouse click) is not passed further. // This should be changed if it is further utilized. @@ -1376,17 +1479,44 @@ void BufferView::mouseEventDispatch(FuncRequest const & cmd0) if (!cur.result().dispatched()) cur.dispatch(cmd); - //Do we have a selection? + // Notify left insets + if (cur != old) { + old.fixIfBroken(); + bool badcursor = notifyCursorLeaves(old, cur); + if (badcursor) + cursor().fixIfBroken(); + } + + // Do we have a selection? theSelection().haveSelection(cursor().selection()); // If the command has been dispatched, - if (cur.result().dispatched() - // an update is asked, - && cur.result().update()) + if (cur.result().dispatched() || cur.result().update()) processUpdateFlags(cur.result().update()); } +void BufferView::lfunScroll(FuncRequest const & cmd) +{ + string const scroll_type = cmd.getArg(0); + int const scroll_step = + (scroll_type == "line")? d->scrollbarParameters_.single_step + : (scroll_type == "page")? d->scrollbarParameters_.page_step : 0; + if (scroll_step == 0) + return; + string const scroll_quantity = cmd.getArg(1); + if (scroll_quantity == "up") + scrollUp(scroll_step); + else if (scroll_quantity == "down") + scrollDown(scroll_step); + else { + int const scroll_value = convert(scroll_quantity); + if (scroll_value) + scroll(scroll_step * scroll_value); + } +} + + void BufferView::scroll(int y) { if (y > 0) @@ -1402,7 +1532,7 @@ void BufferView::scrollDown(int offset) TextMetrics & tm = d->text_metrics_[text]; int ymax = height_ + offset; while (true) { - std::pair last = tm.last(); + pair last = tm.last(); int bottom_pos = last.second->position() + last.second->descent(); if (last.first + 1 == int(text->paragraphs().size())) { if (bottom_pos <= height_) @@ -1414,7 +1544,7 @@ void BufferView::scrollDown(int offset) break; tm.newParMetricsDown(); } - d->offset_ref_ += offset; + d->anchor_ypos_ -= offset; updateMetrics(); buffer_.changed(); } @@ -1426,7 +1556,7 @@ void BufferView::scrollUp(int offset) TextMetrics & tm = d->text_metrics_[text]; int ymin = - offset; while (true) { - std::pair first = tm.first(); + pair first = tm.first(); int top_pos = first.second->position() - first.second->ascent(); if (first.first == 0) { if (top_pos >= 0) @@ -1438,7 +1568,7 @@ void BufferView::scrollUp(int offset) break; tm.newParMetricsUp(); } - d->offset_ref_ -= offset; + d->anchor_ypos_ += offset; updateMetrics(); buffer_.changed(); } @@ -1463,10 +1593,10 @@ void BufferView::gotoLabel(docstring const & label) { for (InsetIterator it = inset_iterator_begin(buffer_.inset()); it; ++it) { vector labels; - it->getLabelList(buffer_, labels); + it->getLabelList(labels); if (std::find(labels.begin(), labels.end(), label) != labels.end()) { setCursor(it); - processUpdateFlags(Update::FitCursor); + showCursor(); return; } } @@ -1530,6 +1660,8 @@ bool BufferView::checkDepm(Cursor & cur, Cursor & old) if (!changed) return false; + d->cursor_ = cur; + updateLabels(buffer_); updateMetrics(); @@ -1550,8 +1682,12 @@ bool BufferView::mouseSetCursor(Cursor & cur, bool select) // Has the cursor just left the inset? bool badcursor = false; bool leftinset = (&d->cursor_.inset() != &cur.inset()); - if (leftinset) + if (leftinset) { + d->cursor_.fixIfBroken(); badcursor = notifyCursorLeaves(d->cursor_, cur); + if (badcursor) + cur.fixIfBroken(); + } // FIXME: shift-mouse selection doesn't work well across insets. bool do_selection = select && &d->cursor_.anchor().inset() == &cur.inset(); @@ -1564,24 +1700,7 @@ bool BufferView::mouseSetCursor(Cursor & cur, bool select) if (!do_selection && !badcursor && d->cursor_.inTexted()) update |= checkDepm(cur, d->cursor_); - // if the cursor was in an empty script inset and the new - // 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 - // The code below could maybe be moved to a DocIterator method. - //lyxerr << "cur before " << cur <cursor_.setCursor(dit); + d->cursor_.setCursor(cur); d->cursor_.boundary(cur.boundary()); if (do_selection) d->cursor_.setSelection(); @@ -1608,6 +1727,9 @@ void BufferView::putSelectionAt(DocIterator const & cur, } else d->cursor_.setSelection(d->cursor_, length); } + // Ensure a redraw happens in any case because the new selection could + // possibly be on the same screen as the previous selection. + processUpdateFlags(Update::Force | Update::FitCursor); } @@ -1625,13 +1747,7 @@ Cursor const & BufferView::cursor() const pit_type BufferView::anchor_ref() const { - return d->anchor_ref_; -} - - -ViewMetricsInfo const & BufferView::viewMetricsInfo() -{ - return d->metrics_info_; + return d->anchor_pit_; } @@ -1642,6 +1758,10 @@ bool BufferView::singleParUpdate() TextMetrics & tm = textMetrics(&buftext); int old_height = tm.parMetrics(bottom_pit).height(); + // make sure inline completion pointer is ok + if (d->inlineCompletionPos.fixIfBroken()) + d->inlineCompletionPos = DocIterator(); + // In Single Paragraph mode, rebreak only // the (main text, not inset!) paragraph containing the cursor. // (if this paragraph contains insets etc., rebreaking will @@ -1653,13 +1773,10 @@ bool BufferView::singleParUpdate() // the singlePar optimisation. return false; - int y1 = pm.position() - pm.ascent(); - int y2 = pm.position() + pm.descent(); - d->metrics_info_ = ViewMetricsInfo(bottom_pit, bottom_pit, y1, y2, - SingleParUpdate, buftext.paragraphs().size()); - LYXERR(Debug::PAINTING, BOOST_CURRENT_FUNCTION - << "\ny1: " << y1 - << " y2: " << y2 + d->update_strategy_ = SingleParUpdate; + + LYXERR(Debug::PAINTING, "\ny1: " << pm.position() - pm.ascent() + << " y2: " << pm.position() + pm.descent() << " pit: " << bottom_pit << " singlepar: 1"); return true; @@ -1671,11 +1788,6 @@ void BufferView::updateMetrics() Text & buftext = buffer_.text(); pit_type const npit = int(buftext.paragraphs().size()); - if (d->anchor_ref_ > int(npit - 1)) { - d->anchor_ref_ = int(npit - 1); - d->offset_ref_ = 0; - } - // Clear out the position cache in case of full screen redraw, d->coord_cache_.clear(); @@ -1686,63 +1798,71 @@ void BufferView::updateMetrics() TextMetrics & tm = textMetrics(&buftext); - pit_type const pit = d->anchor_ref_; - int pit1 = pit; - int pit2 = pit; + // make sure inline completion pointer is ok + if (d->inlineCompletionPos.fixIfBroken()) + d->inlineCompletionPos = DocIterator(); + + if (d->anchor_pit_ >= npit) + // The anchor pit must have been deleted... + d->anchor_pit_ = npit - 1; // Rebreak anchor paragraph. - tm.redoParagraph(pit); - - // Take care of anchor offset if case a recentering is needed. - updateOffsetRef(); + tm.redoParagraph(d->anchor_pit_); + ParagraphMetrics & anchor_pm = tm.par_metrics_[d->anchor_pit_]; + + // position anchor + if (d->anchor_pit_ == 0) { + int scrollRange = d->scrollbarParameters_.max - d->scrollbarParameters_.min; + + // Complete buffer visible? Then it's easy. + if (scrollRange == 0) + d->anchor_ypos_ = anchor_pm.ascent(); + + // FIXME: Some clever handling needed to show + // the _first_ paragraph up to the top if the cursor is + // in the first line. + } + anchor_pm.setPosition(d->anchor_ypos_); - int y0 = tm.parMetrics(pit).ascent() - d->offset_ref_; + LYXERR(Debug::PAINTING, "metrics: " + << " anchor pit = " << d->anchor_pit_ + << " anchor ypos = " << d->anchor_ypos_); // Redo paragraphs above anchor if necessary. - int y1 = y0; - while (y1 > 0 && pit1 > 0) { - y1 -= tm.parMetrics(pit1).ascent(); - --pit1; + int y1 = d->anchor_ypos_ - anchor_pm.ascent(); + // We are now just above the anchor paragraph. + pit_type pit1 = d->anchor_pit_ - 1; + for (; pit1 >= 0 && y1 >= 0; --pit1) { tm.redoParagraph(pit1); - y1 -= tm.parMetrics(pit1).descent(); - } - - // Take care of ascent of first line - y1 -= tm.parMetrics(pit1).ascent(); - - // Normalize anchor for next time - d->anchor_ref_ = pit1; - d->offset_ref_ = -y1; - - // Grey at the beginning is ugly - if (pit1 == 0 && y1 > 0) { - y0 -= y1; - y1 = 0; - d->anchor_ref_ = 0; + ParagraphMetrics & pm = tm.par_metrics_[pit1]; + y1 -= pm.descent(); + // Save the paragraph position in the cache. + pm.setPosition(y1); + y1 -= pm.ascent(); } // Redo paragraphs below the anchor if necessary. - int y2 = y0; - while (y2 < height_ && pit2 < int(npit) - 1) { - y2 += tm.parMetrics(pit2).descent(); - ++pit2; + int y2 = d->anchor_ypos_ + anchor_pm.descent(); + // We are now just below the anchor paragraph. + pit_type pit2 = d->anchor_pit_ + 1; + for (; pit2 < npit && y2 <= height_; ++pit2) { tm.redoParagraph(pit2); - y2 += tm.parMetrics(pit2).ascent(); + ParagraphMetrics & pm = tm.par_metrics_[pit2]; + y2 += pm.ascent(); + // Save the paragraph position in the cache. + pm.setPosition(y2); + y2 += pm.descent(); } - // Take care of descent of last line - y2 += tm.parMetrics(pit2).descent(); - - LYXERR(Debug::PAINTING, BOOST_CURRENT_FUNCTION - << "\n y1: " << y1 - << " y2: " << y2 - << " pit1: " << pit1 - << " pit2: " << pit2 - << " npit: " << npit - << " singlepar: 0"); + LYXERR(Debug::PAINTING, "Metrics: " + << " anchor pit = " << d->anchor_pit_ + << " anchor ypos = " << d->anchor_ypos_ + << " y1 = " << y1 + << " y2 = " << y2 + << " pit1 = " << pit1 + << " pit2 = " << pit2); - d->metrics_info_ = ViewMetricsInfo(pit1, pit2, y1, y2, - FullScreenUpdate, npit); + d->update_strategy_ = FullScreenUpdate; if (lyxerr.debugging(Debug::WORKAREA)) { LYXERR(Debug::WORKAREA, "BufferView::updateMetrics"); @@ -1751,72 +1871,37 @@ void BufferView::updateMetrics() } -void BufferView::menuInsertLyXFile(string const & filenm) +void BufferView::insertLyXFile(FileName const & fname) { BOOST_ASSERT(d->cursor_.inTexted()); - string filename = filenm; - - if (filename.empty()) { - // Launch a file browser - // FIXME UNICODE - string initpath = lyxrc.document_path; - string const trypath = buffer_.filePath(); - // If directory is writeable, use this as default. - if (FileName(trypath).isDirWritable()) - initpath = trypath; - - // FIXME UNICODE - FileDialog dlg(_("Select LyX document to insert"), LFUN_FILE_INSERT); - dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path)); - dlg.setButton2(_("Examples|#E#e"), - from_utf8(addPath(package().system_support().absFilename(), - "examples"))); - - FileDialog::Result result = - dlg.open(from_utf8(initpath), - FileFilterList(_("LyX Documents (*.lyx)")), - docstring()); - - if (result.first == FileDialog::Later) - return; - - // FIXME UNICODE - filename = to_utf8(result.second); - - // check selected filename - if (filename.empty()) { - // emit message signal. - message(_("Canceled.")); - return; - } - } // Get absolute path of file and add ".lyx" // to the filename if necessary - filename = fileSearch(string(), filename, "lyx").absFilename(); + FileName filename = fileSearch(string(), fname.absFilename(), "lyx"); - docstring const disp_fn = makeDisplayPath(filename); + docstring const disp_fn = makeDisplayPath(filename.absFilename()); // emit message signal. message(bformat(_("Inserting document %1$s..."), disp_fn)); docstring res; Buffer buf("", false); - if (buf.loadLyXFile(FileName(filename))) { + if (buf.loadLyXFile(filename)) { ErrorList & el = buffer_.errorList("Parse"); // Copy the inserted document error list into the current buffer one. el = buf.errorList("Parse"); buffer_.undo().recordUndo(d->cursor_); cap::pasteParagraphList(d->cursor_, buf.paragraphs(), - buf.params().getTextClassPtr(), el); + buf.params().documentClassPtr(), el); res = _("Document %1$s inserted."); } else { res = _("Could not insert document %1$s"); } + updateMetrics(); + buffer_.changed(); // emit message signal. message(bformat(res, disp_fn)); buffer_.errors("Parse"); - updateMetrics(); } @@ -1882,7 +1967,7 @@ Point BufferView::coordOffset(DocIterator const & dit, bool boundary) const int pos = sl.pos(); if (pos && boundary) --pos; -// lyxerr << "coordOffset: boundary:" << boundary << " depth:" << dit.depth() << " pos:" << pos << " sl.pos:" << sl.pos() << std::endl; +// lyxerr << "coordOffset: boundary:" << boundary << " depth:" << dit.depth() << " pos:" << pos << " sl.pos:" << sl.pos() << endl; rend = pm.pos2row(pos); } else rend = pm.pos2row(sl.pos()); @@ -1914,7 +1999,7 @@ Point BufferView::getPos(DocIterator const & dit, bool boundary) const { CursorSlice const & bot = dit.bottom(); TextMetrics const & tm = textMetrics(bot.text()); - if (!tm.has(bot.pit())) + if (!tm.contains(bot.pit())) return Point(-1, -1); Point p = coordOffset(dit, boundary); // offset from outer paragraph @@ -1928,11 +2013,10 @@ void BufferView::draw(frontend::Painter & pain) LYXERR(Debug::PAINTING, "\t\t*** START DRAWING ***"); Text & text = buffer_.text(); TextMetrics const & tm = d->text_metrics_[&text]; - int const y = d->metrics_info_.y1 - + tm.parMetrics(d->metrics_info_.p1).ascent(); + int const y = tm.first().second->position(); PainterInfo pi(this, pain); - switch (d->metrics_info_.update_strategy) { + switch (d->update_strategy_) { case NoScreenUpdate: // If no screen painting is actually needed, only some the different @@ -1943,9 +2027,11 @@ void BufferView::draw(frontend::Painter & pain) break; case SingleParUpdate: - // Only the current outermost paragraph will be redrawn. pi.full_repaint = false; - tm.drawParagraph(pi, d->metrics_info_.p1, 0, y); + // In general, only the current row of the outermost paragraph + // will be redrawn. Particular cases where selection spans + // multiple paragraph are correctly detected in TextMetrics. + tm.draw(pi, 0, y); break; case DecorationUpdate: @@ -1957,59 +2043,64 @@ void BufferView::draw(frontend::Painter & pain) // The whole screen, including insets, will be refreshed. pi.full_repaint = true; - // Clear background (if not delegated to rows) - pain.fillRectangle(0, d->metrics_info_.y1, width_, - d->metrics_info_.y2 - d->metrics_info_.y1, + // Clear background. + pain.fillRectangle(0, 0, width_, height_, buffer_.inset().backgroundColor()); - tm.draw(pi, 0, y); - // and grey out above (should not happen later) - if (d->metrics_info_.y1 > 0) - pain.fillRectangle(0, 0, width_, - d->metrics_info_.y1, Color_bottomarea); + // Draw everything. + tm.draw(pi, 0, y); // and possibly grey out below - if (d->metrics_info_.y2 < height_) - pain.fillRectangle(0, d->metrics_info_.y2, width_, - height_ - d->metrics_info_.y2, Color_bottomarea); + pair lastpm = tm.last(); + int const y2 = lastpm.second->position() + lastpm.second->descent(); + if (y2 < height_) + pain.fillRectangle(0, y2, width_, height_ - y2, Color_bottomarea); break; } - LYXERR(Debug::PAINTING, "\n\t\t*** END DRAWING ***"); -} + // The scrollbar needs an update. + updateScrollbar(); -void BufferView::message(docstring const & msg) -{ - if (d->gui_) - d->gui_->message(msg); + // Normalize anchor for next time + pair firstpm = tm.first(); + pair lastpm = tm.last(); + for (pit_type pit = firstpm.first; pit <= lastpm.first; ++pit) { + ParagraphMetrics const & pm = tm.parMetrics(pit); + if (pm.position() + pm.descent() > 0) { + d->anchor_pit_ = pit; + d->anchor_ypos_ = pm.position(); + break; + } + } + LYXERR(Debug::PAINTING, "Found new anchor pit = " << d->anchor_pit_ + << " anchor ypos = " << d->anchor_ypos_); } -void BufferView::showDialog(std::string const & name) +void BufferView::message(docstring const & msg) { if (d->gui_) - d->gui_->showDialog(name); + d->gui_->message(msg); } -void BufferView::showDialogWithData(std::string const & name, - std::string const & data) +void BufferView::showDialog(string const & name) { if (d->gui_) - d->gui_->showDialogWithData(name, data); + d->gui_->showDialog(name, string()); } -void BufferView::showInsetDialog(std::string const & name, - std::string const & data, Inset * inset) +void BufferView::showDialog(string const & name, + string const & data, Inset * inset) { if (d->gui_) - d->gui_->showInsetDialog(name, data, inset); + d->gui_->showDialog(name, data, inset); } -void BufferView::updateDialog(std::string const & name, std::string const & data) +void BufferView::updateDialog(string const & name, string const & data) { if (d->gui_) d->gui_->updateDialog(name, data); @@ -2023,31 +2114,9 @@ void BufferView::setGuiDelegate(frontend::GuiBufferViewDelegate * gui) // FIXME: Move this out of BufferView again -docstring BufferView::contentsOfPlaintextFile(string const & f, - bool asParagraph) +docstring BufferView::contentsOfPlaintextFile(FileName const & fname) { - FileName fname(f); - - if (fname.empty()) { - FileDialog dlg(_("Select file to insert"), - ( asParagraph - ? LFUN_FILE_INSERT_PLAINTEXT_PARA - : LFUN_FILE_INSERT_PLAINTEXT) ); - - FileDialog::Result result = - dlg.open(from_utf8(buffer().filePath()), - FileFilterList(), docstring()); - - if (result.first == FileDialog::Later) - return docstring(); - - fname = makeAbsPath(to_utf8(result.second)); - - if (fname.empty()) - return docstring(); - } - - if (!fname.isReadable()) { + if (!fname.isReadableFile()) { docstring const error = from_ascii(strerror(errno)); docstring const file = makeDisplayPath(fname.absFilename(), 50); docstring const text = @@ -2057,36 +2126,16 @@ docstring BufferView::contentsOfPlaintextFile(string const & f, return docstring(); } - ifstream ifs(fname.toFilesystemEncoding().c_str()); - if (!ifs) { - docstring const error = from_ascii(strerror(errno)); + if (!fname.isReadableFile()) { docstring const file = makeDisplayPath(fname.absFilename(), 50); docstring const text = - bformat(_("Could not open the specified document\n" - "%1$s\ndue to the error: %2$s"), file, error); + bformat(_("%1$s\n is not readable."), file); Alert::error(_("Could not open file"), text); return docstring(); } - ifs.unsetf(std::ios::skipws); - istream_iterator ii(ifs); - istream_iterator end; -#if !defined(USE_INCLUDED_STRING) && !defined(STD_STRING_IS_GOOD) - // We use this until the compilers get better... - std::vector tmp; - copy(ii, end, back_inserter(tmp)); - string const tmpstr(tmp.begin(), tmp.end()); -#else - // This is what we want to use and what we will use once the - // compilers get good enough. - //string tmpstr(ii, end); // yet a reason for using std::string - // alternate approach to get the file into a string: - string tmpstr; - copy(ii, end, back_inserter(tmpstr)); -#endif - // FIXME UNICODE: We don't know the encoding of the file - docstring file_content = from_utf8(tmpstr); + docstring file_content = fname.fileContents("UTF-8"); if (file_content.empty()) { Alert::error(_("Reading not UTF-8 encoded file"), _("The file is not UTF-8 encoded.\n" @@ -2094,16 +2143,16 @@ docstring BufferView::contentsOfPlaintextFile(string const & f, "If this does not give the correct result\n" "then please change the encoding of the file\n" "to UTF-8 with a program other than LyX.\n")); - file_content = from_local8bit(tmpstr); + file_content = fname.fileContents("local8bit"); } return normalize_c(file_content); } -void BufferView::insertPlaintextFile(string const & f, bool asParagraph) +void BufferView::insertPlaintextFile(FileName const & f, bool asParagraph) { - docstring const tmpstr = contentsOfPlaintextFile(f, asParagraph); + docstring const tmpstr = contentsOfPlaintextFile(f); if (tmpstr.empty()) return; @@ -2115,6 +2164,72 @@ void BufferView::insertPlaintextFile(string const & f, bool asParagraph) cur.innerText()->insertStringAsParagraphs(cur, tmpstr); else cur.innerText()->insertStringAsLines(cur, tmpstr); + + updateMetrics(); + buffer_.changed(); +} + + +docstring const & BufferView::inlineCompletion() const +{ + return d->inlineCompletion; +} + + +size_t const & BufferView::inlineCompletionUniqueChars() const +{ + return d->inlineCompletionUniqueChars; +} + + +DocIterator const & BufferView::inlineCompletionPos() const +{ + return d->inlineCompletionPos; +} + + +bool samePar(DocIterator const & a, DocIterator const & b) +{ + if (a.empty() && b.empty()) + return true; + if (a.empty() || b.empty()) + return false; + return &a.innerParagraph() == &b.innerParagraph(); +} + + +void BufferView::setInlineCompletion(Cursor & cur, DocIterator const & pos, + docstring const & completion, size_t uniqueChars) +{ + uniqueChars = min(completion.size(), uniqueChars); + bool changed = d->inlineCompletion != completion + || d->inlineCompletionUniqueChars != uniqueChars; + bool singlePar = true; + d->inlineCompletion = completion; + d->inlineCompletionUniqueChars = min(completion.size(), uniqueChars); + + //lyxerr << "setInlineCompletion pos=" << pos << " completion=" << completion << " uniqueChars=" << uniqueChars << std::endl; + + // at new position? + DocIterator const & old = d->inlineCompletionPos; + if (old != pos) { + //lyxerr << "inlineCompletionPos changed" << std::endl; + // old or pos are in another paragraph? + if ((!samePar(cur, pos) && !pos.empty()) + || (!samePar(cur, old) && !old.empty())) { + singlePar = false; + //lyxerr << "different paragraph" << std::endl; + } + d->inlineCompletionPos = pos; + } + + // set update flags + if (changed) { + if (singlePar && !(cur.disp_.update() & Update::Force)) + cur.updateFlags(cur.disp_.update() | Update::SinglePar); + else + cur.updateFlags(cur.disp_.update() | Update::Force); + } } } // namespace lyx