]> git.lyx.org Git - lyx.git/blobdiff - src/BufferView.C
update comments.
[lyx.git] / src / BufferView.C
index 6760ac736926c2504af1c1cd1de616f6e35b1c69..154755c15e0105b13c812fe3866b6af956101bb1 100644 (file)
@@ -20,6 +20,7 @@
 #include "buffer_funcs.h"
 #include "bufferlist.h"
 #include "bufferparams.h"
+#include "bufferview_funcs.h"
 #include "coordcache.h"
 #include "CutAndPaste.h"
 #include "debug.h"
@@ -88,7 +89,6 @@ using support::fileSearch;
 using support::isDirWriteable;
 using support::isFileReadable;
 using support::makeDisplayPath;
-using support::makeAbsPath;
 using support::package;
 
 using std::distance;
@@ -125,7 +125,7 @@ BufferView::BufferView()
        : width_(0), height_(0), buffer_(0), wh_(0),
          cursor_(*this),
          multiparsel_cache_(false), anchor_ref_(0), offset_ref_(0),
-         intl_(new Intl)
+         intl_(new Intl), last_inset_(0)
 {
        xsel_cache_.set = false;
        intl_->initKeyMapper(lyxrc.use_kbmap);
@@ -187,7 +187,7 @@ void BufferView::setBuffer(Buffer * b)
                                    << "Buffer addr: " << buffer_ << endl;
                cursor_.push(buffer_->inset());
                cursor_.resetAnchor();
-               buffer_->text().init(this);
+               updateLabels(*buffer_);
                buffer_->text().setCurrentFont(cursor_);
                if (buffer_->getCursor().size() > 0 &&
                    buffer_->getAnchor().size() > 0)
@@ -301,8 +301,7 @@ void BufferView::resize()
 
        lyxerr[Debug::DEBUG] << BOOST_CURRENT_FUNCTION << endl;
 
-       buffer_->text().init(this);
-       update();
+       updateMetrics(false);
        switchKeyMap();
 }
 
@@ -334,8 +333,12 @@ bool BufferView::multiParSel()
 }
 
 
-std::pair<bool, bool> BufferView::update(Update::flags flags)
+bool BufferView::update(Update::flags flags)
 {
+       // last_inset_ points to the last visited inset. This pointer may become
+       // invalid because of keyboard editing. Since all such operations
+       // causes screen update(), I reset last_inset_ to avoid such a problem.
+       last_inset_ = 0;
        // This is close to a hot-path.
        if (lyxerr.debugging(Debug::DEBUG)) {
                lyxerr[Debug::DEBUG]
@@ -348,7 +351,7 @@ std::pair<bool, bool> BufferView::update(Update::flags flags)
 
        // Check needed to survive LyX startup
        if (!buffer_)
-               return make_pair(false, false);
+               return false;
 
        if (lyxerr.debugging(Debug::WORKAREA)) {
                lyxerr[Debug::WORKAREA] << "BufferView::update" << std::endl;
@@ -363,16 +366,16 @@ std::pair<bool, bool> BufferView::update(Update::flags flags)
 
        // Case when no explicit update is requested.
        if (!flags) {
-               // no need to do anything.
-               return make_pair(false, false);
+               // no need to redraw anything.
+               return false;
        }
 
        if (flags == Update::FitCursor) {
                bool const fit_cursor = fitCursor();
                if (fit_cursor)
                        updateMetrics(false);
-               // tell the frontend to update the screen.
-               return make_pair(fit_cursor, false);
+               // tell the frontend to update the screen if needed.
+               return fit_cursor;
        }
 
        bool full_metrics = flags & Update::Force;
@@ -386,7 +389,7 @@ std::pair<bool, bool> BufferView::update(Update::flags flags)
                updateMetrics(false);
 
        // tell the frontend to update the screen.
-       return make_pair(true, single_par);
+       return true;
 }
 
 
@@ -400,6 +403,8 @@ void BufferView::updateScrollbar()
        }
 
        LyXText & t = buffer_->text();
+       TextMetrics & tm = text_metrics_[&t];
+
        int const parsize = int(t.paragraphs().size() - 1);
        if (anchor_ref_ >  parsize)  {
                anchor_ref_ = parsize;
@@ -420,13 +425,14 @@ void BufferView::updateScrollbar()
        // estimated average paragraph height:
        if (wh_ == 0)
                wh_ = height_ / 4;
-       int h = t.getPar(anchor_ref_).height();
+
+       int h = tm.parMetrics(anchor_ref_).height();
 
        // Normalize anchor/offset (MV):
        while (offset_ref_ > h && anchor_ref_ < parsize) {
                anchor_ref_++;
                offset_ref_ -= h;
-               h = t.getPar(anchor_ref_).height();
+               h = tm.parMetrics(anchor_ref_).height();
        }
        // Look at paragraph heights on-screen
        int sumh = 0;
@@ -434,7 +440,7 @@ void BufferView::updateScrollbar()
        for (pit_type pit = anchor_ref_; pit <= parsize; ++pit) {
                if (sumh > height_)
                        break;
-               int const h2 = t.getPar(pit).height();
+               int const h2 = tm.parMetrics(pit).height();
                sumh += h2;
                nh++;
        }
@@ -467,14 +473,16 @@ void BufferView::scrollDocView(int value)
                return;
 
        LyXText & t = buffer_->text();
+       TextMetrics & tm = text_metrics_[&t];
 
        float const bar = value / float(wh_ * t.paragraphs().size());
 
        anchor_ref_ = int(bar * t.paragraphs().size());
        if (anchor_ref_ >  int(t.paragraphs().size()) - 1)
                anchor_ref_ = int(t.paragraphs().size()) - 1;
-       t.redoParagraph(*this, anchor_ref_);
-       int const h = t.getPar(anchor_ref_).height();
+
+       tm.redoParagraph(anchor_ref_);
+       int const h = tm.parMetrics(anchor_ref_).height();
        offset_ref_ = int((bar * t.paragraphs().size() - anchor_ref_) * h);
        updateMetrics(false);
 }
@@ -571,12 +579,13 @@ int BufferView::workWidth() const
 void BufferView::center()
 {
        CursorSlice & bot = cursor_.bottom();
+       TextMetrics & tm = text_metrics_[bot.text()];
        pit_type const pit = bot.pit();
-       bot.text()->redoParagraph(*this, pit);
-       Paragraph const & par = bot.text()->paragraphs()[pit];
+       tm.redoParagraph(pit);
+       ParagraphMetrics const & pm = tm.parMetrics(pit);
        anchor_ref_ = pit;
        offset_ref_ = bv_funcs::coordOffset(*this, cursor_, cursor_.boundary()).y_
-               + par.ascent() - height_ / 2;
+               + pm.ascent() - height_ / 2;
 }
 
 
@@ -721,7 +730,7 @@ bool BufferView::dispatch(FuncRequest const & cmd)
                break;
 
        case LFUN_FONT_STATE:
-               cur.message(from_utf8(cur.currentState()));
+               cur.message(cur.currentState());
                break;
 
        case LFUN_BOOKMARK_SAVE:
@@ -1007,27 +1016,28 @@ void BufferView::clearSelection()
        if (buffer_) {
                cursor_.clearSelection();
                xsel_cache_.set = false;
+               // The buffer did not really change, but this causes the
+               // redraw we need because we cleared the selection above.
+               buffer_->changed();
        }
 }
 
 
 void BufferView::workAreaResize(int width, int height)
 {
-       bool const widthChange = width != width_;
-       bool const heightChange = height != height_;
-
        // Update from work area
        width_ = width;
        height_ = height;
 
+       // The complete text metrics will be redone.
+       text_metrics_.clear();
+
        if (buffer_)
                resize();
-
-       update();
 }
 
 
-std::pair<bool, bool> BufferView::workAreaDispatch(FuncRequest const & cmd0)
+bool BufferView::workAreaDispatch(FuncRequest const & cmd0)
 {
        //lyxerr << BOOST_CURRENT_FUNCTION << "[ cmd0 " << cmd0 << "]" << endl;
 
@@ -1037,7 +1047,7 @@ std::pair<bool, bool> BufferView::workAreaDispatch(FuncRequest const & cmd0)
 
        // E.g. Qt mouse press when no buffer
        if (!buffer_)
-               return make_pair(false, false);
+               return false;
 
        LCursor cur(*this);
        cur.push(buffer_->inset());
@@ -1055,27 +1065,50 @@ std::pair<bool, bool> BufferView::workAreaDispatch(FuncRequest const & cmd0)
        //lyxerr << BOOST_CURRENT_FUNCTION
        //       << " * created temp cursor:" << cur << endl;
 
-       // NOTE: eidtXY returns the top level inset of nested insets. If you happen
+       // NOTE: editXY returns the top level inset of nested insets. If you happen
        // to move from a text (inset=0) to a text inside an inset (e.g. an opened
        // footnote inset, again inset=0), that inset will not be redrawn.
-       static InsetBase * last_inset = NULL;
        if (cmd.action == LFUN_MOUSE_MOTION && cmd.button() == mouse_button::none) {
-               bool need_update = false;
+               bool need_redraw = false;
                
-               if (inset != last_inset) {
-                       if (last_inset)
-                               need_update |= last_inset->setMouseHover(false);
+               if (inset != last_inset_) {
+                       if (last_inset_)
+                               need_redraw |= last_inset_->setMouseHover(false);
                        if (inset)
-                               need_update |= inset->setMouseHover(true);
-                       last_inset = inset;
+                               need_redraw |= inset->setMouseHover(true);
+                       last_inset_ = inset;
                }
-               // if in singlepar mode, update to get a full screen repaint.
-               // otherwise, buttons outside of the current paragraph will not be redrawn.
-               if (need_update && metrics_info_.singlepar)
-                       update();
+
+               // 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.
+               // FIXME: It is possible to redraw only the area around the button!
+               if (need_redraw && metrics_info_.singlepar) {
+                       // FIXME: It should be possible to redraw only the area around 
+                       // the button by doing this:
+                       //
+                       //metrics_info_.singlepar = false;
+                       //metrics_info_.y1 = ymin of button;
+                       //metrics_info_.y2 = ymax of button;
+                       //
+                       // Unfortunately, rowpainter.C:paintText() does not distinguish
+                       // 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.
+                       //
+                       //metrics_info_.pit1 = first pit of CoordCache;
+                       //metrics_info_.pit2 = last pit of CoordCache;
+                       //metrics_info_.singlepar = false;
+                       //metrics_info_.y1 = 0;
+                       //metrics_info_.y2 = height_;
+                       //
+                       updateMetrics(false);
+               }
+
                // This event (moving without mouse click) is not passed further.
                // This should be changed if it is further utilized.
-               return make_pair(need_update, need_update);
+               return need_redraw;
        }
 
        // Put anchor at the same position.
@@ -1098,8 +1131,7 @@ std::pair<bool, bool> BufferView::workAreaDispatch(FuncRequest const & cmd0)
        if (cur.result().dispatched() && cur.result().update())
                return update(cur.result().update());
 
-       // When the above and the inner function are fixed, we can do this:
-       return make_pair(false, false);
+       return false;
 }
 
 
@@ -1120,8 +1152,6 @@ void BufferView::scroll(int /*lines*/)
 //
 //     scrollDocView(new_top_y);
 //
-//     // Update the scrollbar.
-//     workArea_->setScrollbarParams(t->height(), top_y(), defaultRowHeight());}
 }
 
 
@@ -1169,6 +1199,30 @@ LyXText const * BufferView::getLyXText() const
 }
 
 
+TextMetrics const & BufferView::textMetrics(LyXText const * t) const
+{
+       return const_cast<BufferView *>(this)->textMetrics(t);
+}
+
+
+TextMetrics & BufferView::textMetrics(LyXText 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<LyXText *>(t)))).first;
+       }       
+       return tmc_it->second;
+}
+
+
+ParagraphMetrics const & BufferView::parMetrics(LyXText const * t,
+               pit_type pit) const
+{
+       return textMetrics(t).parMetrics(pit);
+}
+
+
 int BufferView::workHeight() const
 {
        return height_;
@@ -1186,7 +1240,29 @@ void BufferView::setCursor(DocIterator const & dit)
 }
 
 
-void BufferView::mouseSetCursor(LCursor & cur)
+bool BufferView::checkDepm(LCursor & cur, LCursor & old)
+{
+       // Would be wrong to delete anything if we have a selection.
+       if (cur.selection())
+               return false;
+
+       bool need_anchor_change = false;
+       bool changed = cursor_.text()->deleteEmptyParagraphMechanism(cur, old,
+               need_anchor_change);
+
+       if (need_anchor_change)
+               cur.resetAnchor();
+       
+       if (!changed)
+               return false;
+
+       updateMetrics(false);
+       buffer_->changed();
+       return true;
+}
+
+
+bool BufferView::mouseSetCursor(LCursor & cur)
 {
        BOOST_ASSERT(&cur.bv() == this);
 
@@ -1197,14 +1273,15 @@ void BufferView::mouseSetCursor(LCursor & cur)
 
        // do the dEPM magic if needed
        // FIXME: move this to InsetText::notifyCursorLeaves?
+       bool update = false;
        if (!badcursor && cursor_.inTexted())
-               cursor_.text()->deleteEmptyParagraphMechanism(cur, cursor_);
+               checkDepm(cur, cursor_);
 
        cursor_ = cur;
        cursor_.clearSelection();
        cursor_.setTargetX();
        finishUndo();
-
+       return update;
 }
 
 
@@ -1249,47 +1326,56 @@ ViewMetricsInfo const & BufferView::viewMetricsInfo()
 }
 
 
+// FIXME: We should split-up updateMetrics() for the singlepar case.
 void BufferView::updateMetrics(bool singlepar)
 {
-       // Clear out the position cache in case of full screen redraw.
-       if (!singlepar)
-               coord_cache_.clear();
-
        LyXText & buftext = buffer_->text();
+       TextMetrics & tm = textMetrics(&buftext);
        pit_type size = int(buftext.paragraphs().size());
 
        if (anchor_ref_ > int(buftext.paragraphs().size() - 1)) {
                anchor_ref_ = int(buftext.paragraphs().size() - 1);
                offset_ref_ = 0;
        }
+       
+       // If the paragraph metrics has changed, we can not
+       // use the singlepar optimisation.
+       if (singlepar
+               // In Single Paragraph mode, rebreak only
+               // the (main text, not inset!) paragraph containing the cursor.
+               // (if this paragraph contains insets etc., rebreaking will
+               // recursively descend)
+               && tm.redoParagraph(cursor_.bottom().pit()))
+               singlepar = false;
 
        pit_type const pit = anchor_ref_;
        int pit1 = pit;
        int pit2 = pit;
        size_t const npit = buftext.paragraphs().size();
 
-       // Rebreak anchor paragraph. In Single Paragraph mode, rebreak only
-       // the (main text, not inset!) paragraph containing the cursor.
-       // (if this paragraph contains insets etc., rebreaking will
-       // recursively descend)
-       if (!singlepar || pit == cursor_.bottom().pit())
-               buftext.redoParagraph(*this, pit);
-       int y0 = buftext.getPar(pit).ascent() - offset_ref_;
+       // 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_;
 
-       // Redo paragraphs above anchor if necessary; again, in Single Par
-       // mode, only if we encounter the (main text) one having the cursor.
+       // Redo paragraphs above anchor if necessary.
        int y1 = y0;
        while (y1 > 0 && pit1 > 0) {
-               y1 -= buftext.getPar(pit1).ascent();
+               y1 -= tm.parMetrics(pit1).ascent();
                --pit1;
-               if (!singlepar || pit1 == cursor_.bottom().pit())
-                       buftext.redoParagraph(*this, pit1);
-               y1 -= buftext.getPar(pit1).descent();
+               if (!singlepar)
+                       tm.redoParagraph(pit1);
+               y1 -= tm.parMetrics(pit1).descent();
        }
 
 
        // Take care of ascent of first line
-       y1 -= buftext.getPar(pit1).ascent();
+       y1 -= tm.parMetrics(pit1).ascent();
 
        // Normalize anchor for next time
        anchor_ref_ = pit1;
@@ -1302,34 +1388,33 @@ void BufferView::updateMetrics(bool singlepar)
                anchor_ref_ = 0;
        }
 
-       // Redo paragraphs below the anchor if necessary. Single par mode:
-       // only the one containing the cursor if encountered.
+       // Redo paragraphs below the anchor if necessary.
        int y2 = y0;
        while (y2 < height_ && pit2 < int(npit) - 1) {
-               y2 += buftext.getPar(pit2).descent();
+               y2 += tm.parMetrics(pit2).descent();
                ++pit2;
-               if (!singlepar || pit2 == cursor_.bottom().pit())
-                       buftext.redoParagraph(*this, pit2);
-               y2 += buftext.getPar(pit2).ascent();
+               if (!singlepar)
+                       tm.redoParagraph(pit2);
+               y2 += tm.parMetrics(pit2).ascent();
        }
 
        // Take care of descent of last line
-       y2 += buftext.getPar(pit2).descent();
+       y2 += tm.parMetrics(pit2).descent();
 
        // The coordinates of all these paragraphs are correct, cache them
        int y = y1;
        CoordCache::InnerParPosCache & parPos = coord_cache_.parPos()[&buftext];
        for (pit_type pit = pit1; pit <= pit2; ++pit) {
-               Paragraph const & par = buftext.getPar(pit);
-               y += par.ascent();
+               ParagraphMetrics const & pm = tm.parMetrics(pit);
+               y += pm.ascent();
                parPos[pit] = Point(0, y);
                if (singlepar && pit == cursor_.bottom().pit()) {
                        // In Single Paragraph mode, collect here the
                        // y1 and y2 of the (one) paragraph the cursor is in
-                       y1 = y - par.ascent();
-                       y2 = y + par.descent();
+                       y1 = y - pm.ascent();
+                       y2 = y + pm.descent();
                }
-               y += par.descent();
+               y += pm.descent();
        }
 
        if (singlepar) {
@@ -1371,7 +1456,7 @@ void BufferView::menuInsertLyXFile(string const & filenm)
                if (buffer_) {
                        string const trypath = buffer_->filePath();
                        // If directory is writeable, use this as default.
-                       if (isDirWriteable(trypath))
+                       if (isDirWriteable(FileName(trypath)))
                                initpath = trypath;
                }
 
@@ -1427,5 +1512,4 @@ void BufferView::menuInsertLyXFile(string const & filenm)
        resize();
 }
 
-
 } // namespace lyx