2006-12-29 23:54:48 +00:00
|
|
|
/**
|
2007-04-26 04:41:58 +00:00
|
|
|
* \file src/TextMetrics.cpp
|
2006-12-29 23:54:48 +00:00
|
|
|
* This file is part of LyX, the document processor.
|
|
|
|
* Licence details can be found in the file COPYING.
|
|
|
|
*
|
|
|
|
* \author Asger Alstrup
|
2008-11-14 15:58:50 +00:00
|
|
|
* \author Lars Gullik Bjønnes
|
2006-12-29 23:54:48 +00:00
|
|
|
* \author Jean-Marc Lasgouttes
|
|
|
|
* \author John Levon
|
2008-11-14 15:58:50 +00:00
|
|
|
* \author André Pönitz
|
2006-12-29 23:54:48 +00:00
|
|
|
* \author Dekel Tsur
|
2008-11-14 15:58:50 +00:00
|
|
|
* \author Jürgen Vigna
|
2006-12-29 23:54:48 +00:00
|
|
|
* \author Abdelrazak Younes
|
|
|
|
*
|
|
|
|
* Full author contact details are available in file CREDITS.
|
|
|
|
*/
|
|
|
|
|
2014-03-03 14:29:37 +00:00
|
|
|
//#define KEEP_OLD_METRICS_CODE 1
|
2013-07-21 18:22:32 +00:00
|
|
|
|
2006-12-29 23:54:48 +00:00
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include "TextMetrics.h"
|
|
|
|
|
2013-10-11 14:12:20 +00:00
|
|
|
#ifdef KEEP_OLD_METRICS_CODE
|
2007-09-20 20:44:08 +00:00
|
|
|
#include "Bidi.h"
|
2013-10-11 14:12:20 +00:00
|
|
|
#endif
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "Buffer.h"
|
2007-09-02 11:21:33 +00:00
|
|
|
#include "buffer_funcs.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "BufferParams.h"
|
2006-12-29 23:54:48 +00:00
|
|
|
#include "BufferView.h"
|
2007-11-02 20:41:04 +00:00
|
|
|
#include "CoordCache.h"
|
2007-11-05 22:54:53 +00:00
|
|
|
#include "Cursor.h"
|
2007-09-02 11:21:33 +00:00
|
|
|
#include "CutAndPaste.h"
|
2009-07-19 21:13:27 +00:00
|
|
|
#include "HSpace.h"
|
2007-10-18 15:29:51 +00:00
|
|
|
#include "InsetList.h"
|
2007-09-29 20:02:32 +00:00
|
|
|
#include "Layout.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "LyXRC.h"
|
|
|
|
#include "MetricsInfo.h"
|
2006-12-29 23:54:48 +00:00
|
|
|
#include "ParagraphParameters.h"
|
2007-08-27 22:36:20 +00:00
|
|
|
#include "rowpainter.h"
|
|
|
|
#include "Text.h"
|
2007-11-07 23:25:08 +00:00
|
|
|
#include "TextClass.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "VSpace.h"
|
2006-12-29 23:54:48 +00:00
|
|
|
|
2009-08-09 16:19:43 +00:00
|
|
|
#include "insets/InsetText.h"
|
|
|
|
|
2007-11-01 11:13:07 +00:00
|
|
|
#include "mathed/MathMacroTemplate.h"
|
|
|
|
|
2006-12-29 23:54:48 +00:00
|
|
|
#include "frontends/FontMetrics.h"
|
|
|
|
#include "frontends/Painter.h"
|
|
|
|
|
2008-02-18 07:14:42 +00:00
|
|
|
#include "support/debug.h"
|
2008-04-30 08:26:40 +00:00
|
|
|
#include "support/lassert.h"
|
2008-03-15 00:22:54 +00:00
|
|
|
|
2013-07-21 18:22:32 +00:00
|
|
|
#include <cmath>
|
2009-08-10 20:54:22 +00:00
|
|
|
|
2007-12-12 10:16:00 +00:00
|
|
|
using namespace std;
|
2006-12-29 23:54:48 +00:00
|
|
|
|
2008-03-15 00:22:54 +00:00
|
|
|
|
2006-12-29 23:54:48 +00:00
|
|
|
namespace lyx {
|
|
|
|
|
|
|
|
using frontend::FontMetrics;
|
|
|
|
|
2013-07-17 22:25:08 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
int numberOfSeparators(Row const & row)
|
2006-12-29 23:54:48 +00:00
|
|
|
{
|
|
|
|
int n = 0;
|
2013-07-17 22:25:08 +00:00
|
|
|
Row::const_iterator cit = row.begin();
|
|
|
|
Row::const_iterator const end = row.end();
|
|
|
|
for ( ; cit != end ; ++cit)
|
2013-07-21 18:22:32 +00:00
|
|
|
if (cit->type == Row::SEPARATOR)
|
2006-12-29 23:54:48 +00:00
|
|
|
++n;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-17 22:25:08 +00:00
|
|
|
void setSeparatorWidth(Row & row, double w)
|
|
|
|
{
|
|
|
|
row.separator = w;
|
|
|
|
Row::iterator it = row.begin();
|
|
|
|
Row::iterator const end = row.end();
|
|
|
|
for ( ; it != end ; ++it)
|
2013-07-21 18:22:32 +00:00
|
|
|
if (it->type == Row::SEPARATOR)
|
2013-07-17 22:25:08 +00:00
|
|
|
it->extra = w;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int numberOfLabelHfills(Paragraph const & par, Row const & row)
|
2006-12-29 23:54:48 +00:00
|
|
|
{
|
|
|
|
pos_type last = row.endpos() - 1;
|
|
|
|
pos_type first = row.pos();
|
|
|
|
|
|
|
|
// hfill *DO* count at the beginning of paragraphs!
|
|
|
|
if (first) {
|
|
|
|
while (first < last && par.isHfill(first))
|
|
|
|
++first;
|
|
|
|
}
|
|
|
|
|
|
|
|
last = min(last, par.beginOfBody());
|
|
|
|
int n = 0;
|
|
|
|
for (pos_type p = first; p < last; ++p) {
|
|
|
|
if (par.isHfill(p))
|
|
|
|
++n;
|
|
|
|
}
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-17 22:25:08 +00:00
|
|
|
int numberOfHfills(Row const & row, pos_type const body_pos)
|
2006-12-29 23:54:48 +00:00
|
|
|
{
|
|
|
|
int n = 0;
|
2013-07-17 22:25:08 +00:00
|
|
|
Row::const_iterator cit = row.begin();
|
|
|
|
Row::const_iterator const end = row.end();
|
|
|
|
for ( ; cit != end ; ++cit)
|
|
|
|
if (cit->pos >= body_pos
|
|
|
|
&& cit->inset && cit->inset->isHfill())
|
2006-12-29 23:54:48 +00:00
|
|
|
++n;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2008-03-15 00:22:54 +00:00
|
|
|
|
2013-07-17 22:25:08 +00:00
|
|
|
}
|
|
|
|
|
2008-03-15 00:22:54 +00:00
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// TextMetrics
|
|
|
|
//
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
2006-12-29 23:54:48 +00:00
|
|
|
|
2007-04-29 23:33:02 +00:00
|
|
|
TextMetrics::TextMetrics(BufferView * bv, Text * text)
|
2006-12-29 23:54:48 +00:00
|
|
|
: bv_(bv), text_(text)
|
|
|
|
{
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(bv_);
|
2006-12-29 23:54:48 +00:00
|
|
|
max_width_ = bv_->workWidth();
|
|
|
|
dim_.wid = max_width_;
|
|
|
|
dim_.asc = 10;
|
|
|
|
dim_.des = 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-27 23:03:26 +00:00
|
|
|
bool TextMetrics::contains(pit_type pit) const
|
2007-09-11 16:04:10 +00:00
|
|
|
{
|
|
|
|
return par_metrics_.find(pit) != par_metrics_.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-12-29 23:54:48 +00:00
|
|
|
ParagraphMetrics const & TextMetrics::parMetrics(pit_type pit) const
|
|
|
|
{
|
|
|
|
return const_cast<TextMetrics *>(this)->parMetrics(pit, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-09-15 12:28:41 +00:00
|
|
|
|
2007-09-20 08:48:28 +00:00
|
|
|
pair<pit_type, ParagraphMetrics const *> TextMetrics::first() const
|
2007-09-15 12:28:41 +00:00
|
|
|
{
|
2007-09-20 08:48:28 +00:00
|
|
|
ParMetricsCache::const_iterator it = par_metrics_.begin();
|
|
|
|
return make_pair(it->first, &it->second);
|
2007-09-15 12:28:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-09-20 08:48:28 +00:00
|
|
|
pair<pit_type, ParagraphMetrics const *> TextMetrics::last() const
|
2007-09-15 12:28:41 +00:00
|
|
|
{
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(!par_metrics_.empty());
|
2007-09-20 08:48:28 +00:00
|
|
|
ParMetricsCache::const_reverse_iterator it = par_metrics_.rbegin();
|
|
|
|
return make_pair(it->first, &it->second);
|
2007-09-15 12:28:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-11-02 20:41:04 +00:00
|
|
|
ParagraphMetrics & TextMetrics::parMetrics(pit_type pit, bool redo)
|
2006-12-29 23:54:48 +00:00
|
|
|
{
|
|
|
|
ParMetricsCache::iterator pmc_it = par_metrics_.find(pit);
|
|
|
|
if (pmc_it == par_metrics_.end()) {
|
|
|
|
pmc_it = par_metrics_.insert(
|
2007-09-11 16:04:10 +00:00
|
|
|
make_pair(pit, ParagraphMetrics(text_->getPar(pit)))).first;
|
2006-12-29 23:54:48 +00:00
|
|
|
}
|
2007-11-02 20:41:04 +00:00
|
|
|
if (pmc_it->second.rows().empty() && redo)
|
2006-12-29 23:54:48 +00:00
|
|
|
redoParagraph(pit);
|
|
|
|
return pmc_it->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-09-18 08:52:38 +00:00
|
|
|
bool TextMetrics::metrics(MetricsInfo & mi, Dimension & dim, int min_width)
|
2006-12-29 23:54:48 +00:00
|
|
|
{
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(mi.base.textwidth > 0);
|
2006-12-29 23:54:48 +00:00
|
|
|
max_width_ = mi.base.textwidth;
|
2007-09-01 14:49:08 +00:00
|
|
|
// backup old dimension.
|
|
|
|
Dimension const old_dim = dim_;
|
|
|
|
// reset dimension.
|
|
|
|
dim_ = Dimension();
|
2007-09-18 08:52:38 +00:00
|
|
|
dim_.wid = min_width;
|
2007-09-02 18:05:37 +00:00
|
|
|
pit_type const npar = text_->paragraphs().size();
|
2007-09-01 14:49:08 +00:00
|
|
|
if (npar > 1)
|
2008-03-21 11:26:20 +00:00
|
|
|
// If there is more than one row, expand the text to
|
2007-09-01 14:49:08 +00:00
|
|
|
// the full allowable width.
|
|
|
|
dim_.wid = max_width_;
|
|
|
|
|
2007-09-02 09:44:08 +00:00
|
|
|
//lyxerr << "TextMetrics::metrics: width: " << mi.base.textwidth
|
2007-09-01 14:49:08 +00:00
|
|
|
// << " maxWidth: " << max_width_ << "\nfont: " << mi.base.font << endl;
|
2007-05-28 22:27:45 +00:00
|
|
|
|
2006-12-29 23:54:48 +00:00
|
|
|
bool changed = false;
|
|
|
|
unsigned int h = 0;
|
2007-09-01 14:49:08 +00:00
|
|
|
for (pit_type pit = 0; pit != npar; ++pit) {
|
2006-12-29 23:54:48 +00:00
|
|
|
changed |= redoParagraph(pit);
|
2007-08-18 21:10:45 +00:00
|
|
|
ParagraphMetrics const & pm = par_metrics_[pit];
|
2006-12-29 23:54:48 +00:00
|
|
|
h += pm.height();
|
2007-09-01 14:49:08 +00:00
|
|
|
if (dim_.wid < pm.width())
|
|
|
|
dim_.wid = pm.width();
|
2006-12-29 23:54:48 +00:00
|
|
|
}
|
|
|
|
|
2007-09-01 14:49:08 +00:00
|
|
|
dim_.asc = par_metrics_[0].ascent();
|
|
|
|
dim_.des = h - dim_.asc;
|
|
|
|
//lyxerr << "dim_.wid " << dim_.wid << endl;
|
|
|
|
//lyxerr << "dim_.asc " << dim_.asc << endl;
|
|
|
|
//lyxerr << "dim_.des " << dim_.des << endl;
|
2006-12-29 23:54:48 +00:00
|
|
|
|
2007-09-01 14:49:08 +00:00
|
|
|
changed |= dim_ != old_dim;
|
|
|
|
dim = dim_;
|
2006-12-29 23:54:48 +00:00
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int TextMetrics::rightMargin(ParagraphMetrics const & pm) const
|
|
|
|
{
|
2008-02-09 17:20:23 +00:00
|
|
|
return main_text_? pm.rightMargin(*bv_) : 0;
|
2006-12-29 23:54:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int TextMetrics::rightMargin(pit_type const pit) const
|
|
|
|
{
|
2008-02-09 17:20:23 +00:00
|
|
|
return main_text_? par_metrics_[pit].rightMargin(*bv_) : 0;
|
2006-12-29 23:54:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-09-02 21:48:49 +00:00
|
|
|
void TextMetrics::applyOuterFont(Font & font) const
|
|
|
|
{
|
2009-12-07 19:06:15 +00:00
|
|
|
FontInfo lf(font_.fontInfo());
|
|
|
|
lf.reduce(bv_->buffer().params().getFont().fontInfo());
|
2014-03-14 13:22:26 +00:00
|
|
|
font.fontInfo().realize(lf);
|
2007-09-02 21:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-27 23:03:26 +00:00
|
|
|
Font TextMetrics::displayFont(pit_type pit, pos_type pos) const
|
2007-09-02 21:48:49 +00:00
|
|
|
{
|
2013-04-25 21:27:10 +00:00
|
|
|
LASSERT(pos >= 0, { static Font f; return f; });
|
2007-09-02 21:48:49 +00:00
|
|
|
|
2007-09-02 22:28:49 +00:00
|
|
|
ParagraphList const & pars = text_->paragraphs();
|
|
|
|
Paragraph const & par = pars[pit];
|
2008-03-06 21:31:27 +00:00
|
|
|
Layout const & layout = par.layout();
|
2007-09-02 21:48:49 +00:00
|
|
|
Buffer const & buffer = bv_->buffer();
|
|
|
|
// FIXME: broken?
|
|
|
|
BufferParams const & params = buffer.params();
|
|
|
|
pos_type const body_pos = par.beginOfBody();
|
|
|
|
|
|
|
|
// We specialize the 95% common case:
|
|
|
|
if (!par.getDepth()) {
|
|
|
|
Font f = par.getFontSettings(params, pos);
|
2009-08-09 15:29:34 +00:00
|
|
|
if (!text_->isMainText())
|
2007-09-02 21:48:49 +00:00
|
|
|
applyOuterFont(f);
|
2008-03-06 21:31:27 +00:00
|
|
|
bool lab = layout.labeltype == LABEL_MANUAL && pos < body_pos;
|
2007-09-02 23:37:11 +00:00
|
|
|
|
2008-03-06 21:31:27 +00:00
|
|
|
FontInfo const & lf = lab ? layout.labelfont : layout.font;
|
|
|
|
FontInfo rlf = lab ? layout.reslabelfont : layout.resfont;
|
2008-03-21 11:26:20 +00:00
|
|
|
|
2007-09-02 21:48:49 +00:00
|
|
|
// In case the default family has been customized
|
2007-10-28 18:51:54 +00:00
|
|
|
if (lf.family() == INHERIT_FAMILY)
|
|
|
|
rlf.setFamily(params.getFont().fontInfo().family());
|
|
|
|
f.fontInfo().realize(rlf);
|
|
|
|
return f;
|
2007-09-02 21:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// The uncommon case need not be optimized as much
|
2008-03-21 11:26:20 +00:00
|
|
|
FontInfo const & layoutfont = pos < body_pos ?
|
2008-03-06 21:31:27 +00:00
|
|
|
layout.labelfont : layout.font;
|
2007-09-02 21:48:49 +00:00
|
|
|
|
|
|
|
Font font = par.getFontSettings(params, pos);
|
2007-10-28 18:51:54 +00:00
|
|
|
font.fontInfo().realize(layoutfont);
|
2007-09-02 21:48:49 +00:00
|
|
|
|
2009-08-09 15:29:34 +00:00
|
|
|
if (!text_->isMainText())
|
2007-09-02 21:48:49 +00:00
|
|
|
applyOuterFont(font);
|
|
|
|
|
|
|
|
// Realize against environment font information
|
|
|
|
// NOTE: the cast to pit_type should be removed when pit_type
|
|
|
|
// changes to a unsigned integer.
|
|
|
|
if (pit < pit_type(pars.size()))
|
2009-08-09 18:35:39 +00:00
|
|
|
font.fontInfo().realize(text_->outerFont(pit).fontInfo());
|
2007-09-02 21:48:49 +00:00
|
|
|
|
|
|
|
// Realize with the fonts of lesser depth.
|
2007-10-28 18:51:54 +00:00
|
|
|
font.fontInfo().realize(params.getFont().fontInfo());
|
2007-09-02 21:48:49 +00:00
|
|
|
|
|
|
|
return font;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool TextMetrics::isRTL(CursorSlice const & sl, bool boundary) const
|
|
|
|
{
|
2007-09-03 13:34:26 +00:00
|
|
|
if (!lyxrc.rtl_support || !sl.text())
|
2007-09-02 21:48:49 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
int correction = 0;
|
|
|
|
if (boundary && sl.pos() > 0)
|
|
|
|
correction = -1;
|
2008-03-21 11:26:20 +00:00
|
|
|
|
2008-02-27 23:03:26 +00:00
|
|
|
return displayFont(sl.pit(), sl.pos() + correction).isVisibleRightToLeft();
|
2007-09-02 21:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-09-02 22:28:49 +00:00
|
|
|
bool TextMetrics::isRTLBoundary(pit_type pit, pos_type pos) const
|
2007-09-02 21:48:49 +00:00
|
|
|
{
|
2009-07-24 21:33:50 +00:00
|
|
|
// no RTL boundary at paragraph start
|
2010-03-26 17:00:07 +00:00
|
|
|
if (!lyxrc.rtl_support || pos == 0)
|
2007-09-02 21:48:49 +00:00
|
|
|
return false;
|
|
|
|
|
2008-11-21 11:32:56 +00:00
|
|
|
Font const & left_font = displayFont(pit, pos - 1);
|
2007-09-02 22:28:49 +00:00
|
|
|
|
2008-11-21 11:32:56 +00:00
|
|
|
return isRTLBoundary(pit, pos, left_font);
|
2007-09-02 21:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-02-13 09:49:50 +00:00
|
|
|
// isRTLBoundary returns false on a real end-of-line boundary,
|
|
|
|
// because otherwise the two boundary types get mixed up.
|
|
|
|
// This is the whole purpose of this being in TextMetrics.
|
2007-09-02 22:28:49 +00:00
|
|
|
bool TextMetrics::isRTLBoundary(pit_type pit, pos_type pos,
|
|
|
|
Font const & font) const
|
2007-09-02 21:48:49 +00:00
|
|
|
{
|
2010-03-19 01:46:13 +00:00
|
|
|
if (!lyxrc.rtl_support
|
|
|
|
// no RTL boundary at paragraph start
|
|
|
|
|| pos == 0
|
|
|
|
// if the metrics have not been calculated, then we are not
|
|
|
|
// on screen and can safely ignore issues about boundaries.
|
|
|
|
|| !contains(pit))
|
2010-03-19 01:43:53 +00:00
|
|
|
return false;
|
|
|
|
|
2009-07-24 21:33:50 +00:00
|
|
|
ParagraphMetrics & pm = par_metrics_[pit];
|
|
|
|
// no RTL boundary in empty paragraph
|
|
|
|
if (pm.rows().empty())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
pos_type endpos = pm.getRow(pos - 1, false).endpos();
|
|
|
|
pos_type startpos = pm.getRow(pos, false).pos();
|
|
|
|
// no RTL boundary at line start:
|
|
|
|
// abc\n -> toggle to RTL -> abc\n (and not: abc\n|
|
|
|
|
// | | )
|
|
|
|
if (pos == startpos && pos == endpos) // start of cur row, end of prev row
|
|
|
|
return false;
|
|
|
|
|
2007-09-02 22:28:49 +00:00
|
|
|
Paragraph const & par = text_->getPar(pit);
|
2009-07-24 21:33:50 +00:00
|
|
|
// no RTL boundary at line break:
|
|
|
|
// abc|\n -> move right -> abc\n (and not: abc\n|
|
|
|
|
// FED FED| FED )
|
2014-03-14 13:22:26 +00:00
|
|
|
if (startpos == pos && endpos == pos && endpos != par.size()
|
|
|
|
&& (par.isNewline(pos - 1)
|
|
|
|
|| par.isLineSeparator(pos - 1)
|
2009-07-24 21:33:50 +00:00
|
|
|
|| par.isSeparator(pos - 1)))
|
|
|
|
return false;
|
2014-03-14 13:22:26 +00:00
|
|
|
|
2010-03-19 15:07:52 +00:00
|
|
|
bool left = font.isVisibleRightToLeft();
|
|
|
|
bool right;
|
|
|
|
if (pos == par.size())
|
|
|
|
right = par.isRTL(bv_->buffer().params());
|
|
|
|
else
|
|
|
|
right = displayFont(pit, pos).isVisibleRightToLeft();
|
2014-03-14 13:22:26 +00:00
|
|
|
|
2007-09-02 21:48:49 +00:00
|
|
|
return left != right;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-12-29 23:54:48 +00:00
|
|
|
bool TextMetrics::redoParagraph(pit_type const pit)
|
|
|
|
{
|
2007-08-27 14:04:46 +00:00
|
|
|
Paragraph & par = text_->getPar(pit);
|
2010-01-29 14:44:21 +00:00
|
|
|
// IMPORTANT NOTE: We pass 'false' explicitly in order to not call
|
2007-08-18 21:10:45 +00:00
|
|
|
// redoParagraph() recursively inside parMetrics.
|
|
|
|
Dimension old_dim = parMetrics(pit, false).dim();
|
|
|
|
ParagraphMetrics & pm = par_metrics_[pit];
|
2007-08-27 14:04:46 +00:00
|
|
|
pm.reset(par);
|
|
|
|
|
2007-08-21 13:03:55 +00:00
|
|
|
Buffer & buffer = bv_->buffer();
|
2006-12-29 23:54:48 +00:00
|
|
|
main_text_ = (text_ == &buffer.text());
|
|
|
|
bool changed = false;
|
|
|
|
|
2012-07-17 21:59:04 +00:00
|
|
|
// Check whether there are InsetBibItems that need fixing
|
2011-05-11 12:44:39 +00:00
|
|
|
// FIXME: This check ought to be done somewhere else. It is the reason
|
2012-07-17 21:59:04 +00:00
|
|
|
// why text_ is not const. But then, where else to do it?
|
2011-05-11 12:44:39 +00:00
|
|
|
// Well, how can you end up with either (a) a biblio environment that
|
|
|
|
// has no InsetBibitem or (b) a biblio environment with more than one
|
|
|
|
// InsetBibitem? I think the answer is: when paragraphs are merged;
|
|
|
|
// when layout is set; when material is pasted.
|
2012-07-17 21:59:04 +00:00
|
|
|
if (par.brokenBiblio()) {
|
|
|
|
Cursor & cur = const_cast<Cursor &>(bv_->cursor());
|
|
|
|
// In some cases, we do not know how to record undo
|
|
|
|
if (&cur.inset() == &text_->inset())
|
|
|
|
cur.recordUndo(ATOMIC_UNDO, pit, pit);
|
|
|
|
|
|
|
|
int const moveCursor = par.fixBiblio(buffer);
|
|
|
|
|
|
|
|
// Is it necessary to update the cursor?
|
|
|
|
if (&cur.inset() == &text_->inset() && cur.pit() == pit) {
|
|
|
|
if (moveCursor > 0)
|
|
|
|
cur.posForward();
|
|
|
|
else if (moveCursor < 0 && cur.pos() >= -moveCursor)
|
|
|
|
cur.posBackward();
|
|
|
|
}
|
2011-05-11 12:44:39 +00:00
|
|
|
}
|
|
|
|
|
2006-12-29 23:54:48 +00:00
|
|
|
// Optimisation: this is used in the next two loops
|
|
|
|
// so better to calculate that once here.
|
|
|
|
int const right_margin = rightMargin(pm);
|
|
|
|
|
2007-12-21 20:42:46 +00:00
|
|
|
// iterator pointing to paragraph to resolve macros
|
|
|
|
DocIterator parPos = text_->macrocontextPosition();
|
|
|
|
if (!parPos.empty())
|
|
|
|
parPos.pit() = pit;
|
2008-02-06 11:21:42 +00:00
|
|
|
else {
|
|
|
|
LYXERR(Debug::INFO, "MacroContext not initialised!"
|
|
|
|
<< " Going through the buffer again and hope"
|
|
|
|
<< " the context is better then.");
|
2010-07-09 14:37:00 +00:00
|
|
|
// FIXME audit updateBuffer calls
|
|
|
|
// This should not be here, but it is not clear yet where else it
|
|
|
|
// should be.
|
2010-03-03 22:13:45 +00:00
|
|
|
bv_->buffer().updateBuffer();
|
2008-02-06 11:21:42 +00:00
|
|
|
parPos = text_->macrocontextPosition();
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(!parPos.empty());
|
2008-02-06 11:26:46 +00:00
|
|
|
parPos.pit() = pit;
|
2008-02-06 11:21:42 +00:00
|
|
|
}
|
2008-03-21 11:26:20 +00:00
|
|
|
|
2006-12-29 23:54:48 +00:00
|
|
|
// redo insets
|
2007-04-29 18:17:15 +00:00
|
|
|
Font const bufferfont = buffer.params().getFont();
|
2007-10-18 15:29:51 +00:00
|
|
|
InsetList::const_iterator ii = par.insetList().begin();
|
|
|
|
InsetList::const_iterator iend = par.insetList().end();
|
2006-12-29 23:54:48 +00:00
|
|
|
for (; ii != iend; ++ii) {
|
2011-04-25 01:46:37 +00:00
|
|
|
// FIXME Doesn't this HAVE to be non-empty?
|
2007-12-21 20:42:46 +00:00
|
|
|
// position already initialized?
|
|
|
|
if (!parPos.empty()) {
|
|
|
|
parPos.pos() = ii->pos;
|
2008-03-21 11:26:20 +00:00
|
|
|
|
|
|
|
// A macro template would normally not be visible
|
|
|
|
// by itself. But the tex macro semantics allow
|
2007-12-21 20:42:46 +00:00
|
|
|
// recursion, so we artifically take the context
|
|
|
|
// after the macro template to simulate this.
|
|
|
|
if (ii->inset->lyxCode() == MATHMACRO_CODE)
|
|
|
|
parPos.pos()++;
|
2007-11-01 11:13:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// do the metric calculation
|
2006-12-29 23:54:48 +00:00
|
|
|
Dimension dim;
|
2007-09-02 09:44:08 +00:00
|
|
|
int const w = max_width_ - leftMargin(max_width_, pit, ii->pos)
|
2006-12-29 23:54:48 +00:00
|
|
|
- right_margin;
|
2011-02-22 19:41:04 +00:00
|
|
|
Font const & font = ii->inset->inheritFont() ?
|
|
|
|
displayFont(pit, ii->pos) : bufferfont;
|
2009-11-08 11:45:46 +00:00
|
|
|
MacroContext mc(&buffer, parPos);
|
2007-11-01 11:13:07 +00:00
|
|
|
MetricsInfo mi(bv_, font.fontInfo(), w, mc);
|
2007-09-21 20:39:47 +00:00
|
|
|
ii->inset->metrics(mi, dim);
|
|
|
|
Dimension const old_dim = pm.insetDimension(ii->inset);
|
2008-09-21 01:39:00 +00:00
|
|
|
if (old_dim != dim) {
|
|
|
|
pm.setInsetDimension(ii->inset, dim);
|
|
|
|
changed = true;
|
|
|
|
}
|
2006-12-29 23:54:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
par.setBeginOfBody();
|
2007-08-29 21:03:41 +00:00
|
|
|
pos_type first = 0;
|
|
|
|
size_t row_index = 0;
|
2007-01-01 09:36:55 +00:00
|
|
|
// maximum pixel width of a row
|
2006-12-29 23:54:48 +00:00
|
|
|
do {
|
2007-08-29 21:03:41 +00:00
|
|
|
if (row_index == pm.rows().size())
|
|
|
|
pm.rows().push_back(Row());
|
|
|
|
Row & row = pm.rows()[row_index];
|
|
|
|
row.pos(first);
|
2013-06-14 17:24:29 +00:00
|
|
|
breakRow(row, right_margin, pit);
|
|
|
|
setRowHeight(row, pit);
|
|
|
|
row.setChanged(false);
|
|
|
|
if (row_index || row.endpos() < par.size())
|
|
|
|
// If there is more than one row, expand the text to
|
|
|
|
// the full allowable width. This setting here is needed
|
|
|
|
// for the computeRowMetrics() below.
|
|
|
|
dim_.wid = max_width_;
|
|
|
|
int const max_row_width = max(dim_.wid, row.width());
|
2007-09-23 15:35:06 +00:00
|
|
|
computeRowMetrics(pit, row, max_row_width);
|
2013-06-14 17:24:29 +00:00
|
|
|
first = row.endpos();
|
2007-08-29 21:03:41 +00:00
|
|
|
++row_index;
|
|
|
|
|
2013-06-14 17:24:29 +00:00
|
|
|
pm.dim().wid = max(pm.dim().wid, row.width());
|
|
|
|
pm.dim().des += row.height();
|
2007-08-29 21:03:41 +00:00
|
|
|
} while (first < par.size());
|
2006-12-29 23:54:48 +00:00
|
|
|
|
2007-08-29 21:33:11 +00:00
|
|
|
if (row_index < pm.rows().size())
|
|
|
|
pm.rows().resize(row_index);
|
|
|
|
|
2006-12-29 23:54:48 +00:00
|
|
|
// Make sure that if a par ends in newline, there is one more row
|
|
|
|
// under it
|
2007-08-29 21:03:41 +00:00
|
|
|
if (first > 0 && par.isNewline(first - 1)) {
|
|
|
|
if (row_index == pm.rows().size())
|
|
|
|
pm.rows().push_back(Row());
|
|
|
|
Row & row = pm.rows()[row_index];
|
|
|
|
row.pos(first);
|
|
|
|
row.endpos(first);
|
2013-06-14 17:24:29 +00:00
|
|
|
setRowHeight(row, pit);
|
|
|
|
row.setChanged(false);
|
|
|
|
int const max_row_width = max(dim_.wid, row.width());
|
2007-09-23 15:35:06 +00:00
|
|
|
computeRowMetrics(pit, row, max_row_width);
|
2013-06-14 17:24:29 +00:00
|
|
|
pm.dim().des += row.height();
|
2006-12-29 23:54:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pm.dim().asc += pm.rows()[0].ascent();
|
|
|
|
pm.dim().des -= pm.rows()[0].ascent();
|
|
|
|
|
|
|
|
changed |= old_dim.height() != pm.dim().height();
|
|
|
|
|
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
2007-09-01 14:49:08 +00:00
|
|
|
|
2013-07-17 22:25:08 +00:00
|
|
|
int TextMetrics::getAlign(Paragraph const & par, pos_type const pos) const
|
|
|
|
{
|
|
|
|
Layout const & layout = par.layout();
|
|
|
|
|
|
|
|
int align;
|
|
|
|
if (par.params().align() == LYX_ALIGN_LAYOUT)
|
|
|
|
align = layout.align;
|
|
|
|
else
|
|
|
|
align = par.params().align();
|
|
|
|
|
|
|
|
// handle alignment inside tabular cells
|
|
|
|
Inset const & owner = text_->inset();
|
|
|
|
switch (owner.contentAlignment()) {
|
|
|
|
case LYX_ALIGN_CENTER:
|
|
|
|
case LYX_ALIGN_LEFT:
|
|
|
|
case LYX_ALIGN_RIGHT:
|
|
|
|
if (align == LYX_ALIGN_NONE || align == LYX_ALIGN_BLOCK)
|
|
|
|
align = owner.contentAlignment();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// unchanged (use align)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Display-style insets should always be on a centered row
|
|
|
|
if (Inset const * inset = par.getInset(pos)) {
|
|
|
|
switch (inset->display()) {
|
|
|
|
case Inset::AlignLeft:
|
|
|
|
align = LYX_ALIGN_BLOCK;
|
|
|
|
break;
|
|
|
|
case Inset::AlignCenter:
|
|
|
|
align = LYX_ALIGN_CENTER;
|
|
|
|
break;
|
|
|
|
case Inset::Inline:
|
|
|
|
// unchanged (use align)
|
|
|
|
break;
|
|
|
|
case Inset::AlignRight:
|
|
|
|
align = LYX_ALIGN_RIGHT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Has the user requested we not justify stuff?
|
|
|
|
if (!bv_->buffer().params().justification
|
|
|
|
&& align == LYX_ALIGN_BLOCK)
|
|
|
|
align = LYX_ALIGN_LEFT;
|
|
|
|
|
|
|
|
return align;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-08-28 15:47:53 +00:00
|
|
|
void TextMetrics::computeRowMetrics(pit_type const pit,
|
2007-09-23 15:35:06 +00:00
|
|
|
Row & row, int width) const
|
2006-12-29 23:54:48 +00:00
|
|
|
{
|
2007-09-02 09:07:07 +00:00
|
|
|
row.label_hfill = 0;
|
|
|
|
row.separator = 0;
|
|
|
|
|
2006-12-29 23:54:48 +00:00
|
|
|
Paragraph const & par = text_->getPar(pit);
|
|
|
|
|
2014-03-21 10:56:42 +00:00
|
|
|
double w = width - row.right_margin - row.width();
|
2007-09-01 22:01:34 +00:00
|
|
|
// FIXME: put back this assertion when the crash on new doc is solved.
|
2008-04-10 21:49:34 +00:00
|
|
|
//LASSERT(w >= 0, /**/);
|
2007-09-01 22:01:34 +00:00
|
|
|
|
2009-08-09 15:29:34 +00:00
|
|
|
bool const is_rtl = text_->isRTL(par);
|
2006-12-29 23:54:48 +00:00
|
|
|
if (is_rtl)
|
2007-08-28 15:47:53 +00:00
|
|
|
row.x = rightMargin(pit);
|
2006-12-29 23:54:48 +00:00
|
|
|
else
|
2007-09-02 09:44:08 +00:00
|
|
|
row.x = leftMargin(max_width_, pit, row.pos());
|
2006-12-29 23:54:48 +00:00
|
|
|
|
|
|
|
// is there a manual margin with a manual label
|
2008-03-06 21:31:27 +00:00
|
|
|
Layout const & layout = par.layout();
|
2006-12-29 23:54:48 +00:00
|
|
|
|
2013-07-17 22:25:08 +00:00
|
|
|
int nlh = 0;
|
2008-03-06 21:31:27 +00:00
|
|
|
if (layout.margintype == MARGIN_MANUAL
|
|
|
|
&& layout.labeltype == LABEL_MANUAL) {
|
2006-12-29 23:54:48 +00:00
|
|
|
/// We might have real hfills in the label part
|
2013-07-17 22:25:08 +00:00
|
|
|
nlh = numberOfLabelHfills(par, row);
|
2006-12-29 23:54:48 +00:00
|
|
|
|
|
|
|
// A manual label par (e.g. List) has an auto-hfill
|
|
|
|
// between the label text and the body of the
|
|
|
|
// paragraph too.
|
|
|
|
// But we don't want to do this auto hfill if the par
|
|
|
|
// is empty.
|
|
|
|
if (!par.empty())
|
|
|
|
++nlh;
|
|
|
|
|
|
|
|
if (nlh && !par.getLabelWidthString().empty())
|
2007-08-28 15:47:53 +00:00
|
|
|
row.label_hfill = labelFill(pit, row) / double(nlh);
|
2006-12-29 23:54:48 +00:00
|
|
|
}
|
|
|
|
|
2007-12-06 08:39:42 +00:00
|
|
|
double hfill = 0;
|
2007-12-07 21:57:56 +00:00
|
|
|
// are there any hfills in the row?
|
2013-07-17 22:25:08 +00:00
|
|
|
if (int const nh = numberOfHfills(row, par.beginOfBody())) {
|
2007-12-07 21:57:56 +00:00
|
|
|
if (w > 0)
|
2007-12-06 08:39:42 +00:00
|
|
|
hfill = w / double(nh);
|
2006-12-29 23:54:48 +00:00
|
|
|
// we don't have to look at the alignment if it is ALIGN_LEFT and
|
|
|
|
// if the row is already larger then the permitted width as then
|
|
|
|
// we force the LEFT_ALIGN'edness!
|
|
|
|
} else if (int(row.width()) < max_width_) {
|
|
|
|
// is it block, flushleft or flushright?
|
|
|
|
// set x how you need it
|
2013-07-17 22:25:08 +00:00
|
|
|
int const align = getAlign(par, row.pos());
|
2011-12-07 22:33:25 +00:00
|
|
|
|
2006-12-29 23:54:48 +00:00
|
|
|
switch (align) {
|
|
|
|
case LYX_ALIGN_BLOCK: {
|
2013-07-17 22:25:08 +00:00
|
|
|
int const ns = numberOfSeparators(row);
|
2013-07-23 14:24:01 +00:00
|
|
|
/** If we have separators, and this row has
|
|
|
|
* not be broken abruptly by a display inset
|
|
|
|
* or newline, then stretch it */
|
|
|
|
if (ns && !row.right_boundary()
|
|
|
|
&& row.endpos() != par.size()) {
|
2013-07-17 22:25:08 +00:00
|
|
|
setSeparatorWidth(row, w / ns);
|
2013-07-21 18:22:32 +00:00
|
|
|
row.dimension().wid = width;
|
2006-12-29 23:54:48 +00:00
|
|
|
} else if (is_rtl) {
|
2013-07-21 18:22:32 +00:00
|
|
|
row.dimension().wid = width;
|
2007-08-28 15:47:53 +00:00
|
|
|
row.x += w;
|
2006-12-29 23:54:48 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LYX_ALIGN_RIGHT:
|
2007-08-28 15:47:53 +00:00
|
|
|
row.x += w;
|
2006-12-29 23:54:48 +00:00
|
|
|
break;
|
|
|
|
case LYX_ALIGN_CENTER:
|
2014-03-21 10:56:42 +00:00
|
|
|
row.dimension().wid = width - w / 2;
|
2007-08-28 15:47:53 +00:00
|
|
|
row.x += w / 2;
|
2006-12-29 23:54:48 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-17 22:25:08 +00:00
|
|
|
#if 0
|
2006-12-29 23:54:48 +00:00
|
|
|
if (is_rtl) {
|
|
|
|
pos_type body_pos = par.beginOfBody();
|
|
|
|
pos_type end = row.endpos();
|
|
|
|
|
|
|
|
if (body_pos > 0
|
2013-07-17 22:25:08 +00:00
|
|
|
&& (body_pos > end || !par.isLineSeparator(body_pos - 1))) {
|
2009-08-09 15:29:34 +00:00
|
|
|
row.x += theFontMetrics(text_->labelFont(par)).
|
2008-03-06 21:31:27 +00:00
|
|
|
width(layout.labelsep);
|
2006-12-29 23:54:48 +00:00
|
|
|
if (body_pos <= end)
|
2007-08-28 15:47:53 +00:00
|
|
|
row.x += row.label_hfill;
|
2006-12-29 23:54:48 +00:00
|
|
|
}
|
|
|
|
}
|
2013-07-17 22:25:08 +00:00
|
|
|
#endif
|
2007-12-05 22:25:07 +00:00
|
|
|
|
|
|
|
pos_type const endpos = row.endpos();
|
|
|
|
pos_type body_pos = par.beginOfBody();
|
|
|
|
if (body_pos > 0
|
2013-07-17 22:25:08 +00:00
|
|
|
&& (body_pos > endpos || !par.isLineSeparator(body_pos - 1)))
|
2007-12-05 22:25:07 +00:00
|
|
|
body_pos = 0;
|
|
|
|
|
|
|
|
ParagraphMetrics & pm = par_metrics_[pit];
|
2013-07-17 22:25:08 +00:00
|
|
|
Row::iterator cit = row.begin();
|
|
|
|
Row::iterator const cend = row.end();
|
|
|
|
for ( ; cit != cend; ++cit) {
|
|
|
|
if (row.label_hfill && cit->endpos == body_pos
|
2013-07-21 18:22:32 +00:00
|
|
|
&& cit->type == Row::SPACE)
|
2013-07-17 22:25:08 +00:00
|
|
|
cit->dim.wid -= row.label_hfill * (nlh - 1);
|
|
|
|
if (!cit->inset || !cit->inset->isHfill())
|
2007-12-05 22:25:07 +00:00
|
|
|
continue;
|
2013-07-17 22:25:08 +00:00
|
|
|
if (pm.hfillExpansion(row, cit->pos))
|
|
|
|
cit->dim.wid = int(cit->pos >= body_pos ?
|
|
|
|
max(hfill, 5.0) : row.label_hfill);
|
2007-12-05 22:47:14 +00:00
|
|
|
else
|
2013-07-17 22:25:08 +00:00
|
|
|
cit->dim.wid = 5;
|
2008-03-21 11:26:20 +00:00
|
|
|
// Cache the inset dimension.
|
2013-07-17 22:25:08 +00:00
|
|
|
bv_->coordCache().insets().add(cit->inset, cit->dim);
|
|
|
|
pm.setInsetDimension(cit->inset, cit->dim);
|
2007-12-05 22:25:07 +00:00
|
|
|
}
|
2006-12-29 23:54:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-08-18 21:17:10 +00:00
|
|
|
int TextMetrics::labelFill(pit_type const pit, Row const & row) const
|
2006-12-29 23:54:48 +00:00
|
|
|
{
|
2007-08-18 21:17:10 +00:00
|
|
|
Paragraph const & par = text_->getPar(pit);
|
2013-07-17 22:25:08 +00:00
|
|
|
LBUFERR(par.beginOfBody() > 0);
|
2006-12-29 23:54:48 +00:00
|
|
|
|
|
|
|
int w = 0;
|
2013-07-17 22:25:08 +00:00
|
|
|
Row::const_iterator cit = row.begin();
|
|
|
|
Row::const_iterator const end = row.end();
|
|
|
|
// iterate over elements before main body (except the last one,
|
|
|
|
// which is extra space).
|
|
|
|
while (cit!= end && cit->endpos < par.beginOfBody()) {
|
|
|
|
w += cit->width();
|
|
|
|
++cit;
|
|
|
|
}
|
2006-12-29 23:54:48 +00:00
|
|
|
|
|
|
|
docstring const & label = par.params().labelWidthString();
|
|
|
|
if (label.empty())
|
|
|
|
return 0;
|
|
|
|
|
2007-05-28 22:27:45 +00:00
|
|
|
FontMetrics const & fm
|
2009-08-09 15:29:34 +00:00
|
|
|
= theFontMetrics(text_->labelFont(par));
|
2006-12-29 23:54:48 +00:00
|
|
|
|
|
|
|
return max(0, fm.width(label) - w);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-05-20 08:56:44 +00:00
|
|
|
#if 0
|
2013-06-14 17:24:29 +00:00
|
|
|
// Not used, see TextMetrics::breakRow
|
2007-01-01 09:36:55 +00:00
|
|
|
// this needs special handling - only newlines count as a break point
|
2007-11-02 20:41:04 +00:00
|
|
|
static pos_type addressBreakPoint(pos_type i, Paragraph const & par)
|
2007-01-01 09:36:55 +00:00
|
|
|
{
|
|
|
|
pos_type const end = par.size();
|
|
|
|
|
|
|
|
for (; i < end; ++i)
|
|
|
|
if (par.isNewline(i))
|
|
|
|
return i + 1;
|
|
|
|
|
|
|
|
return end;
|
|
|
|
}
|
2012-05-20 08:56:44 +00:00
|
|
|
#endif
|
2007-01-01 09:36:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
int TextMetrics::labelEnd(pit_type const pit) const
|
|
|
|
{
|
|
|
|
// labelEnd is only needed if the layout fills a flushleft label.
|
2008-03-06 21:31:27 +00:00
|
|
|
if (text_->getPar(pit).layout().margintype != MARGIN_MANUAL)
|
2007-01-01 09:36:55 +00:00
|
|
|
return 0;
|
|
|
|
// return the beginning of the body
|
2007-09-02 09:44:08 +00:00
|
|
|
return leftMargin(max_width_, pit);
|
2007-01-01 09:36:55 +00:00
|
|
|
}
|
|
|
|
|
2008-03-28 08:45:33 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calling Text::getFont is slow. While rebreaking we scan a
|
|
|
|
* paragraph from left to right calling getFont for every char. This
|
|
|
|
* simple class address this problem by hidding an optimization trick
|
|
|
|
* (not mine btw -AB): the font is reused in the whole font span. The
|
|
|
|
* class handles transparently the "hidden" (not part of the fontlist)
|
|
|
|
* label font (as getFont does).
|
|
|
|
**/
|
|
|
|
class FontIterator
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
///
|
|
|
|
FontIterator(TextMetrics const & tm,
|
|
|
|
Paragraph const & par, pit_type pit, pos_type pos)
|
|
|
|
: tm_(tm), par_(par), pit_(pit), pos_(pos),
|
|
|
|
font_(tm.displayFont(pit, pos)),
|
|
|
|
endspan_(par.fontSpan(pos).last),
|
|
|
|
bodypos_(par.beginOfBody())
|
|
|
|
{}
|
|
|
|
|
|
|
|
///
|
|
|
|
Font const & operator*() const { return font_; }
|
|
|
|
|
|
|
|
///
|
|
|
|
FontIterator & operator++()
|
|
|
|
{
|
|
|
|
++pos_;
|
2013-04-28 12:44:08 +00:00
|
|
|
if (pos_ < par_.size() && (pos_ > endspan_ || pos_ == bodypos_)) {
|
2008-03-28 08:45:33 +00:00
|
|
|
font_ = tm_.displayFont(pit_, pos_);
|
|
|
|
endspan_ = par_.fontSpan(pos_).last;
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
Font * operator->() { return &font_; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
///
|
|
|
|
TextMetrics const & tm_;
|
|
|
|
///
|
|
|
|
Paragraph const & par_;
|
|
|
|
///
|
|
|
|
pit_type pit_;
|
|
|
|
///
|
|
|
|
pos_type pos_;
|
|
|
|
///
|
|
|
|
Font font_;
|
|
|
|
///
|
|
|
|
pos_type endspan_;
|
|
|
|
///
|
|
|
|
pos_type bodypos_;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // anon namespace
|
2007-01-01 09:36:55 +00:00
|
|
|
|
2013-07-23 14:24:01 +00:00
|
|
|
/** This is the function where the hard work is done. The code here is
|
|
|
|
* very sensitive to small changes :) Note that part of the
|
|
|
|
* intelligence is also in Row::shorten_if_needed
|
|
|
|
*/
|
2013-06-14 17:24:29 +00:00
|
|
|
void TextMetrics::breakRow(Row & row, int const right_margin, pit_type const pit) const
|
2007-01-01 09:36:55 +00:00
|
|
|
{
|
|
|
|
Paragraph const & par = text_->getPar(pit);
|
|
|
|
pos_type const end = par.size();
|
2013-06-14 17:24:29 +00:00
|
|
|
pos_type const pos = row.pos();
|
|
|
|
int const width = max_width_ - right_margin;
|
2013-06-25 12:57:09 +00:00
|
|
|
pos_type const body_pos = par.beginOfBody();
|
|
|
|
row.clear();
|
2014-03-19 13:44:53 +00:00
|
|
|
row.x = leftMargin(max_width_, pit, pos);
|
|
|
|
row.dimension().wid = row.x;
|
2013-07-21 18:22:32 +00:00
|
|
|
row.right_margin = right_margin;
|
2013-06-25 12:57:09 +00:00
|
|
|
|
|
|
|
if (pos >= end || row.width() > width) {
|
|
|
|
row.dimension().wid += right_margin;
|
2013-06-14 17:24:29 +00:00
|
|
|
row.endpos(end);
|
|
|
|
return;
|
|
|
|
}
|
2007-01-01 09:36:55 +00:00
|
|
|
|
2013-06-13 22:01:49 +00:00
|
|
|
ParagraphMetrics const & pm = par_metrics_[pit];
|
|
|
|
ParagraphList const & pars = text_->paragraphs();
|
2007-01-01 09:36:55 +00:00
|
|
|
|
2012-05-09 19:46:37 +00:00
|
|
|
#if 0
|
2014-03-14 13:22:26 +00:00
|
|
|
//FIXME: As long as leftMargin() is not correctly implemented for
|
2012-05-09 19:46:37 +00:00
|
|
|
// MARGIN_RIGHT_ADDRESS_BOX, we should also not do this here.
|
|
|
|
// Otherwise, long rows will be painted off the screen.
|
2013-06-13 22:01:49 +00:00
|
|
|
if (par.layout().margintype == MARGIN_RIGHT_ADDRESS_BOX)
|
2007-08-29 21:03:41 +00:00
|
|
|
return addressBreakPoint(pos, par);
|
2012-05-09 19:46:37 +00:00
|
|
|
#endif
|
2007-01-01 09:36:55 +00:00
|
|
|
|
2008-02-21 19:42:34 +00:00
|
|
|
// check for possible inline completion
|
|
|
|
DocIterator const & inlineCompletionPos = bv_->inlineCompletionPos();
|
2008-02-23 03:14:33 +00:00
|
|
|
pos_type inlineCompletionLPos = -1;
|
2008-02-21 19:42:34 +00:00
|
|
|
if (inlineCompletionPos.inTexted()
|
|
|
|
&& inlineCompletionPos.text() == text_
|
|
|
|
&& inlineCompletionPos.pit() == pit) {
|
2008-02-23 03:14:33 +00:00
|
|
|
// draw logically behind the previous character
|
|
|
|
inlineCompletionLPos = inlineCompletionPos.pos() - 1;
|
2008-02-21 19:42:34 +00:00
|
|
|
}
|
2007-01-01 09:36:55 +00:00
|
|
|
|
2013-06-13 22:01:49 +00:00
|
|
|
// Now we iterate through until we reach the right margin
|
2013-06-25 12:57:09 +00:00
|
|
|
// or the end of the par, then build a representation of the row.
|
2013-06-13 22:01:49 +00:00
|
|
|
pos_type i = pos;
|
2013-06-25 12:57:09 +00:00
|
|
|
FontIterator fi = FontIterator(*this, par, pit, pos);
|
|
|
|
while (i < end && row.width() < width) {
|
2013-06-13 22:01:49 +00:00
|
|
|
char_type c = par.getChar(i);
|
|
|
|
// The most special cases are handled first.
|
|
|
|
if (par.isInset(i)) {
|
2013-06-25 12:57:09 +00:00
|
|
|
Inset const * ins = par.getInset(i);
|
|
|
|
Dimension dim = pm.insetDimension(ins);
|
2013-07-16 22:59:34 +00:00
|
|
|
row.add(i, ins, dim, *fi, par.lookupChange(i));
|
2014-03-21 10:56:42 +00:00
|
|
|
} else if (c == ' ' && i + 1 == body_pos) {
|
|
|
|
// There is a space at i, but it should not be
|
|
|
|
// added as a separator, because it is just
|
|
|
|
// before body_pos. Instead, insert some spacing to
|
|
|
|
// align text
|
|
|
|
FontMetrics const & fm = theFontMetrics(text_->labelFont(par));
|
|
|
|
int const add = max(fm.width(par.layout().labelsep),
|
|
|
|
labelEnd(pit) - row.width());
|
|
|
|
row.addSpace(i, add, *fi, par.lookupChange(i));
|
2013-06-25 12:57:09 +00:00
|
|
|
} else if (par.isLineSeparator(i)) {
|
|
|
|
// In theory, no inset has this property. If
|
|
|
|
// this is done, a new addSeparator which
|
|
|
|
// takes an inset as parameter should be
|
|
|
|
// added.
|
|
|
|
LATTEST(!par.isInset(i));
|
|
|
|
row.addSeparator(i, c, *fi, par.lookupChange(i));
|
2013-06-13 22:01:49 +00:00
|
|
|
} else if (c == '\t')
|
2013-07-17 22:25:08 +00:00
|
|
|
row.addSpace(i, theFontMetrics(*fi).width(from_ascii(" ")),
|
|
|
|
*fi, par.lookupChange(i));
|
2013-07-16 22:59:34 +00:00
|
|
|
else
|
2013-06-25 12:57:09 +00:00
|
|
|
row.add(i, c, *fi, par.lookupChange(i));
|
2014-03-14 13:22:26 +00:00
|
|
|
|
2008-02-21 19:42:34 +00:00
|
|
|
// add inline completion width
|
2013-07-17 22:25:08 +00:00
|
|
|
if (inlineCompletionLPos == i &&
|
|
|
|
!bv_->inlineCompletion().empty()) {
|
2013-06-25 12:57:09 +00:00
|
|
|
Font f = *fi;
|
|
|
|
f.fontInfo().setColor(Color_inlinecompletion);
|
2013-07-21 18:22:32 +00:00
|
|
|
row.addVirtual(i + 1, bv_->inlineCompletion(),
|
2013-07-17 22:25:08 +00:00
|
|
|
f, Change());
|
2013-06-25 12:57:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Handle some situations that abruptly terminate the row
|
|
|
|
// - A newline inset
|
|
|
|
// - Before a display inset
|
|
|
|
// - After a display inset
|
|
|
|
Inset const * inset = 0;
|
|
|
|
if (par.isNewline(i)
|
|
|
|
|| (i + 1 < end && (inset = par.getInset(i + 1))
|
|
|
|
&& inset->display())
|
|
|
|
|| (!row.empty() && row.back().inset
|
|
|
|
&& row.back().inset->display())) {
|
2013-07-23 14:24:01 +00:00
|
|
|
row.right_boundary(true);
|
2013-06-25 12:57:09 +00:00
|
|
|
++i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
++i;
|
|
|
|
++fi;
|
2014-03-21 10:56:42 +00:00
|
|
|
}
|
2008-02-21 19:42:34 +00:00
|
|
|
|
2014-03-21 10:56:42 +00:00
|
|
|
// End of paragraph marker
|
|
|
|
if (lyxrc.paragraph_markers
|
|
|
|
&& i == end && size_type(pit + 1) < pars.size()) {
|
|
|
|
// add a virtual element for the end-of-paragraph
|
|
|
|
// marker; it is shown on screen, but does not exist
|
|
|
|
// in the paragraph.
|
|
|
|
Font f(text_->layoutFont(pit));
|
|
|
|
f.fontInfo().setColor(Color_paragraphmarker);
|
|
|
|
BufferParams const & bparams
|
|
|
|
= text_->inset().buffer().params();
|
|
|
|
f.setLanguage(par.getParLanguage(bparams));
|
|
|
|
row.addVirtual(end, docstring(1, char_type(0x00B6)), f, Change());
|
2007-01-01 09:36:55 +00:00
|
|
|
}
|
|
|
|
|
2013-06-25 12:57:09 +00:00
|
|
|
row.finalizeLast();
|
|
|
|
row.endpos(i);
|
|
|
|
// if the row is too large, try to cut at last separator.
|
2013-07-21 18:22:32 +00:00
|
|
|
row.shorten_if_needed(body_pos, width);
|
2013-06-25 12:57:09 +00:00
|
|
|
|
|
|
|
// if the row ends with a separator that is not at end of
|
|
|
|
// paragraph, remove it
|
2013-07-21 18:22:32 +00:00
|
|
|
if (!row.empty() && row.back().type == Row::SEPARATOR
|
2013-06-25 12:57:09 +00:00
|
|
|
&& row.endpos() < par.size())
|
|
|
|
row.pop_back();
|
|
|
|
|
2013-07-21 18:22:32 +00:00
|
|
|
// make sure that the RTL elements are in reverse ordering
|
2014-03-19 13:44:31 +00:00
|
|
|
row.reverseRTL(text_->isRTL(par));
|
2007-01-01 09:36:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-17 22:25:08 +00:00
|
|
|
void TextMetrics::setRowHeight(Row & row, pit_type const pit,
|
2013-06-14 17:24:29 +00:00
|
|
|
bool topBottomSpace) const
|
2007-01-01 09:36:55 +00:00
|
|
|
{
|
|
|
|
Paragraph const & par = text_->getPar(pit);
|
|
|
|
// get the maximum ascent and the maximum descent
|
|
|
|
double layoutasc = 0;
|
|
|
|
double layoutdesc = 0;
|
|
|
|
double const dh = defaultRowHeight();
|
|
|
|
|
|
|
|
// ok, let us initialize the maxasc and maxdesc value.
|
|
|
|
// Only the fontsize count. The other properties
|
|
|
|
// are taken from the layoutfont. Nicer on the screen :)
|
2008-03-06 21:31:27 +00:00
|
|
|
Layout const & layout = par.layout();
|
2007-01-01 09:36:55 +00:00
|
|
|
|
|
|
|
// as max get the first character of this row then it can
|
|
|
|
// increase but not decrease the height. Just some point to
|
|
|
|
// start with so we don't have to do the assignment below too
|
|
|
|
// often.
|
2007-08-21 13:03:55 +00:00
|
|
|
Buffer const & buffer = bv_->buffer();
|
2013-06-14 17:24:29 +00:00
|
|
|
Font font = displayFont(pit, row.pos());
|
2007-10-28 18:51:54 +00:00
|
|
|
FontSize const tmpsize = font.fontInfo().size();
|
2009-08-09 15:29:34 +00:00
|
|
|
font.fontInfo() = text_->layoutFont(pit);
|
2007-10-28 18:51:54 +00:00
|
|
|
FontSize const size = font.fontInfo().size();
|
|
|
|
font.fontInfo().setSize(tmpsize);
|
2007-01-01 09:36:55 +00:00
|
|
|
|
2009-08-09 15:29:34 +00:00
|
|
|
FontInfo labelfont = text_->labelFont(par);
|
2007-01-01 09:36:55 +00:00
|
|
|
|
|
|
|
FontMetrics const & labelfont_metrics = theFontMetrics(labelfont);
|
|
|
|
FontMetrics const & fontmetrics = theFontMetrics(font);
|
|
|
|
|
|
|
|
// these are minimum values
|
2008-03-06 21:31:27 +00:00
|
|
|
double const spacing_val = layout.spacing.getValue()
|
2009-08-09 15:29:34 +00:00
|
|
|
* text_->spacing(par);
|
2007-01-01 09:36:55 +00:00
|
|
|
//lyxerr << "spacing_val = " << spacing_val << endl;
|
|
|
|
int maxasc = int(fontmetrics.maxAscent() * spacing_val);
|
|
|
|
int maxdesc = int(fontmetrics.maxDescent() * spacing_val);
|
|
|
|
|
|
|
|
// insets may be taller
|
2007-09-21 20:39:47 +00:00
|
|
|
ParagraphMetrics const & pm = par_metrics_[pit];
|
2013-07-21 18:22:32 +00:00
|
|
|
Row::const_iterator cit = row.begin();
|
|
|
|
Row::const_iterator cend = row.end();
|
|
|
|
for ( ; cit != cend; ++cit) {
|
|
|
|
if (cit->inset) {
|
|
|
|
Dimension const & dim = pm.insetDimension(cit->inset);
|
2007-09-21 20:39:47 +00:00
|
|
|
maxasc = max(maxasc, dim.ascent());
|
|
|
|
maxdesc = max(maxdesc, dim.descent());
|
2007-01-01 09:36:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if any custom fonts are larger (Asger)
|
|
|
|
// This is not completely correct, but we can live with the small,
|
|
|
|
// cosmetic error for now.
|
|
|
|
int labeladdon = 0;
|
|
|
|
|
2007-10-28 18:51:54 +00:00
|
|
|
FontSize maxsize =
|
2013-06-14 17:24:29 +00:00
|
|
|
par.highestFontInRange(row.pos(), row.endpos(), size);
|
2007-10-28 18:51:54 +00:00
|
|
|
if (maxsize > font.fontInfo().size()) {
|
2007-06-10 14:54:45 +00:00
|
|
|
// use standard paragraph font with the maximal size
|
2007-10-28 18:51:54 +00:00
|
|
|
FontInfo maxfont = font.fontInfo();
|
2007-06-10 15:34:00 +00:00
|
|
|
maxfont.setSize(maxsize);
|
|
|
|
FontMetrics const & maxfontmetrics = theFontMetrics(maxfont);
|
2007-06-10 14:54:45 +00:00
|
|
|
maxasc = max(maxasc, maxfontmetrics.maxAscent());
|
|
|
|
maxdesc = max(maxdesc, maxfontmetrics.maxDescent());
|
2007-01-01 09:36:55 +00:00
|
|
|
}
|
|
|
|
|
2010-09-13 13:16:49 +00:00
|
|
|
// This is nicer with box insets:
|
|
|
|
++maxasc;
|
|
|
|
++maxdesc;
|
|
|
|
|
2007-01-01 09:36:55 +00:00
|
|
|
ParagraphList const & pars = text_->paragraphs();
|
2009-08-09 17:01:04 +00:00
|
|
|
Inset const & inset = text_->inset();
|
2007-01-01 09:36:55 +00:00
|
|
|
|
|
|
|
// is it a top line?
|
2013-06-14 17:24:29 +00:00
|
|
|
if (row.pos() == 0 && topBottomSpace) {
|
2007-01-01 09:36:55 +00:00
|
|
|
BufferParams const & bufparams = buffer.params();
|
2007-05-05 12:06:37 +00:00
|
|
|
// some parskips VERY EASY IMPLEMENTATION
|
2011-03-08 14:41:01 +00:00
|
|
|
if (bufparams.paragraph_separation == BufferParams::ParagraphSkipSeparation
|
|
|
|
&& !inset.getLayout().parbreakIsNewline()
|
|
|
|
&& !par.layout().parbreak_is_newline
|
|
|
|
&& pit > 0
|
|
|
|
&& ((layout.isParagraph() && par.getDepth() == 0)
|
|
|
|
|| (pars[pit - 1].layout().isParagraph()
|
|
|
|
&& pars[pit - 1].getDepth() == 0))) {
|
|
|
|
maxasc += bufparams.getDefSkip().inPixels(*bv_);
|
2007-01-01 09:36:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (par.params().startOfAppendix())
|
|
|
|
maxasc += int(3 * dh);
|
|
|
|
|
|
|
|
// special code for the top label
|
2013-02-09 16:13:01 +00:00
|
|
|
if (layout.labelIsAbove()
|
|
|
|
&& (!layout.isParagraphGroup() || text_->isFirstInSequence(pit))
|
2013-07-17 22:25:08 +00:00
|
|
|
&& !par.labelString().empty()) {
|
2007-01-01 09:36:55 +00:00
|
|
|
labeladdon = int(
|
|
|
|
labelfont_metrics.maxHeight()
|
2008-03-06 21:31:27 +00:00
|
|
|
* layout.spacing.getValue()
|
2009-08-09 15:29:34 +00:00
|
|
|
* text_->spacing(par)
|
2008-03-06 21:31:27 +00:00
|
|
|
+ (layout.topsep + layout.labelbottomsep) * dh);
|
2007-01-01 09:36:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add the layout spaces, for example before and after
|
|
|
|
// a section, or between the items of a itemize or enumerate
|
|
|
|
// environment.
|
|
|
|
|
2009-08-09 18:35:39 +00:00
|
|
|
pit_type prev = text_->depthHook(pit, par.getDepth());
|
2007-02-24 23:58:16 +00:00
|
|
|
Paragraph const & prevpar = pars[prev];
|
|
|
|
if (prev != pit
|
|
|
|
&& prevpar.layout() == layout
|
|
|
|
&& prevpar.getDepth() == par.getDepth()
|
|
|
|
&& prevpar.getLabelWidthString()
|
2007-01-11 22:38:00 +00:00
|
|
|
== par.getLabelWidthString()) {
|
2008-03-06 21:31:27 +00:00
|
|
|
layoutasc = layout.itemsep * dh;
|
2013-06-14 17:24:29 +00:00
|
|
|
} else if (pit != 0 || row.pos() != 0) {
|
2008-03-06 21:31:27 +00:00
|
|
|
if (layout.topsep > 0)
|
|
|
|
layoutasc = layout.topsep * dh;
|
2007-01-01 09:36:55 +00:00
|
|
|
}
|
|
|
|
|
2009-08-09 18:35:39 +00:00
|
|
|
prev = text_->outerHook(pit);
|
2007-01-01 09:36:55 +00:00
|
|
|
if (prev != pit_type(pars.size())) {
|
2008-03-06 21:31:27 +00:00
|
|
|
maxasc += int(pars[prev].layout().parsep * dh);
|
2007-01-01 09:36:55 +00:00
|
|
|
} else if (pit != 0) {
|
2007-01-11 22:38:00 +00:00
|
|
|
Paragraph const & prevpar = pars[pit - 1];
|
|
|
|
if (prevpar.getDepth() != 0 ||
|
|
|
|
prevpar.layout() == layout) {
|
2008-03-06 21:31:27 +00:00
|
|
|
maxasc += int(layout.parsep * dh);
|
2007-01-01 09:36:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// is it a bottom line?
|
2013-06-14 17:24:29 +00:00
|
|
|
if (row.endpos() >= par.size() && topBottomSpace) {
|
2007-01-01 09:36:55 +00:00
|
|
|
// add the layout spaces, for example before and after
|
|
|
|
// a section, or between the items of a itemize or enumerate
|
|
|
|
// environment
|
|
|
|
pit_type nextpit = pit + 1;
|
|
|
|
if (nextpit != pit_type(pars.size())) {
|
|
|
|
pit_type cpit = pit;
|
|
|
|
|
|
|
|
if (pars[cpit].getDepth() > pars[nextpit].getDepth()) {
|
2014-04-04 19:54:32 +00:00
|
|
|
double usual = pars[cpit].layout().bottomsep * dh;
|
|
|
|
double unusual = 0;
|
2009-08-09 18:35:39 +00:00
|
|
|
cpit = text_->depthHook(cpit, pars[nextpit].getDepth());
|
2007-01-01 09:36:55 +00:00
|
|
|
if (pars[cpit].layout() != pars[nextpit].layout()
|
2013-07-17 22:25:08 +00:00
|
|
|
|| pars[nextpit].getLabelWidthString() != pars[cpit].getLabelWidthString())
|
2008-03-06 21:31:27 +00:00
|
|
|
unusual = pars[cpit].layout().bottomsep * dh;
|
2007-01-01 09:36:55 +00:00
|
|
|
layoutdesc = max(unusual, usual);
|
|
|
|
} else if (pars[cpit].getDepth() == pars[nextpit].getDepth()) {
|
|
|
|
if (pars[cpit].layout() != pars[nextpit].layout()
|
|
|
|
|| pars[nextpit].getLabelWidthString() != pars[cpit].getLabelWidthString())
|
2008-03-06 21:31:27 +00:00
|
|
|
layoutdesc = int(pars[cpit].layout().bottomsep * dh);
|
2007-01-01 09:36:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// incalculate the layout spaces
|
|
|
|
maxasc += int(layoutasc * 2 / (2 + pars[pit].getDepth()));
|
|
|
|
maxdesc += int(layoutdesc * 2 / (2 + pars[pit].getDepth()));
|
|
|
|
|
2007-05-28 22:27:45 +00:00
|
|
|
// FIXME: the correct way is to do the following is to move the
|
|
|
|
// following code in another method specially tailored for the
|
2007-04-29 23:33:02 +00:00
|
|
|
// main Text. The following test is thus bogus.
|
2007-01-01 09:36:55 +00:00
|
|
|
// Top and bottom margin of the document (only at top-level)
|
2008-02-21 19:42:34 +00:00
|
|
|
if (main_text_ && topBottomSpace) {
|
2013-06-14 17:24:29 +00:00
|
|
|
if (pit == 0 && row.pos() == 0)
|
2007-01-01 09:36:55 +00:00
|
|
|
maxasc += 20;
|
|
|
|
if (pit + 1 == pit_type(pars.size()) &&
|
2013-06-14 17:24:29 +00:00
|
|
|
row.endpos() == par.size() &&
|
|
|
|
!(row.endpos() > 0 && par.isNewline(row.endpos() - 1)))
|
2007-01-01 09:36:55 +00:00
|
|
|
maxdesc += 20;
|
|
|
|
}
|
|
|
|
|
2013-06-14 17:24:29 +00:00
|
|
|
row.dimension().asc = maxasc + labeladdon;
|
|
|
|
row.dimension().des = maxdesc;
|
2007-01-01 09:36:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-01-01 10:33:37 +00:00
|
|
|
// x is an absolute screen coord
|
|
|
|
// returns the column near the specified x-coordinate of the row
|
|
|
|
// x is set to the real beginning of this column
|
2013-12-20 13:02:31 +00:00
|
|
|
pos_type TextMetrics::getPosNearX(pit_type const pit,
|
2007-01-01 10:33:37 +00:00
|
|
|
Row const & row, int & x, bool & boundary) const
|
|
|
|
{
|
2013-10-11 14:12:20 +00:00
|
|
|
/// For the main Text, it is possible that this pit is not
|
|
|
|
/// yet in the CoordCache when moving cursor up.
|
|
|
|
/// x Paragraph coordinate is always 0 for main text anyway.
|
|
|
|
int const xo = origin_.x_;
|
|
|
|
x -= xo;
|
|
|
|
#ifdef KEEP_OLD_METRICS_CODE
|
|
|
|
int const x_orig = x;
|
|
|
|
#endif
|
2013-07-21 18:22:32 +00:00
|
|
|
|
2013-07-21 18:22:32 +00:00
|
|
|
pos_type pos = row.pos();
|
2013-10-11 14:12:20 +00:00
|
|
|
boundary = false;
|
2014-03-19 13:44:53 +00:00
|
|
|
if (row.empty())
|
2013-07-21 18:22:32 +00:00
|
|
|
x = row.x;
|
2014-03-21 10:56:42 +00:00
|
|
|
else if (x <= row.x) {
|
|
|
|
pos = row.front().left_pos();
|
2014-03-19 13:44:53 +00:00
|
|
|
x = row.x;
|
2014-03-21 10:56:42 +00:00
|
|
|
} else if (x >= row.width() - row.right_margin) {
|
|
|
|
pos = row.back().right_pos();
|
2013-07-21 18:22:32 +00:00
|
|
|
x = row.width() - row.right_margin;
|
|
|
|
} else {
|
|
|
|
double w = row.x;
|
|
|
|
Row::const_iterator cit = row.begin();
|
|
|
|
Row::const_iterator cend = row.end();
|
|
|
|
for ( ; cit != cend; ++cit) {
|
|
|
|
if (w <= x && w + cit->width() > x) {
|
|
|
|
double x_offset = x - w;
|
|
|
|
pos = cit->x2pos(x_offset);
|
|
|
|
x = x_offset + w;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
w += cit->width();
|
|
|
|
}
|
2014-03-21 10:56:42 +00:00
|
|
|
if (cit == row.end()) {
|
|
|
|
pos = row.back().right_pos();
|
|
|
|
x = row.width() - row.right_margin;
|
|
|
|
}
|
2013-07-21 18:22:32 +00:00
|
|
|
/** This tests for the case where the cursor is placed
|
|
|
|
* just before a font direction change. See comment on
|
|
|
|
* the boundary_ member in DocIterator.h to understand
|
2014-03-21 10:56:42 +00:00
|
|
|
* how boundary helps here.
|
2013-07-21 18:22:32 +00:00
|
|
|
*/
|
|
|
|
else if (pos == cit->endpos
|
|
|
|
&& cit + 1 != row.end()
|
|
|
|
&& cit->font.isVisibleRightToLeft() != (cit + 1)->font.isVisibleRightToLeft())
|
|
|
|
boundary = true;
|
2013-07-21 18:22:32 +00:00
|
|
|
}
|
|
|
|
|
2013-07-21 18:22:32 +00:00
|
|
|
/** This tests for the case where the cursor is set at the end
|
|
|
|
* of a row which has been broken due to a display inset on
|
2013-07-23 14:24:01 +00:00
|
|
|
* next row. This is indicated by Row::right_boundary.
|
2013-07-21 18:22:32 +00:00
|
|
|
*/
|
|
|
|
if (!row.empty() && pos == row.back().endpos
|
|
|
|
&& row.back().endpos == row.endpos())
|
2013-07-23 14:24:01 +00:00
|
|
|
boundary = row.right_boundary();
|
2013-07-21 18:22:32 +00:00
|
|
|
|
2013-10-11 14:12:20 +00:00
|
|
|
x += xo;
|
2014-03-19 13:44:53 +00:00
|
|
|
#ifdef KEEP_OLD_METRICS_CODE
|
2007-08-21 13:03:55 +00:00
|
|
|
Buffer const & buffer = bv_->buffer();
|
2007-01-01 10:33:37 +00:00
|
|
|
|
2013-10-11 14:12:20 +00:00
|
|
|
int x2 = x_orig;
|
2007-01-01 10:33:37 +00:00
|
|
|
Paragraph const & par = text_->getPar(pit);
|
2007-06-07 20:39:14 +00:00
|
|
|
Bidi bidi;
|
|
|
|
bidi.computeTables(par, buffer, row);
|
2007-01-01 10:33:37 +00:00
|
|
|
|
|
|
|
pos_type vc = row.pos();
|
2013-06-14 17:24:29 +00:00
|
|
|
pos_type const end = row.endpos();
|
2007-01-01 10:33:37 +00:00
|
|
|
pos_type c = 0;
|
2008-03-06 21:31:27 +00:00
|
|
|
Layout const & layout = par.layout();
|
2007-01-01 10:33:37 +00:00
|
|
|
|
|
|
|
bool left_side = false;
|
|
|
|
|
|
|
|
pos_type body_pos = par.beginOfBody();
|
|
|
|
|
2007-08-28 15:47:53 +00:00
|
|
|
double tmpx = row.x;
|
2007-01-01 10:33:37 +00:00
|
|
|
double last_tmpx = tmpx;
|
|
|
|
|
|
|
|
if (body_pos > 0 &&
|
|
|
|
(body_pos > end || !par.isLineSeparator(body_pos - 1)))
|
|
|
|
body_pos = 0;
|
|
|
|
|
|
|
|
// check for empty row
|
|
|
|
if (vc == end) {
|
2013-07-21 18:22:32 +00:00
|
|
|
x2 = int(tmpx) + xo;
|
2007-01-01 10:33:37 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-06-25 14:07:55 +00:00
|
|
|
// This (rtl_support test) is not needed, but gives
|
|
|
|
// some speedup if rtl_support == false
|
|
|
|
bool const lastrow = lyxrc.rtl_support && row.endpos() == par.size();
|
|
|
|
|
|
|
|
// If lastrow is false, we don't need to compute
|
|
|
|
// the value of rtl.
|
2012-04-29 20:46:49 +00:00
|
|
|
bool const rtl_on_lastrow = lastrow ? text_->isRTL(par) : false;
|
2010-06-25 14:07:55 +00:00
|
|
|
|
2013-07-21 18:22:32 +00:00
|
|
|
while (vc < end && tmpx <= x2) {
|
2007-06-07 20:39:14 +00:00
|
|
|
c = bidi.vis2log(vc);
|
2007-01-01 10:33:37 +00:00
|
|
|
last_tmpx = tmpx;
|
|
|
|
if (body_pos > 0 && c == body_pos - 1) {
|
2007-08-18 13:21:12 +00:00
|
|
|
FontMetrics const & fm = theFontMetrics(
|
2009-08-09 15:29:34 +00:00
|
|
|
text_->labelFont(par));
|
2008-03-06 21:31:27 +00:00
|
|
|
tmpx += row.label_hfill + fm.width(layout.labelsep);
|
2007-01-01 10:33:37 +00:00
|
|
|
if (par.isLineSeparator(body_pos - 1))
|
2007-08-18 21:17:10 +00:00
|
|
|
tmpx -= singleWidth(pit, body_pos - 1);
|
2007-01-01 10:33:37 +00:00
|
|
|
}
|
|
|
|
|
2007-12-05 22:25:07 +00:00
|
|
|
tmpx += singleWidth(pit, c);
|
|
|
|
if (par.isSeparator(c) && c >= body_pos)
|
2007-08-28 15:47:53 +00:00
|
|
|
tmpx += row.separator;
|
2007-01-01 10:33:37 +00:00
|
|
|
++vc;
|
|
|
|
}
|
|
|
|
|
2013-07-21 18:22:32 +00:00
|
|
|
if ((tmpx + last_tmpx) / 2 > x2) {
|
2007-01-01 10:33:37 +00:00
|
|
|
tmpx = last_tmpx;
|
|
|
|
left_side = true;
|
|
|
|
}
|
|
|
|
|
2013-04-25 21:27:10 +00:00
|
|
|
// This shouldn't happen. But we can reset and try to continue.
|
|
|
|
LASSERT(vc <= end, vc = end);
|
2007-01-01 10:33:37 +00:00
|
|
|
|
2013-07-21 18:22:32 +00:00
|
|
|
bool boundary2 = false;
|
2007-01-01 10:33:37 +00:00
|
|
|
|
|
|
|
if (lastrow &&
|
2013-07-21 18:22:32 +00:00
|
|
|
((rtl_on_lastrow && left_side && vc == row.pos() && x2 < tmpx - 5) ||
|
|
|
|
(!rtl_on_lastrow && !left_side && vc == end && x2 > tmpx + 5))) {
|
2014-06-25 10:39:58 +00:00
|
|
|
if (!par.isNewline(end - 1))
|
2008-02-01 17:29:31 +00:00
|
|
|
c = end;
|
|
|
|
} else if (vc == row.pos()) {
|
2007-06-07 20:39:14 +00:00
|
|
|
c = bidi.vis2log(vc);
|
|
|
|
if (bidi.level(c) % 2 == 1)
|
2007-01-01 10:33:37 +00:00
|
|
|
++c;
|
|
|
|
} else {
|
2007-06-07 20:39:14 +00:00
|
|
|
c = bidi.vis2log(vc - 1);
|
|
|
|
bool const rtl = (bidi.level(c) % 2 == 1);
|
2007-01-01 10:33:37 +00:00
|
|
|
if (left_side == rtl) {
|
|
|
|
++c;
|
2013-07-21 18:22:32 +00:00
|
|
|
boundary2 = isRTLBoundary(pit, c);
|
2007-01-01 10:33:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// I believe this code is not needed anymore (Jug 20050717)
|
|
|
|
#if 0
|
|
|
|
// The following code is necessary because the cursor position past
|
|
|
|
// the last char in a row is logically equivalent to that before
|
|
|
|
// the first char in the next row. That's why insets causing row
|
|
|
|
// divisions -- Newline and display-style insets -- must be treated
|
|
|
|
// specially, so cursor up/down doesn't get stuck in an air gap -- MV
|
|
|
|
// Newline inset, air gap below:
|
|
|
|
if (row.pos() < end && c >= end && par.isNewline(end - 1)) {
|
2007-06-07 20:39:14 +00:00
|
|
|
if (bidi.level(end -1) % 2 == 0)
|
2007-08-18 21:17:10 +00:00
|
|
|
tmpx -= singleWidth(pit, end - 1);
|
2007-01-01 10:33:37 +00:00
|
|
|
else
|
2007-08-18 21:17:10 +00:00
|
|
|
tmpx += singleWidth(pit, end - 1);
|
2007-01-01 10:33:37 +00:00
|
|
|
c = end - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Air gap above display inset:
|
|
|
|
if (row.pos() < end && c >= end && end < par.size()
|
|
|
|
&& par.isInset(end) && par.getInset(end)->display()) {
|
|
|
|
c = end - 1;
|
|
|
|
}
|
|
|
|
// Air gap below display inset:
|
|
|
|
if (row.pos() < end && c >= end && par.isInset(end - 1)
|
|
|
|
&& par.getInset(end - 1)->display()) {
|
|
|
|
c = end - 1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-07-21 18:22:32 +00:00
|
|
|
x2 = int(tmpx) + xo;
|
2014-03-19 13:44:53 +00:00
|
|
|
//pos_type const col = c - row.pos();
|
2007-01-01 10:33:37 +00:00
|
|
|
|
2013-07-21 18:22:32 +00:00
|
|
|
if (abs(x2 - x) > 0.1 || boundary != boundary
|
|
|
|
|| c != pos) {
|
2013-12-20 13:02:31 +00:00
|
|
|
lyxerr << "getPosNearX(" << x_orig << "): new=(x=" << x - xo << ", b=" << boundary << ", p=" << pos << "), "
|
2013-10-11 14:12:20 +00:00
|
|
|
<< "old=(x=" << x2 - xo << ", b=" << boundary2 << ", p=" << c << "), " << row;
|
2013-07-21 18:22:32 +00:00
|
|
|
}
|
|
|
|
|
2014-03-19 13:44:53 +00:00
|
|
|
#if 0
|
2007-01-01 10:33:37 +00:00
|
|
|
if (!c || end == par.size())
|
|
|
|
return col;
|
|
|
|
|
2014-06-25 10:39:58 +00:00
|
|
|
if (c==end && !par.isLineSeparator(c-1) && !par.isNewline(c-1)) {
|
2013-07-21 18:22:32 +00:00
|
|
|
boundary2 = true;
|
2007-01-01 10:33:37 +00:00
|
|
|
return col;
|
|
|
|
}
|
|
|
|
|
|
|
|
return min(col, end - 1 - row.pos());
|
2014-03-19 13:44:53 +00:00
|
|
|
#endif // 0
|
|
|
|
#endif // KEEP_OLD_METRICS_CODE
|
2013-12-20 13:02:31 +00:00
|
|
|
return pos;
|
2007-01-01 10:33:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-01-01 11:36:30 +00:00
|
|
|
pos_type TextMetrics::x2pos(pit_type pit, int row, int x) const
|
|
|
|
{
|
2007-09-05 13:04:05 +00:00
|
|
|
// We play safe and use parMetrics(pit) to make sure the
|
|
|
|
// ParagraphMetrics will be redone and OK to use if needed.
|
|
|
|
// Otherwise we would use an empty ParagraphMetrics in
|
|
|
|
// upDownInText() while in selection mode.
|
|
|
|
ParagraphMetrics const & pm = parMetrics(pit);
|
|
|
|
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(row < int(pm.rows().size()));
|
2007-01-01 11:36:30 +00:00
|
|
|
bool bound = false;
|
|
|
|
Row const & r = pm.rows()[row];
|
2013-12-20 13:02:31 +00:00
|
|
|
return getPosNearX(pit, r, x, bound);
|
2007-01-01 11:36:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-09-15 12:28:41 +00:00
|
|
|
void TextMetrics::newParMetricsDown()
|
|
|
|
{
|
|
|
|
pair<pit_type, ParagraphMetrics> const & last = *par_metrics_.rbegin();
|
|
|
|
pit_type const pit = last.first + 1;
|
2007-09-29 08:02:37 +00:00
|
|
|
if (pit == int(text_->paragraphs().size()))
|
2007-09-15 12:28:41 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
// do it and update its position.
|
|
|
|
redoParagraph(pit);
|
|
|
|
par_metrics_[pit].setPosition(last.second.position()
|
2007-12-21 07:16:50 +00:00
|
|
|
+ last.second.descent() + par_metrics_[pit].ascent());
|
2007-09-15 12:28:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TextMetrics::newParMetricsUp()
|
|
|
|
{
|
|
|
|
pair<pit_type, ParagraphMetrics> const & first = *par_metrics_.begin();
|
|
|
|
if (first.first == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pit_type const pit = first.first - 1;
|
|
|
|
// do it and update its position.
|
|
|
|
redoParagraph(pit);
|
|
|
|
par_metrics_[pit].setPosition(first.second.position()
|
2007-12-21 07:16:50 +00:00
|
|
|
- first.second.ascent() - par_metrics_[pit].descent());
|
2007-09-15 12:28:41 +00:00
|
|
|
}
|
|
|
|
|
2007-09-02 09:44:08 +00:00
|
|
|
// y is screen coordinate
|
|
|
|
pit_type TextMetrics::getPitNearY(int y)
|
|
|
|
{
|
2008-10-24 08:49:31 +00:00
|
|
|
LASSERT(!text_->paragraphs().empty(), return -1);
|
|
|
|
LASSERT(!par_metrics_.empty(), return -1);
|
2007-11-29 21:10:35 +00:00
|
|
|
LYXERR(Debug::DEBUG, "y: " << y << " cache size: " << par_metrics_.size());
|
2007-09-02 09:44:08 +00:00
|
|
|
|
|
|
|
// look for highest numbered paragraph with y coordinate less than given y
|
2007-11-26 15:29:54 +00:00
|
|
|
pit_type pit = -1;
|
2007-09-02 09:44:08 +00:00
|
|
|
int yy = -1;
|
2007-09-11 16:04:10 +00:00
|
|
|
ParMetricsCache::const_iterator it = par_metrics_.begin();
|
|
|
|
ParMetricsCache::const_iterator et = par_metrics_.end();
|
2012-05-28 20:41:32 +00:00
|
|
|
ParMetricsCache::const_iterator last = et;
|
|
|
|
--last;
|
2007-09-02 09:44:08 +00:00
|
|
|
|
2007-09-11 16:04:10 +00:00
|
|
|
ParagraphMetrics const & pm = it->second;
|
2007-09-02 09:44:08 +00:00
|
|
|
|
2007-11-26 15:29:54 +00:00
|
|
|
if (y < it->second.position() - int(pm.ascent())) {
|
|
|
|
// We are looking for a position that is before the first paragraph in
|
|
|
|
// the cache (which is in priciple off-screen, that is before the
|
|
|
|
// visible part.
|
2007-09-02 09:44:08 +00:00
|
|
|
if (it->first == 0)
|
2007-11-26 15:29:54 +00:00
|
|
|
// We are already at the first paragraph in the inset.
|
2007-09-02 09:44:08 +00:00
|
|
|
return 0;
|
2007-11-26 15:29:54 +00:00
|
|
|
// OK, this is the paragraph we are looking for.
|
2007-09-02 09:44:08 +00:00
|
|
|
pit = it->first - 1;
|
2007-11-26 15:29:54 +00:00
|
|
|
newParMetricsUp();
|
2007-09-02 09:44:08 +00:00
|
|
|
return pit;
|
|
|
|
}
|
|
|
|
|
|
|
|
ParagraphMetrics const & pm_last = par_metrics_[last->first];
|
|
|
|
|
2007-11-26 15:29:54 +00:00
|
|
|
if (y >= last->second.position() + int(pm_last.descent())) {
|
|
|
|
// We are looking for a position that is after the last paragraph in
|
2009-05-20 19:54:29 +00:00
|
|
|
// the cache (which is in priciple off-screen), that is before the
|
2007-11-26 15:29:54 +00:00
|
|
|
// visible part.
|
2007-09-02 09:44:08 +00:00
|
|
|
pit = last->first + 1;
|
|
|
|
if (pit == int(text_->paragraphs().size()))
|
2007-11-26 15:29:54 +00:00
|
|
|
// We are already at the last paragraph in the inset.
|
2007-09-02 09:44:08 +00:00
|
|
|
return last->first;
|
2007-11-26 15:29:54 +00:00
|
|
|
// OK, this is the paragraph we are looking for.
|
|
|
|
newParMetricsDown();
|
2007-09-02 09:44:08 +00:00
|
|
|
return pit;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; it != et; ++it) {
|
2007-11-29 21:10:35 +00:00
|
|
|
LYXERR(Debug::DEBUG, "examining: pit: " << it->first
|
2007-11-15 20:04:51 +00:00
|
|
|
<< " y: " << it->second.position());
|
2007-09-02 09:44:08 +00:00
|
|
|
|
|
|
|
ParagraphMetrics const & pm = par_metrics_[it->first];
|
|
|
|
|
2007-09-11 16:04:10 +00:00
|
|
|
if (it->first >= pit && int(it->second.position()) - int(pm.ascent()) <= y) {
|
2007-09-02 09:44:08 +00:00
|
|
|
pit = it->first;
|
2007-09-11 16:04:10 +00:00
|
|
|
yy = it->second.position();
|
2007-09-02 09:44:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-11-29 21:10:35 +00:00
|
|
|
LYXERR(Debug::DEBUG, "found best y: " << yy << " for pit: " << pit);
|
2007-09-02 09:44:08 +00:00
|
|
|
|
|
|
|
return pit;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-13 14:18:12 +00:00
|
|
|
Row const & TextMetrics::getPitAndRowNearY(int & y, pit_type & pit,
|
2009-05-20 20:28:24 +00:00
|
|
|
bool assert_in_view, bool up)
|
2007-09-02 09:44:08 +00:00
|
|
|
{
|
|
|
|
ParagraphMetrics const & pm = par_metrics_[pit];
|
|
|
|
|
2007-09-11 16:04:10 +00:00
|
|
|
int yy = pm.position() - pm.ascent();
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(!pm.rows().empty());
|
2007-09-02 09:44:08 +00:00
|
|
|
RowList::const_iterator rit = pm.rows().begin();
|
2007-10-02 18:27:20 +00:00
|
|
|
RowList::const_iterator rlast = pm.rows().end();
|
|
|
|
--rlast;
|
2007-09-02 09:44:08 +00:00
|
|
|
for (; rit != rlast; yy += rit->height(), ++rit)
|
|
|
|
if (yy + rit->height() > y)
|
|
|
|
break;
|
2009-05-20 20:28:24 +00:00
|
|
|
|
2009-11-15 23:53:40 +00:00
|
|
|
if (assert_in_view) {
|
|
|
|
if (!up && yy + rit->height() > y) {
|
2009-08-13 14:18:12 +00:00
|
|
|
if (rit != pm.rows().begin()) {
|
|
|
|
y = yy;
|
2009-05-20 20:28:24 +00:00
|
|
|
--rit;
|
2009-08-13 14:18:12 +00:00
|
|
|
} else if (pit != 0) {
|
2009-05-20 20:28:24 +00:00
|
|
|
--pit;
|
|
|
|
newParMetricsUp();
|
|
|
|
ParagraphMetrics const & pm2 = par_metrics_[pit];
|
|
|
|
rit = pm2.rows().end();
|
|
|
|
--rit;
|
2009-08-13 14:18:12 +00:00
|
|
|
y = yy;
|
2009-05-20 20:28:24 +00:00
|
|
|
}
|
2009-11-15 23:53:40 +00:00
|
|
|
} else if (up && yy != y) {
|
2009-08-13 14:18:12 +00:00
|
|
|
if (rit != rlast) {
|
|
|
|
y = yy + rit->height();
|
2009-05-20 20:28:24 +00:00
|
|
|
++rit;
|
2009-11-15 23:45:39 +00:00
|
|
|
} else if (pit < int(text_->paragraphs().size()) - 1) {
|
2009-05-20 20:28:24 +00:00
|
|
|
++pit;
|
|
|
|
newParMetricsDown();
|
|
|
|
ParagraphMetrics const & pm2 = par_metrics_[pit];
|
|
|
|
rit = pm2.rows().begin();
|
2009-08-13 14:18:12 +00:00
|
|
|
y = pm2.position();
|
2009-05-20 20:28:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-09-02 09:44:08 +00:00
|
|
|
return *rit;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// x,y are absolute screen coordinates
|
|
|
|
// sets cursor recursively descending into nested editable insets
|
2009-05-20 20:28:24 +00:00
|
|
|
Inset * TextMetrics::editXY(Cursor & cur, int x, int y,
|
|
|
|
bool assert_in_view, bool up)
|
2007-09-02 09:44:08 +00:00
|
|
|
{
|
|
|
|
if (lyxerr.debugging(Debug::WORKAREA)) {
|
2007-11-28 22:12:03 +00:00
|
|
|
LYXERR0("TextMetrics::editXY(cur, " << x << ", " << y << ")");
|
2007-09-02 09:44:08 +00:00
|
|
|
cur.bv().coordCache().dump();
|
|
|
|
}
|
|
|
|
pit_type pit = getPitNearY(y);
|
2008-10-24 08:49:31 +00:00
|
|
|
LASSERT(pit != -1, return 0);
|
2014-03-14 13:22:26 +00:00
|
|
|
|
2009-08-13 14:18:12 +00:00
|
|
|
int yy = y; // is modified by getPitAndRowNearY
|
|
|
|
Row const & row = getPitAndRowNearY(yy, pit, assert_in_view, up);
|
2007-09-02 09:44:08 +00:00
|
|
|
|
|
|
|
cur.pit() = pit;
|
|
|
|
|
2014-05-27 13:14:14 +00:00
|
|
|
// Do we cover an inset?
|
|
|
|
InsetList::InsetTable * it = checkInsetHit(pit, x, yy);
|
|
|
|
|
|
|
|
if (!it) {
|
|
|
|
// No inset, set position in the text
|
2013-12-20 13:02:31 +00:00
|
|
|
bool bound = false; // is modified by getPosNearX
|
|
|
|
int xx = x; // is modified by getPosNearX
|
|
|
|
cur.pos() = getPosNearX(pit, row, xx, bound);
|
2014-05-27 13:14:14 +00:00
|
|
|
cur.boundary(bound);
|
2007-09-02 13:35:48 +00:00
|
|
|
cur.setCurrentFont();
|
2014-05-27 13:14:14 +00:00
|
|
|
cur.setTargetX(xx);
|
2007-09-02 09:44:08 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-05-27 13:14:14 +00:00
|
|
|
Inset * inset = it->inset;
|
|
|
|
//lyxerr << "inset " << inset << " hit at x: " << x << " y: " << y << endl;
|
|
|
|
|
|
|
|
// Set position in front of inset
|
|
|
|
cur.pos() = it->pos;
|
|
|
|
cur.boundary(false);
|
|
|
|
cur.setTargetX(x);
|
2007-09-02 09:44:08 +00:00
|
|
|
|
|
|
|
// Try to descend recursively inside the inset.
|
2009-08-13 14:18:12 +00:00
|
|
|
inset = inset->editXY(cur, x, yy);
|
2007-09-02 09:44:08 +00:00
|
|
|
|
|
|
|
if (cur.top().text() == text_)
|
2007-09-02 13:35:48 +00:00
|
|
|
cur.setCurrentFont();
|
2007-09-02 09:44:08 +00:00
|
|
|
return inset;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TextMetrics::setCursorFromCoordinates(Cursor & cur, int const x, int const y)
|
|
|
|
{
|
2013-04-25 21:27:10 +00:00
|
|
|
LASSERT(text_ == cur.text(), return);
|
2013-04-08 22:32:02 +00:00
|
|
|
pit_type const pit = getPitNearY(y);
|
2008-10-24 08:49:31 +00:00
|
|
|
LASSERT(pit != -1, return);
|
2007-09-02 09:44:08 +00:00
|
|
|
|
|
|
|
ParagraphMetrics const & pm = par_metrics_[pit];
|
|
|
|
|
2007-09-11 16:04:10 +00:00
|
|
|
int yy = pm.position() - pm.ascent();
|
2007-11-29 21:10:35 +00:00
|
|
|
LYXERR(Debug::DEBUG, "x: " << x << " y: " << y <<
|
|
|
|
" pit: " << pit << " yy: " << yy);
|
2007-09-02 09:44:08 +00:00
|
|
|
|
|
|
|
int r = 0;
|
2013-04-27 21:52:55 +00:00
|
|
|
LBUFERR(pm.rows().size());
|
2007-09-02 09:44:08 +00:00
|
|
|
for (; r < int(pm.rows().size()) - 1; ++r) {
|
|
|
|
Row const & row = pm.rows()[r];
|
|
|
|
if (int(yy + row.height()) > y)
|
|
|
|
break;
|
|
|
|
yy += row.height();
|
|
|
|
}
|
|
|
|
|
|
|
|
Row const & row = pm.rows()[r];
|
|
|
|
|
2007-11-29 21:10:35 +00:00
|
|
|
LYXERR(Debug::DEBUG, "row " << r << " from pos: " << row.pos());
|
2007-09-02 09:44:08 +00:00
|
|
|
|
|
|
|
bool bound = false;
|
|
|
|
int xx = x;
|
2013-12-20 13:02:31 +00:00
|
|
|
pos_type const pos = getPosNearX(pit, row, xx, bound);
|
2007-09-02 09:44:08 +00:00
|
|
|
|
2007-11-29 21:10:35 +00:00
|
|
|
LYXERR(Debug::DEBUG, "setting cursor pit: " << pit << " pos: " << pos);
|
2007-09-02 09:44:08 +00:00
|
|
|
|
|
|
|
text_->setCursor(cur, pit, pos, true, bound);
|
|
|
|
// remember new position.
|
|
|
|
cur.setTargetX();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//takes screen x,y coordinates
|
2014-05-27 13:14:14 +00:00
|
|
|
InsetList::InsetTable * TextMetrics::checkInsetHit(pit_type pit, int x, int y)
|
2007-09-02 09:44:08 +00:00
|
|
|
{
|
|
|
|
Paragraph const & par = text_->paragraphs()[pit];
|
2007-09-21 20:39:47 +00:00
|
|
|
ParagraphMetrics const & pm = par_metrics_[pit];
|
2007-09-02 09:44:08 +00:00
|
|
|
|
2007-11-29 21:10:35 +00:00
|
|
|
LYXERR(Debug::DEBUG, "x: " << x << " y: " << y << " pit: " << pit);
|
2007-11-15 20:04:51 +00:00
|
|
|
|
2007-10-18 15:29:51 +00:00
|
|
|
InsetList::const_iterator iit = par.insetList().begin();
|
|
|
|
InsetList::const_iterator iend = par.insetList().end();
|
2007-09-02 09:44:08 +00:00
|
|
|
for (; iit != iend; ++iit) {
|
|
|
|
Inset * inset = iit->inset;
|
2007-09-21 20:39:47 +00:00
|
|
|
|
2007-11-29 21:10:35 +00:00
|
|
|
LYXERR(Debug::DEBUG, "examining inset " << inset);
|
2007-09-02 09:44:08 +00:00
|
|
|
|
2007-09-21 20:39:47 +00:00
|
|
|
if (!bv_->coordCache().getInsets().has(inset)) {
|
2007-11-29 21:10:35 +00:00
|
|
|
LYXERR(Debug::DEBUG, "inset has no cached position");
|
2007-09-21 20:39:47 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Dimension const & dim = pm.insetDimension(inset);
|
|
|
|
Point p = bv_->coordCache().getInsets().xy(inset);
|
|
|
|
|
2007-11-29 21:10:35 +00:00
|
|
|
LYXERR(Debug::DEBUG, "xo: " << p.x_ << "..." << p.x_ + dim.wid
|
2007-11-15 20:04:51 +00:00
|
|
|
<< " yo: " << p.y_ - dim.asc << "..." << p.y_ + dim.des);
|
2007-09-21 20:39:47 +00:00
|
|
|
|
2013-07-17 22:25:08 +00:00
|
|
|
if (x >= p.x_ && x <= p.x_ + dim.wid
|
|
|
|
&& y >= p.y_ - dim.asc && y <= p.y_ + dim.des) {
|
2007-11-29 21:10:35 +00:00
|
|
|
LYXERR(Debug::DEBUG, "Hit inset: " << inset);
|
2014-05-27 13:14:14 +00:00
|
|
|
return const_cast<InsetList::InsetTable *>(&(*iit));
|
2007-09-02 09:44:08 +00:00
|
|
|
}
|
|
|
|
}
|
2007-09-21 20:39:47 +00:00
|
|
|
|
2007-11-29 21:10:35 +00:00
|
|
|
LYXERR(Debug::DEBUG, "No inset hit. ");
|
2007-09-02 09:44:08 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-05-27 13:14:14 +00:00
|
|
|
//takes screen x,y coordinates
|
|
|
|
Inset * TextMetrics::checkInsetHit(int x, int y)
|
|
|
|
{
|
|
|
|
pit_type const pit = getPitNearY(y);
|
|
|
|
LASSERT(pit != -1, return 0);
|
|
|
|
InsetList::InsetTable * it = checkInsetHit(pit, x, y);
|
|
|
|
|
|
|
|
if (!it)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return it->inset;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-09-02 09:44:08 +00:00
|
|
|
int TextMetrics::cursorX(CursorSlice const & sl,
|
|
|
|
bool boundary) const
|
|
|
|
{
|
2013-04-25 21:27:10 +00:00
|
|
|
LASSERT(sl.text() == text_, return 0);
|
2013-07-17 22:25:08 +00:00
|
|
|
|
2014-03-03 14:29:37 +00:00
|
|
|
ParagraphMetrics const & pm = par_metrics_[sl.pit()];
|
2007-09-02 09:44:08 +00:00
|
|
|
if (pm.rows().empty())
|
|
|
|
return 0;
|
2013-07-17 22:25:08 +00:00
|
|
|
Row const & row = pm.getRow(sl.pos(), boundary);
|
2014-03-03 14:29:37 +00:00
|
|
|
pos_type const pos = sl.pos();
|
2013-07-17 22:25:08 +00:00
|
|
|
|
|
|
|
/**
|
2013-10-18 15:55:30 +00:00
|
|
|
* When boundary is true, position i is in the row element (pos, endpos)
|
2013-07-17 22:25:08 +00:00
|
|
|
* if
|
2013-10-18 15:55:30 +00:00
|
|
|
* pos < i <= endpos
|
2013-07-17 22:25:08 +00:00
|
|
|
* whereas, when boundary is false, the test is
|
2013-10-18 15:55:30 +00:00
|
|
|
* pos <= i < endpos
|
2013-07-17 22:25:08 +00:00
|
|
|
* The correction below allows to handle both cases.
|
|
|
|
*/
|
2013-07-21 18:22:32 +00:00
|
|
|
int const boundary_corr = (boundary && pos) ? -1 : 0;
|
2013-07-17 22:25:08 +00:00
|
|
|
|
2014-03-21 10:56:42 +00:00
|
|
|
/** Early return in trivial cases
|
|
|
|
* 1) the row is empty
|
|
|
|
* 2) the position is the left-most position of the row; there
|
|
|
|
* is a quirck herehowever: if the first element is virtual
|
|
|
|
* (end-of-par marker for example), then we have to look
|
|
|
|
* closer
|
|
|
|
*/
|
2013-07-21 18:22:32 +00:00
|
|
|
if (row.empty()
|
2014-03-21 10:56:42 +00:00
|
|
|
|| (pos == row.begin()->left_pos()
|
|
|
|
&& pos != row.begin()->right_pos()))
|
2014-03-03 14:29:37 +00:00
|
|
|
return int(row.x);
|
2013-07-17 22:25:08 +00:00
|
|
|
|
|
|
|
Row::const_iterator cit = row.begin();
|
2014-03-03 14:29:37 +00:00
|
|
|
double x = row.x;
|
2013-07-17 22:25:08 +00:00
|
|
|
for ( ; cit != row.end() ; ++cit) {
|
2014-03-21 10:56:42 +00:00
|
|
|
/** Look whether the cursor is inside the element's
|
|
|
|
* span. Note that it is necessary to take the
|
|
|
|
* boundary in account, and to accept virtual
|
|
|
|
* elements, which have pos == endpos.
|
|
|
|
*/
|
2013-07-21 18:22:32 +00:00
|
|
|
if (pos + boundary_corr >= cit->pos
|
2014-03-21 10:56:42 +00:00
|
|
|
&& (pos + boundary_corr < cit->endpos
|
|
|
|
|| cit->pos == cit->endpos)) {
|
2013-07-21 18:22:32 +00:00
|
|
|
x += cit->pos2x(pos);
|
2013-07-17 22:25:08 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
x += cit->width();
|
|
|
|
}
|
|
|
|
|
2013-07-21 18:22:32 +00:00
|
|
|
#ifdef KEEP_OLD_METRICS_CODE
|
2014-03-03 14:29:37 +00:00
|
|
|
pit_type const pit = sl.pit();
|
2013-07-21 18:22:32 +00:00
|
|
|
Paragraph const & par = text_->paragraphs()[pit];
|
|
|
|
|
2007-09-02 09:44:08 +00:00
|
|
|
// Correct position in front of big insets
|
2013-07-21 18:22:32 +00:00
|
|
|
bool const boundary_correction = pos != 0 && boundary;
|
2007-09-02 09:44:08 +00:00
|
|
|
if (boundary_correction)
|
2013-07-21 18:22:32 +00:00
|
|
|
--pos;
|
2007-09-02 09:44:08 +00:00
|
|
|
|
|
|
|
pos_type cursor_vpos = 0;
|
|
|
|
|
|
|
|
Buffer const & buffer = bv_->buffer();
|
2013-07-17 22:25:08 +00:00
|
|
|
double x2 = row.x;
|
2007-09-02 09:44:08 +00:00
|
|
|
Bidi bidi;
|
|
|
|
bidi.computeTables(par, buffer, row);
|
|
|
|
|
|
|
|
pos_type const row_pos = row.pos();
|
|
|
|
pos_type const end = row.endpos();
|
2008-03-21 11:26:20 +00:00
|
|
|
// Spaces at logical line breaks in bidi text must be skipped during
|
2007-09-02 09:44:08 +00:00
|
|
|
// cursor positioning. However, they may appear visually in the middle
|
|
|
|
// of a row; they must be skipped, wherever they are...
|
|
|
|
// * logically "abc_[HEBREW_\nHEBREW]"
|
|
|
|
// * visually "abc_[_WERBEH\nWERBEH]"
|
|
|
|
pos_type skipped_sep_vpos = -1;
|
|
|
|
|
|
|
|
if (end <= row_pos)
|
|
|
|
cursor_vpos = row_pos;
|
2013-07-21 18:22:32 +00:00
|
|
|
else if (pos >= end)
|
2009-08-09 15:29:34 +00:00
|
|
|
cursor_vpos = text_->isRTL(par) ? row_pos : end;
|
2013-07-21 18:22:32 +00:00
|
|
|
else if (pos > row_pos && pos >= end)
|
2009-11-04 15:30:35 +00:00
|
|
|
//FIXME: this code is never reached!
|
|
|
|
// (see http://www.lyx.org/trac/changeset/8251)
|
2007-09-02 09:44:08 +00:00
|
|
|
// Place cursor after char at (logical) position pos - 1
|
2013-07-21 18:22:32 +00:00
|
|
|
cursor_vpos = (bidi.level(pos - 1) % 2 == 0)
|
|
|
|
? bidi.log2vis(pos - 1) + 1 : bidi.log2vis(pos - 1);
|
2007-09-02 09:44:08 +00:00
|
|
|
else
|
2013-07-21 18:22:32 +00:00
|
|
|
// Place cursor before char at (logical) position pos
|
|
|
|
cursor_vpos = (bidi.level(pos) % 2 == 0)
|
|
|
|
? bidi.log2vis(pos) : bidi.log2vis(pos) + 1;
|
2007-09-02 09:44:08 +00:00
|
|
|
|
|
|
|
pos_type body_pos = par.beginOfBody();
|
|
|
|
if (body_pos > 0 &&
|
|
|
|
(body_pos > end || !par.isLineSeparator(body_pos - 1)))
|
|
|
|
body_pos = 0;
|
|
|
|
|
2008-02-23 03:14:33 +00:00
|
|
|
// check for possible inline completion in this row
|
|
|
|
DocIterator const & inlineCompletionPos = bv_->inlineCompletionPos();
|
|
|
|
pos_type inlineCompletionVPos = -1;
|
|
|
|
if (inlineCompletionPos.inTexted()
|
|
|
|
&& inlineCompletionPos.text() == text_
|
|
|
|
&& inlineCompletionPos.pit() == pit
|
2008-02-28 12:44:42 +00:00
|
|
|
&& inlineCompletionPos.pos() - 1 >= row_pos
|
|
|
|
&& inlineCompletionPos.pos() - 1 < end) {
|
2008-02-23 03:14:33 +00:00
|
|
|
// draw logically behind the previous character
|
|
|
|
inlineCompletionVPos = bidi.log2vis(inlineCompletionPos.pos() - 1);
|
|
|
|
}
|
|
|
|
|
2007-09-02 09:44:08 +00:00
|
|
|
// Use font span to speed things up, see below
|
|
|
|
FontSpan font_span;
|
|
|
|
Font font;
|
|
|
|
|
|
|
|
// If the last logical character is a separator, skip it, unless
|
|
|
|
// it's in the last row of a paragraph; see skipped_sep_vpos declaration
|
|
|
|
if (end > 0 && end < par.size() && par.isSeparator(end - 1))
|
|
|
|
skipped_sep_vpos = bidi.log2vis(end - 1);
|
2008-03-21 11:26:20 +00:00
|
|
|
|
2009-08-19 22:46:43 +00:00
|
|
|
if (lyxrc.paragraph_markers && text_->isRTL(par)) {
|
2009-08-20 18:30:06 +00:00
|
|
|
ParagraphList const & pars_ = text_->paragraphs();
|
|
|
|
if (size_type(pit + 1) < pars_.size()) {
|
2013-07-21 18:22:32 +00:00
|
|
|
FontInfo f(text_->layoutFont(pit));
|
2009-08-20 18:30:06 +00:00
|
|
|
docstring const s = docstring(1, char_type(0x00B6));
|
2013-07-17 22:25:08 +00:00
|
|
|
x2 += theFontMetrics(f).width(s);
|
2009-08-20 18:30:06 +00:00
|
|
|
}
|
2009-08-19 22:46:43 +00:00
|
|
|
}
|
|
|
|
|
2008-02-23 03:14:33 +00:00
|
|
|
// Inline completion RTL special case row_pos == cursor_pos:
|
|
|
|
// "__|b" => cursor_pos is right of __
|
|
|
|
if (row_pos == inlineCompletionVPos && row_pos == cursor_vpos) {
|
2008-02-27 23:03:26 +00:00
|
|
|
font = displayFont(pit, row_pos + 1);
|
2008-02-23 03:14:33 +00:00
|
|
|
docstring const & completion = bv_->inlineCompletion();
|
|
|
|
if (font.isRightToLeft() && completion.length() > 0)
|
2013-07-17 22:25:08 +00:00
|
|
|
x2 += theFontMetrics(font.fontInfo()).width(completion);
|
2008-02-23 03:14:33 +00:00
|
|
|
}
|
2008-03-21 11:26:20 +00:00
|
|
|
|
2007-09-02 09:44:08 +00:00
|
|
|
for (pos_type vpos = row_pos; vpos < cursor_vpos; ++vpos) {
|
|
|
|
// Skip the separator which is at the logical end of the row
|
|
|
|
if (vpos == skipped_sep_vpos)
|
|
|
|
continue;
|
|
|
|
pos_type pos = bidi.vis2log(vpos);
|
|
|
|
if (body_pos > 0 && pos == body_pos - 1) {
|
|
|
|
FontMetrics const & labelfm = theFontMetrics(
|
2009-08-09 15:29:34 +00:00
|
|
|
text_->labelFont(par));
|
2013-07-17 22:25:08 +00:00
|
|
|
x2 += row.label_hfill + labelfm.width(par.layout().labelsep);
|
2007-09-02 09:44:08 +00:00
|
|
|
if (par.isLineSeparator(body_pos - 1))
|
2013-07-17 22:25:08 +00:00
|
|
|
x2 -= singleWidth(pit, body_pos - 1);
|
2007-09-02 09:44:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Use font span to speed things up, see above
|
|
|
|
if (pos < font_span.first || pos > font_span.last) {
|
|
|
|
font_span = par.fontSpan(pos);
|
2008-02-27 23:03:26 +00:00
|
|
|
font = displayFont(pit, pos);
|
2007-09-02 09:44:08 +00:00
|
|
|
}
|
|
|
|
|
2013-07-17 22:25:08 +00:00
|
|
|
x2 += pm.singleWidth(pos, font);
|
2007-09-02 09:44:08 +00:00
|
|
|
|
2008-02-23 03:14:33 +00:00
|
|
|
// Inline completion RTL case:
|
|
|
|
// "a__|b", __ of b => non-boundary a-pos is right of __
|
2008-03-21 11:26:20 +00:00
|
|
|
if (vpos + 1 == inlineCompletionVPos
|
2008-02-23 03:14:33 +00:00
|
|
|
&& (vpos + 1 < cursor_vpos || !boundary_correction)) {
|
2008-02-27 23:03:26 +00:00
|
|
|
font = displayFont(pit, vpos + 1);
|
2008-02-23 03:14:33 +00:00
|
|
|
docstring const & completion = bv_->inlineCompletion();
|
|
|
|
if (font.isRightToLeft() && completion.length() > 0)
|
2013-07-17 22:25:08 +00:00
|
|
|
x2 += theFontMetrics(font.fontInfo()).width(completion);
|
2008-02-23 03:14:33 +00:00
|
|
|
}
|
2008-03-21 11:26:20 +00:00
|
|
|
|
2008-02-23 03:14:33 +00:00
|
|
|
// Inline completion LTR case:
|
|
|
|
// "b|__a", __ of b => non-boundary a-pos is in front of __
|
|
|
|
if (vpos == inlineCompletionVPos
|
|
|
|
&& (vpos + 1 < cursor_vpos || boundary_correction)) {
|
2008-02-27 23:03:26 +00:00
|
|
|
font = displayFont(pit, vpos);
|
2008-02-23 03:14:33 +00:00
|
|
|
docstring const & completion = bv_->inlineCompletion();
|
|
|
|
if (!font.isRightToLeft() && completion.length() > 0)
|
2013-07-17 22:25:08 +00:00
|
|
|
x2 += theFontMetrics(font.fontInfo()).width(completion);
|
2008-02-23 03:14:33 +00:00
|
|
|
}
|
2008-03-21 11:26:20 +00:00
|
|
|
|
2007-12-05 22:25:07 +00:00
|
|
|
if (par.isSeparator(pos) && pos >= body_pos)
|
2013-07-17 22:25:08 +00:00
|
|
|
x2 += row.separator;
|
2007-09-02 09:44:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// see correction above
|
|
|
|
if (boundary_correction) {
|
2007-09-02 21:48:49 +00:00
|
|
|
if (isRTL(sl, boundary))
|
2013-07-21 18:22:32 +00:00
|
|
|
x2 -= singleWidth(pit, pos);
|
2007-09-02 09:44:08 +00:00
|
|
|
else
|
2013-07-21 18:22:32 +00:00
|
|
|
x2 += singleWidth(pit, pos);
|
2007-09-02 09:44:08 +00:00
|
|
|
}
|
|
|
|
|
2013-07-21 18:22:32 +00:00
|
|
|
if (abs(x2 - x) > 0.01) {
|
2014-03-19 13:44:53 +00:00
|
|
|
lyxerr << "cursorX(" << pos - boundary_corr << ", " << boundary_corr
|
|
|
|
<< "): old=" << x2 << ", new=" << x;
|
2013-07-17 22:25:08 +00:00
|
|
|
if (cit == row.end())
|
2014-03-19 13:44:53 +00:00
|
|
|
lyxerr << "Element not found\n";
|
2013-07-17 22:25:08 +00:00
|
|
|
else
|
2014-03-19 13:44:53 +00:00
|
|
|
lyxerr << " found in " << *cit << "\n";
|
2013-07-17 22:25:08 +00:00
|
|
|
lyxerr << row <<endl;
|
|
|
|
}
|
|
|
|
|
2014-03-19 13:44:53 +00:00
|
|
|
#endif // KEEP_OLD_METRICS_CODE
|
2007-09-02 09:44:08 +00:00
|
|
|
return int(x);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int TextMetrics::cursorY(CursorSlice const & sl, bool boundary) const
|
|
|
|
{
|
2007-12-12 19:28:07 +00:00
|
|
|
//lyxerr << "TextMetrics::cursorY: boundary: " << boundary << endl;
|
2007-09-02 09:44:08 +00:00
|
|
|
ParagraphMetrics const & pm = par_metrics_[sl.pit()];
|
|
|
|
if (pm.rows().empty())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
int h = 0;
|
|
|
|
h -= par_metrics_[0].rows()[0].ascent();
|
|
|
|
for (pit_type pit = 0; pit < sl.pit(); ++pit) {
|
|
|
|
h += par_metrics_[pit].height();
|
|
|
|
}
|
|
|
|
int pos = sl.pos();
|
|
|
|
if (pos && boundary)
|
|
|
|
--pos;
|
|
|
|
size_t const rend = pm.pos2row(pos);
|
|
|
|
for (size_t rit = 0; rit != rend; ++rit)
|
|
|
|
h += pm.rows()[rit].height();
|
|
|
|
h += pm.rows()[rend].ascent();
|
|
|
|
return h;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-09-02 11:21:33 +00:00
|
|
|
// the cursor set functions have a special mechanism. When they
|
|
|
|
// realize you left an empty paragraph, they will delete it.
|
|
|
|
|
|
|
|
bool TextMetrics::cursorHome(Cursor & cur)
|
|
|
|
{
|
2013-04-25 21:27:10 +00:00
|
|
|
LASSERT(text_ == cur.text(), return false);
|
2007-09-02 11:21:33 +00:00
|
|
|
ParagraphMetrics const & pm = par_metrics_[cur.pit()];
|
|
|
|
Row const & row = pm.getRow(cur.pos(),cur.boundary());
|
|
|
|
return text_->setCursor(cur, cur.pit(), row.pos());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool TextMetrics::cursorEnd(Cursor & cur)
|
|
|
|
{
|
2013-04-25 21:27:10 +00:00
|
|
|
LASSERT(text_ == cur.text(), return false);
|
2007-09-02 11:21:33 +00:00
|
|
|
// if not on the last row of the par, put the cursor before
|
|
|
|
// the final space exept if I have a spanning inset or one string
|
|
|
|
// is so long that we force a break.
|
|
|
|
pos_type end = cur.textRow().endpos();
|
|
|
|
if (end == 0)
|
|
|
|
// empty text, end-1 is no valid position
|
|
|
|
return false;
|
|
|
|
bool boundary = false;
|
|
|
|
if (end != cur.lastpos()) {
|
|
|
|
if (!cur.paragraph().isLineSeparator(end-1)
|
2014-06-25 10:39:58 +00:00
|
|
|
&& !cur.paragraph().isNewline(end-1))
|
2007-09-02 11:21:33 +00:00
|
|
|
boundary = true;
|
|
|
|
else
|
|
|
|
--end;
|
|
|
|
}
|
|
|
|
return text_->setCursor(cur, cur.pit(), end, true, boundary);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TextMetrics::deleteLineForward(Cursor & cur)
|
|
|
|
{
|
2013-04-25 21:27:10 +00:00
|
|
|
LASSERT(text_ == cur.text(), return);
|
2007-09-02 11:21:33 +00:00
|
|
|
if (cur.lastpos() == 0) {
|
2007-10-22 22:18:52 +00:00
|
|
|
// Paragraph is empty, so we just go forward
|
|
|
|
text_->cursorForward(cur);
|
2007-09-02 11:21:33 +00:00
|
|
|
} else {
|
|
|
|
cur.resetAnchor();
|
2008-09-24 21:27:41 +00:00
|
|
|
cur.setSelection(true); // to avoid deletion
|
2007-09-02 11:21:33 +00:00
|
|
|
cursorEnd(cur);
|
|
|
|
cur.setSelection();
|
|
|
|
// What is this test for ??? (JMarc)
|
|
|
|
if (!cur.selection())
|
|
|
|
text_->deleteWordForward(cur);
|
|
|
|
else
|
|
|
|
cap::cutSelection(cur, true, false);
|
2008-01-12 21:38:51 +00:00
|
|
|
cur.checkBufferStructure();
|
2007-09-02 11:21:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-09-02 11:27:19 +00:00
|
|
|
bool TextMetrics::isLastRow(pit_type pit, Row const & row) const
|
|
|
|
{
|
|
|
|
ParagraphList const & pars = text_->paragraphs();
|
|
|
|
return row.endpos() >= pars[pit].size()
|
|
|
|
&& pit + 1 == pit_type(pars.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool TextMetrics::isFirstRow(pit_type pit, Row const & row) const
|
|
|
|
{
|
|
|
|
return row.pos() == 0 && pit == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-09-02 09:44:08 +00:00
|
|
|
int TextMetrics::leftMargin(int max_width, pit_type pit) const
|
|
|
|
{
|
|
|
|
return leftMargin(max_width, pit, text_->paragraphs()[pit].size());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int TextMetrics::leftMargin(int max_width,
|
|
|
|
pit_type const pit, pos_type const pos) const
|
|
|
|
{
|
|
|
|
ParagraphList const & pars = text_->paragraphs();
|
|
|
|
|
2013-04-25 21:27:10 +00:00
|
|
|
LASSERT(pit >= 0, return 0);
|
|
|
|
LASSERT(pit < int(pars.size()), return 0);
|
2007-09-02 09:44:08 +00:00
|
|
|
Paragraph const & par = pars[pit];
|
2013-04-25 21:27:10 +00:00
|
|
|
LASSERT(pos >= 0, return 0);
|
|
|
|
LASSERT(pos <= par.size(), return 0);
|
2007-09-02 09:44:08 +00:00
|
|
|
Buffer const & buffer = bv_->buffer();
|
|
|
|
//lyxerr << "TextMetrics::leftMargin: pit: " << pit << " pos: " << pos << endl;
|
2008-02-28 01:42:02 +00:00
|
|
|
DocumentClass const & tclass = buffer.params().documentClass();
|
2008-03-06 21:31:27 +00:00
|
|
|
Layout const & layout = par.layout();
|
2007-09-02 09:44:08 +00:00
|
|
|
|
2008-03-06 21:31:27 +00:00
|
|
|
docstring parindent = layout.parindent;
|
2007-09-02 09:44:08 +00:00
|
|
|
|
|
|
|
int l_margin = 0;
|
|
|
|
|
2009-08-09 15:29:34 +00:00
|
|
|
if (text_->isMainText())
|
2008-02-09 17:20:23 +00:00
|
|
|
l_margin += bv_->leftMargin();
|
2007-09-02 09:44:08 +00:00
|
|
|
|
|
|
|
l_margin += theFontMetrics(buffer.params().getFont()).signedWidth(
|
|
|
|
tclass.leftmargin());
|
|
|
|
|
|
|
|
if (par.getDepth() != 0) {
|
|
|
|
// find the next level paragraph
|
2009-08-09 18:35:39 +00:00
|
|
|
pit_type newpar = text_->outerHook(pit);
|
2007-09-02 09:44:08 +00:00
|
|
|
if (newpar != pit_type(pars.size())) {
|
2008-03-06 21:31:27 +00:00
|
|
|
if (pars[newpar].layout().isEnvironment()) {
|
2007-09-02 09:44:08 +00:00
|
|
|
l_margin = leftMargin(max_width, newpar);
|
2014-05-22 21:47:38 +00:00
|
|
|
// Remove the parindent that has been added
|
|
|
|
// if the paragraph was empty.
|
2014-06-25 10:39:58 +00:00
|
|
|
if (pars[newpar].empty()) {
|
2014-05-22 21:47:38 +00:00
|
|
|
docstring pi = pars[newpar].layout().parindent;
|
|
|
|
l_margin -= theFontMetrics(
|
|
|
|
buffer.params().getFont()).signedWidth(pi);
|
|
|
|
}
|
2007-09-02 09:44:08 +00:00
|
|
|
}
|
2008-03-21 11:26:20 +00:00
|
|
|
if (tclass.isDefaultLayout(par.layout())
|
2008-07-10 17:41:52 +00:00
|
|
|
|| tclass.isPlainLayout(par.layout())) {
|
2007-09-02 09:44:08 +00:00
|
|
|
if (pars[newpar].params().noindent())
|
|
|
|
parindent.erase();
|
|
|
|
else
|
2008-03-06 21:31:27 +00:00
|
|
|
parindent = pars[newpar].layout().parindent;
|
2007-09-02 09:44:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-16 13:11:08 +00:00
|
|
|
// This happens after sections or environments in standard classes.
|
|
|
|
// We have to check the previous layout at same depth.
|
2014-06-25 10:39:58 +00:00
|
|
|
if (tclass.isDefaultLayout(par.layout()) && pit > 0
|
|
|
|
&& pars[pit - 1].getDepth() >= par.getDepth()) {
|
2014-05-21 19:47:01 +00:00
|
|
|
pit_type prev = text_->depthHook(pit, par.getDepth());
|
2014-06-25 10:39:58 +00:00
|
|
|
if (pars[prev < pit ? prev : pit - 1].layout().nextnoindent)
|
2014-05-21 19:47:01 +00:00
|
|
|
parindent.erase();
|
2014-05-16 13:11:08 +00:00
|
|
|
}
|
2007-09-02 09:44:08 +00:00
|
|
|
|
2009-08-09 15:29:34 +00:00
|
|
|
FontInfo const labelfont = text_->labelFont(par);
|
2007-09-02 09:44:08 +00:00
|
|
|
FontMetrics const & labelfont_metrics = theFontMetrics(labelfont);
|
|
|
|
|
2008-03-06 21:31:27 +00:00
|
|
|
switch (layout.margintype) {
|
2007-09-02 09:44:08 +00:00
|
|
|
case MARGIN_DYNAMIC:
|
2008-03-06 21:31:27 +00:00
|
|
|
if (!layout.leftmargin.empty()) {
|
2007-09-02 09:44:08 +00:00
|
|
|
l_margin += theFontMetrics(buffer.params().getFont()).signedWidth(
|
2008-03-06 21:31:27 +00:00
|
|
|
layout.leftmargin);
|
2007-09-02 09:44:08 +00:00
|
|
|
}
|
2008-02-23 16:45:38 +00:00
|
|
|
if (!par.labelString().empty()) {
|
2008-03-06 21:31:27 +00:00
|
|
|
l_margin += labelfont_metrics.signedWidth(layout.labelindent);
|
2008-02-23 16:45:38 +00:00
|
|
|
l_margin += labelfont_metrics.width(par.labelString());
|
2008-03-06 21:31:27 +00:00
|
|
|
l_margin += labelfont_metrics.width(layout.labelsep);
|
2007-09-02 09:44:08 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MARGIN_MANUAL: {
|
2008-03-06 21:31:27 +00:00
|
|
|
l_margin += labelfont_metrics.signedWidth(layout.labelindent);
|
2007-09-02 09:44:08 +00:00
|
|
|
// The width of an empty par, even with manual label, should be 0
|
|
|
|
if (!par.empty() && pos >= par.beginOfBody()) {
|
|
|
|
if (!par.getLabelWidthString().empty()) {
|
|
|
|
docstring labstr = par.getLabelWidthString();
|
|
|
|
l_margin += labelfont_metrics.width(labstr);
|
2008-03-06 21:31:27 +00:00
|
|
|
l_margin += labelfont_metrics.width(layout.labelsep);
|
2007-09-02 09:44:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case MARGIN_STATIC: {
|
|
|
|
l_margin += theFontMetrics(buffer.params().getFont()).
|
2008-03-06 21:31:27 +00:00
|
|
|
signedWidth(layout.leftmargin) * 4 / (par.getDepth() + 4);
|
2007-09-02 09:44:08 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case MARGIN_FIRST_DYNAMIC:
|
2008-03-06 21:31:27 +00:00
|
|
|
if (layout.labeltype == LABEL_MANUAL) {
|
2009-04-02 21:36:30 +00:00
|
|
|
// if we are at position 0, we are never in the body
|
|
|
|
if (pos > 0 && pos >= par.beginOfBody())
|
2008-03-06 21:31:27 +00:00
|
|
|
l_margin += labelfont_metrics.signedWidth(layout.leftmargin);
|
2009-04-02 21:36:30 +00:00
|
|
|
else
|
2008-03-06 21:31:27 +00:00
|
|
|
l_margin += labelfont_metrics.signedWidth(layout.labelindent);
|
2007-09-02 09:44:08 +00:00
|
|
|
} else if (pos != 0
|
|
|
|
// Special case to fix problems with
|
|
|
|
// theorems (JMarc)
|
2008-03-06 21:31:27 +00:00
|
|
|
|| (layout.labeltype == LABEL_STATIC
|
|
|
|
&& layout.latextype == LATEX_ENVIRONMENT
|
2009-08-09 18:35:39 +00:00
|
|
|
&& !text_->isFirstInSequence(pit))) {
|
2008-03-06 21:31:27 +00:00
|
|
|
l_margin += labelfont_metrics.signedWidth(layout.leftmargin);
|
2013-02-09 16:13:01 +00:00
|
|
|
} else if (!layout.labelIsAbove()) {
|
2008-03-06 21:31:27 +00:00
|
|
|
l_margin += labelfont_metrics.signedWidth(layout.labelindent);
|
|
|
|
l_margin += labelfont_metrics.width(layout.labelsep);
|
2008-02-23 16:45:38 +00:00
|
|
|
l_margin += labelfont_metrics.width(par.labelString());
|
2007-09-02 09:44:08 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MARGIN_RIGHT_ADDRESS_BOX: {
|
|
|
|
#if 0
|
2012-05-10 08:39:38 +00:00
|
|
|
// The left margin depends on the widest row in this paragraph.
|
|
|
|
// This code is wrong because it depends on the rows, but at the
|
|
|
|
// same time this function is used in redoParagraph to construct
|
2014-03-14 13:22:26 +00:00
|
|
|
// the rows.
|
2012-05-10 08:39:38 +00:00
|
|
|
ParagraphMetrics const & pm = par_metrics_[pit];
|
|
|
|
RowList::const_iterator rit = pm.rows().begin();
|
|
|
|
RowList::const_iterator end = pm.rows().end();
|
2007-09-02 09:44:08 +00:00
|
|
|
int minfill = max_width;
|
|
|
|
for ( ; rit != end; ++rit)
|
|
|
|
if (rit->fill() < minfill)
|
|
|
|
minfill = rit->fill();
|
2012-05-10 08:39:38 +00:00
|
|
|
l_margin += theFontMetrics(buffer.params().getFont()).signedWidth(layout.leftmargin);
|
2007-09-02 09:44:08 +00:00
|
|
|
l_margin += minfill;
|
|
|
|
#endif
|
|
|
|
// also wrong, but much shorter.
|
|
|
|
l_margin += max_width / 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!par.params().leftIndent().zero())
|
|
|
|
l_margin += par.params().leftIndent().inPixels(max_width);
|
|
|
|
|
|
|
|
LyXAlignment align;
|
|
|
|
|
|
|
|
if (par.params().align() == LYX_ALIGN_LAYOUT)
|
2008-03-06 21:31:27 +00:00
|
|
|
align = layout.align;
|
2007-09-02 09:44:08 +00:00
|
|
|
else
|
|
|
|
align = par.params().align();
|
|
|
|
|
|
|
|
// set the correct parindent
|
|
|
|
if (pos == 0
|
2008-03-06 21:31:27 +00:00
|
|
|
&& (layout.labeltype == LABEL_NO_LABEL
|
2013-07-17 22:25:08 +00:00
|
|
|
|| layout.labeltype == LABEL_ABOVE
|
|
|
|
|| layout.labeltype == LABEL_CENTERED
|
|
|
|
|| (layout.labeltype == LABEL_STATIC
|
|
|
|
&& layout.latextype == LATEX_ENVIRONMENT
|
|
|
|
&& !text_->isFirstInSequence(pit)))
|
2011-02-14 00:58:44 +00:00
|
|
|
&& (align == LYX_ALIGN_BLOCK || align == LYX_ALIGN_LEFT)
|
2007-09-02 09:44:08 +00:00
|
|
|
&& !par.params().noindent()
|
|
|
|
// in some insets, paragraphs are never indented
|
2009-08-09 16:19:43 +00:00
|
|
|
&& !text_->inset().neverIndent()
|
2007-09-02 09:44:08 +00:00
|
|
|
// display style insets are always centered, omit indentation
|
|
|
|
&& !(!par.empty()
|
2013-07-17 22:25:08 +00:00
|
|
|
&& par.isInset(pos)
|
|
|
|
&& par.getInset(pos)->display())
|
|
|
|
&& (!(tclass.isDefaultLayout(par.layout())
|
|
|
|
|| tclass.isPlainLayout(par.layout()))
|
2014-03-14 13:22:26 +00:00
|
|
|
|| buffer.params().paragraph_separation
|
2013-07-17 22:25:08 +00:00
|
|
|
== BufferParams::ParagraphIndentSeparation)) {
|
|
|
|
// use the parindent of the layout when the
|
|
|
|
// default indentation is used otherwise use
|
|
|
|
// the indentation set in the document
|
|
|
|
// settings
|
2009-07-19 21:13:27 +00:00
|
|
|
if (buffer.params().getIndentation().asLyXCommand() == "default")
|
2010-05-05 16:23:11 +00:00
|
|
|
l_margin += theFontMetrics(
|
|
|
|
buffer.params().getFont()).signedWidth(parindent);
|
2009-07-19 21:13:27 +00:00
|
|
|
else
|
|
|
|
l_margin += buffer.params().getIndentation().inPixels(*bv_);
|
|
|
|
}
|
2014-03-14 13:22:26 +00:00
|
|
|
|
2007-09-02 09:44:08 +00:00
|
|
|
return l_margin;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-21 18:22:32 +00:00
|
|
|
#ifdef KEEP_OLD_METRICS_CODE
|
2007-08-18 21:17:10 +00:00
|
|
|
int TextMetrics::singleWidth(pit_type pit, pos_type pos) const
|
|
|
|
{
|
|
|
|
ParagraphMetrics const & pm = par_metrics_[pit];
|
|
|
|
|
2008-02-27 23:03:26 +00:00
|
|
|
return pm.singleWidth(pos, displayFont(pit, pos));
|
2007-08-18 21:17:10 +00:00
|
|
|
}
|
2013-07-21 18:22:32 +00:00
|
|
|
#endif
|
2007-08-18 21:17:10 +00:00
|
|
|
|
2007-08-27 22:36:20 +00:00
|
|
|
void TextMetrics::draw(PainterInfo & pi, int x, int y) const
|
|
|
|
{
|
2007-08-28 18:05:55 +00:00
|
|
|
if (par_metrics_.empty())
|
|
|
|
return;
|
2007-08-31 10:05:12 +00:00
|
|
|
|
2007-09-11 16:04:10 +00:00
|
|
|
origin_.x_ = x;
|
|
|
|
origin_.y_ = y;
|
2007-09-05 13:04:05 +00:00
|
|
|
|
2007-09-11 16:04:10 +00:00
|
|
|
ParMetricsCache::iterator it = par_metrics_.begin();
|
|
|
|
ParMetricsCache::iterator const pm_end = par_metrics_.end();
|
2007-09-09 20:21:43 +00:00
|
|
|
y -= it->second.ascent();
|
2007-09-05 13:04:05 +00:00
|
|
|
for (; it != pm_end; ++it) {
|
2007-08-27 22:36:20 +00:00
|
|
|
ParagraphMetrics const & pmi = it->second;
|
|
|
|
y += pmi.ascent();
|
2007-09-05 13:04:05 +00:00
|
|
|
pit_type const pit = it->first;
|
|
|
|
// Save the paragraph position in the cache.
|
2007-09-11 16:04:10 +00:00
|
|
|
it->second.setPosition(y);
|
2007-09-05 13:04:05 +00:00
|
|
|
drawParagraph(pi, pit, x, y);
|
2007-08-27 22:36:20 +00:00
|
|
|
y += pmi.descent();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-08-28 08:57:13 +00:00
|
|
|
|
2007-08-30 15:17:38 +00:00
|
|
|
void TextMetrics::drawParagraph(PainterInfo & pi, pit_type pit, int x, int y) const
|
2007-08-28 08:57:13 +00:00
|
|
|
{
|
2007-11-17 11:27:03 +00:00
|
|
|
BufferParams const & bparams = bv_->buffer().params();
|
2007-08-28 08:57:13 +00:00
|
|
|
ParagraphMetrics const & pm = par_metrics_[pit];
|
|
|
|
if (pm.rows().empty())
|
|
|
|
return;
|
|
|
|
|
2007-11-06 14:07:49 +00:00
|
|
|
bool const original_drawing_state = pi.pain.isDrawingEnabled();
|
2007-11-16 23:00:57 +00:00
|
|
|
int const ww = bv_->workHeight();
|
|
|
|
size_t const nrows = pm.rows().size();
|
2007-11-06 14:07:49 +00:00
|
|
|
|
2008-03-03 11:03:47 +00:00
|
|
|
Cursor const & cur = bv_->cursor();
|
|
|
|
DocIterator sel_beg = cur.selectionBegin();
|
|
|
|
DocIterator sel_end = cur.selectionEnd();
|
|
|
|
bool selection = cur.selection()
|
|
|
|
// This is our text.
|
|
|
|
&& cur.text() == text_
|
2008-03-21 11:26:20 +00:00
|
|
|
// if the anchor is outside, this is not our selection
|
2010-04-15 17:49:15 +00:00
|
|
|
&& cur.normalAnchor().text() == text_
|
2008-03-03 11:03:47 +00:00
|
|
|
&& pit >= sel_beg.pit() && pit <= sel_end.pit();
|
|
|
|
|
Patch by Vincent that solves a number of problems related to the painting of a selection:
1. When a listing is inserted in a bit of text, the line above the listing is not drawn over the full width like it is done for lines above other insets. This is because InsetListing has a AlignLeft alignment. Now, if you start selecting downwards with the mouse in this empty area, strange selection drawings appear (see attachment).
This is caused by the fact that starting your selection at such a place, causes beg.boundary() to be true in TextMetrics::drawRowSelection(..). This is correct, but this value is true for _all_ selected lines. Now, the selection acts as if it is RTL text. Therefore, just like for end.boundary, this value needs to be reset for every line.
2. Starting your selection in an end margin often causes the selection in this end margin to be painted later. This is because when starting your selection in an end margin, you may have set a (possible empty) selection before really selecting the end margin. The problem is that the checksum (computed later) is the same for this empty selection and for the end margin selection. Therfore, we need a call to cur.setSelection() before evaluating cur.selection().
3. In the following two lines, it is assumed that there is only an end margin to be painted if the selection extends to the next paragraph. This is not true for the above described case of an AlignLeft Inset. Then, the margin has also be drawn within a paragraph
4. The end and begin margins are only painted when the selection extends into the following or previous paragraph. This difference is not resembled in the checksum if you first select a row completely and then procede to the next or previous paragraph as the selection remains at the end of a row. This also holds for the AlignLeft case. Therefore I added a term to the checksum to monitor whether the end and begin margins need to be drawn.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@26399 a592a061-630c-0410-9148-cb99ea01b6c8
2008-09-14 14:32:40 +00:00
|
|
|
// We store the begin and end pos of the selection relative to this par
|
|
|
|
DocIterator sel_beg_par = cur.selectionBegin();
|
|
|
|
DocIterator sel_end_par = cur.selectionEnd();
|
2014-03-14 13:22:26 +00:00
|
|
|
|
2008-03-03 11:03:47 +00:00
|
|
|
// We care only about visible selection.
|
|
|
|
if (selection) {
|
|
|
|
if (pit != sel_beg.pit()) {
|
Patch by Vincent that solves a number of problems related to the painting of a selection:
1. When a listing is inserted in a bit of text, the line above the listing is not drawn over the full width like it is done for lines above other insets. This is because InsetListing has a AlignLeft alignment. Now, if you start selecting downwards with the mouse in this empty area, strange selection drawings appear (see attachment).
This is caused by the fact that starting your selection at such a place, causes beg.boundary() to be true in TextMetrics::drawRowSelection(..). This is correct, but this value is true for _all_ selected lines. Now, the selection acts as if it is RTL text. Therefore, just like for end.boundary, this value needs to be reset for every line.
2. Starting your selection in an end margin often causes the selection in this end margin to be painted later. This is because when starting your selection in an end margin, you may have set a (possible empty) selection before really selecting the end margin. The problem is that the checksum (computed later) is the same for this empty selection and for the end margin selection. Therfore, we need a call to cur.setSelection() before evaluating cur.selection().
3. In the following two lines, it is assumed that there is only an end margin to be painted if the selection extends to the next paragraph. This is not true for the above described case of an AlignLeft Inset. Then, the margin has also be drawn within a paragraph
4. The end and begin margins are only painted when the selection extends into the following or previous paragraph. This difference is not resembled in the checksum if you first select a row completely and then procede to the next or previous paragraph as the selection remains at the end of a row. This also holds for the AlignLeft case. Therefore I added a term to the checksum to monitor whether the end and begin margins need to be drawn.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@26399 a592a061-630c-0410-9148-cb99ea01b6c8
2008-09-14 14:32:40 +00:00
|
|
|
sel_beg_par.pit() = pit;
|
|
|
|
sel_beg_par.pos() = 0;
|
2008-03-03 11:03:47 +00:00
|
|
|
}
|
|
|
|
if (pit != sel_end.pit()) {
|
Patch by Vincent that solves a number of problems related to the painting of a selection:
1. When a listing is inserted in a bit of text, the line above the listing is not drawn over the full width like it is done for lines above other insets. This is because InsetListing has a AlignLeft alignment. Now, if you start selecting downwards with the mouse in this empty area, strange selection drawings appear (see attachment).
This is caused by the fact that starting your selection at such a place, causes beg.boundary() to be true in TextMetrics::drawRowSelection(..). This is correct, but this value is true for _all_ selected lines. Now, the selection acts as if it is RTL text. Therefore, just like for end.boundary, this value needs to be reset for every line.
2. Starting your selection in an end margin often causes the selection in this end margin to be painted later. This is because when starting your selection in an end margin, you may have set a (possible empty) selection before really selecting the end margin. The problem is that the checksum (computed later) is the same for this empty selection and for the end margin selection. Therfore, we need a call to cur.setSelection() before evaluating cur.selection().
3. In the following two lines, it is assumed that there is only an end margin to be painted if the selection extends to the next paragraph. This is not true for the above described case of an AlignLeft Inset. Then, the margin has also be drawn within a paragraph
4. The end and begin margins are only painted when the selection extends into the following or previous paragraph. This difference is not resembled in the checksum if you first select a row completely and then procede to the next or previous paragraph as the selection remains at the end of a row. This also holds for the AlignLeft case. Therefore I added a term to the checksum to monitor whether the end and begin margins need to be drawn.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@26399 a592a061-630c-0410-9148-cb99ea01b6c8
2008-09-14 14:32:40 +00:00
|
|
|
sel_end_par.pit() = pit;
|
|
|
|
sel_end_par.pos() = sel_end_par.lastpos();
|
2008-03-03 11:03:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-11-16 22:52:15 +00:00
|
|
|
for (size_t i = 0; i != nrows; ++i) {
|
2007-11-16 23:00:57 +00:00
|
|
|
|
2007-11-16 22:52:15 +00:00
|
|
|
Row const & row = pm.rows()[i];
|
|
|
|
if (i)
|
|
|
|
y += row.ascent();
|
|
|
|
|
|
|
|
bool const inside = (y + row.descent() >= 0
|
|
|
|
&& y - row.ascent() < ww);
|
2007-11-16 23:00:57 +00:00
|
|
|
// It is not needed to draw on screen if we are not inside.
|
2007-11-06 14:07:49 +00:00
|
|
|
pi.pain.setDrawingEnabled(inside && original_drawing_state);
|
2013-10-11 14:12:20 +00:00
|
|
|
RowPainter rp(pi, *text_, pit, row, x, y);
|
2007-08-30 13:19:24 +00:00
|
|
|
|
2008-03-03 11:03:47 +00:00
|
|
|
if (selection)
|
Patch by Vincent that solves a number of problems related to the painting of a selection:
1. When a listing is inserted in a bit of text, the line above the listing is not drawn over the full width like it is done for lines above other insets. This is because InsetListing has a AlignLeft alignment. Now, if you start selecting downwards with the mouse in this empty area, strange selection drawings appear (see attachment).
This is caused by the fact that starting your selection at such a place, causes beg.boundary() to be true in TextMetrics::drawRowSelection(..). This is correct, but this value is true for _all_ selected lines. Now, the selection acts as if it is RTL text. Therefore, just like for end.boundary, this value needs to be reset for every line.
2. Starting your selection in an end margin often causes the selection in this end margin to be painted later. This is because when starting your selection in an end margin, you may have set a (possible empty) selection before really selecting the end margin. The problem is that the checksum (computed later) is the same for this empty selection and for the end margin selection. Therfore, we need a call to cur.setSelection() before evaluating cur.selection().
3. In the following two lines, it is assumed that there is only an end margin to be painted if the selection extends to the next paragraph. This is not true for the above described case of an AlignLeft Inset. Then, the margin has also be drawn within a paragraph
4. The end and begin margins are only painted when the selection extends into the following or previous paragraph. This difference is not resembled in the checksum if you first select a row completely and then procede to the next or previous paragraph as the selection remains at the end of a row. This also holds for the AlignLeft case. Therefore I added a term to the checksum to monitor whether the end and begin margins need to be drawn.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@26399 a592a061-630c-0410-9148-cb99ea01b6c8
2008-09-14 14:32:40 +00:00
|
|
|
row.setSelectionAndMargins(sel_beg_par, sel_end_par);
|
2008-03-03 11:03:47 +00:00
|
|
|
else
|
|
|
|
row.setSelection(-1, -1);
|
2014-03-14 13:22:26 +00:00
|
|
|
|
Patch by Vincent that solves a number of problems related to the painting of a selection:
1. When a listing is inserted in a bit of text, the line above the listing is not drawn over the full width like it is done for lines above other insets. This is because InsetListing has a AlignLeft alignment. Now, if you start selecting downwards with the mouse in this empty area, strange selection drawings appear (see attachment).
This is caused by the fact that starting your selection at such a place, causes beg.boundary() to be true in TextMetrics::drawRowSelection(..). This is correct, but this value is true for _all_ selected lines. Now, the selection acts as if it is RTL text. Therefore, just like for end.boundary, this value needs to be reset for every line.
2. Starting your selection in an end margin often causes the selection in this end margin to be painted later. This is because when starting your selection in an end margin, you may have set a (possible empty) selection before really selecting the end margin. The problem is that the checksum (computed later) is the same for this empty selection and for the end margin selection. Therfore, we need a call to cur.setSelection() before evaluating cur.selection().
3. In the following two lines, it is assumed that there is only an end margin to be painted if the selection extends to the next paragraph. This is not true for the above described case of an AlignLeft Inset. Then, the margin has also be drawn within a paragraph
4. The end and begin margins are only painted when the selection extends into the following or previous paragraph. This difference is not resembled in the checksum if you first select a row completely and then procede to the next or previous paragraph as the selection remains at the end of a row. This also holds for the AlignLeft case. Therefore I added a term to the checksum to monitor whether the end and begin margins need to be drawn.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@26399 a592a061-630c-0410-9148-cb99ea01b6c8
2008-09-14 14:32:40 +00:00
|
|
|
// The row knows nothing about the paragraph, so we have to check
|
|
|
|
// whether this row is the first or last and update the margins.
|
|
|
|
if (row.selection()) {
|
|
|
|
if (row.sel_beg == 0)
|
2008-10-13 23:19:06 +00:00
|
|
|
row.begin_margin_sel = sel_beg.pit() < pit;
|
Patch by Vincent that solves a number of problems related to the painting of a selection:
1. When a listing is inserted in a bit of text, the line above the listing is not drawn over the full width like it is done for lines above other insets. This is because InsetListing has a AlignLeft alignment. Now, if you start selecting downwards with the mouse in this empty area, strange selection drawings appear (see attachment).
This is caused by the fact that starting your selection at such a place, causes beg.boundary() to be true in TextMetrics::drawRowSelection(..). This is correct, but this value is true for _all_ selected lines. Now, the selection acts as if it is RTL text. Therefore, just like for end.boundary, this value needs to be reset for every line.
2. Starting your selection in an end margin often causes the selection in this end margin to be painted later. This is because when starting your selection in an end margin, you may have set a (possible empty) selection before really selecting the end margin. The problem is that the checksum (computed later) is the same for this empty selection and for the end margin selection. Therfore, we need a call to cur.setSelection() before evaluating cur.selection().
3. In the following two lines, it is assumed that there is only an end margin to be painted if the selection extends to the next paragraph. This is not true for the above described case of an AlignLeft Inset. Then, the margin has also be drawn within a paragraph
4. The end and begin margins are only painted when the selection extends into the following or previous paragraph. This difference is not resembled in the checksum if you first select a row completely and then procede to the next or previous paragraph as the selection remains at the end of a row. This also holds for the AlignLeft case. Therefore I added a term to the checksum to monitor whether the end and begin margins need to be drawn.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@26399 a592a061-630c-0410-9148-cb99ea01b6c8
2008-09-14 14:32:40 +00:00
|
|
|
if (row.sel_end == sel_end_par.lastpos())
|
2008-10-13 23:19:06 +00:00
|
|
|
row.end_margin_sel = sel_end.pit() > pit;
|
Patch by Vincent that solves a number of problems related to the painting of a selection:
1. When a listing is inserted in a bit of text, the line above the listing is not drawn over the full width like it is done for lines above other insets. This is because InsetListing has a AlignLeft alignment. Now, if you start selecting downwards with the mouse in this empty area, strange selection drawings appear (see attachment).
This is caused by the fact that starting your selection at such a place, causes beg.boundary() to be true in TextMetrics::drawRowSelection(..). This is correct, but this value is true for _all_ selected lines. Now, the selection acts as if it is RTL text. Therefore, just like for end.boundary, this value needs to be reset for every line.
2. Starting your selection in an end margin often causes the selection in this end margin to be painted later. This is because when starting your selection in an end margin, you may have set a (possible empty) selection before really selecting the end margin. The problem is that the checksum (computed later) is the same for this empty selection and for the end margin selection. Therfore, we need a call to cur.setSelection() before evaluating cur.selection().
3. In the following two lines, it is assumed that there is only an end margin to be painted if the selection extends to the next paragraph. This is not true for the above described case of an AlignLeft Inset. Then, the margin has also be drawn within a paragraph
4. The end and begin margins are only painted when the selection extends into the following or previous paragraph. This difference is not resembled in the checksum if you first select a row completely and then procede to the next or previous paragraph as the selection remains at the end of a row. This also holds for the AlignLeft case. Therefore I added a term to the checksum to monitor whether the end and begin margins need to be drawn.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@26399 a592a061-630c-0410-9148-cb99ea01b6c8
2008-09-14 14:32:40 +00:00
|
|
|
}
|
2008-03-03 11:03:47 +00:00
|
|
|
|
2007-08-28 08:57:13 +00:00
|
|
|
// Row signature; has row changed since last paint?
|
2007-11-17 11:27:03 +00:00
|
|
|
row.setCrc(pm.computeRowSignature(row, bparams));
|
2007-11-16 22:52:15 +00:00
|
|
|
bool row_has_changed = row.changed();
|
2008-03-21 11:26:20 +00:00
|
|
|
|
2009-08-10 20:54:22 +00:00
|
|
|
// Take this opportunity to spellcheck the row contents.
|
|
|
|
if (row_has_changed && lyxrc.spellcheck_continuously) {
|
2010-09-14 05:24:04 +00:00
|
|
|
text_->getPar(pit).spellCheck();
|
2009-08-10 20:54:22 +00:00
|
|
|
}
|
|
|
|
|
2007-11-17 11:27:03 +00:00
|
|
|
// Don't paint the row if a full repaint has not been requested
|
2007-11-19 14:13:06 +00:00
|
|
|
// and if it has not changed.
|
2007-11-17 11:27:03 +00:00
|
|
|
if (!pi.full_repaint && !row_has_changed) {
|
|
|
|
// Paint only the insets if the text itself is
|
2007-08-30 13:19:24 +00:00
|
|
|
// unchanged.
|
|
|
|
rp.paintOnlyInsets();
|
2007-11-16 22:52:15 +00:00
|
|
|
y += row.descent();
|
2007-08-30 13:19:24 +00:00
|
|
|
continue;
|
|
|
|
}
|
2007-08-28 08:57:13 +00:00
|
|
|
|
2007-11-17 11:27:03 +00:00
|
|
|
// Clear background of this row if paragraph background was not
|
|
|
|
// already cleared because of a full repaint.
|
|
|
|
if (!pi.full_repaint && row_has_changed) {
|
2007-11-16 22:52:15 +00:00
|
|
|
pi.pain.fillRectangle(x, y - row.ascent(),
|
|
|
|
width(), row.height(), pi.background_color);
|
2007-09-01 09:24:20 +00:00
|
|
|
}
|
2014-03-14 13:22:26 +00:00
|
|
|
|
2007-08-30 13:19:24 +00:00
|
|
|
// Instrumentation for testing row cache (see also
|
|
|
|
// 12 lines lower):
|
2007-11-17 11:27:03 +00:00
|
|
|
if (lyxerr.debugging(Debug::PAINTING) && inside
|
Patch by Vincent that solves a number of problems related to the painting of a selection:
1. When a listing is inserted in a bit of text, the line above the listing is not drawn over the full width like it is done for lines above other insets. This is because InsetListing has a AlignLeft alignment. Now, if you start selecting downwards with the mouse in this empty area, strange selection drawings appear (see attachment).
This is caused by the fact that starting your selection at such a place, causes beg.boundary() to be true in TextMetrics::drawRowSelection(..). This is correct, but this value is true for _all_ selected lines. Now, the selection acts as if it is RTL text. Therefore, just like for end.boundary, this value needs to be reset for every line.
2. Starting your selection in an end margin often causes the selection in this end margin to be painted later. This is because when starting your selection in an end margin, you may have set a (possible empty) selection before really selecting the end margin. The problem is that the checksum (computed later) is the same for this empty selection and for the end margin selection. Therfore, we need a call to cur.setSelection() before evaluating cur.selection().
3. In the following two lines, it is assumed that there is only an end margin to be painted if the selection extends to the next paragraph. This is not true for the above described case of an AlignLeft Inset. Then, the margin has also be drawn within a paragraph
4. The end and begin margins are only painted when the selection extends into the following or previous paragraph. This difference is not resembled in the checksum if you first select a row completely and then procede to the next or previous paragraph as the selection remains at the end of a row. This also holds for the AlignLeft case. Therefore I added a term to the checksum to monitor whether the end and begin margins need to be drawn.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@26399 a592a061-630c-0410-9148-cb99ea01b6c8
2008-09-14 14:32:40 +00:00
|
|
|
&& (row.selection() || pi.full_repaint || row_has_changed)) {
|
2009-08-09 15:29:34 +00:00
|
|
|
string const foreword = text_->isMainText() ?
|
2007-11-17 11:27:03 +00:00
|
|
|
"main text redraw " : "inset text redraw: ";
|
|
|
|
LYXERR(Debug::PAINTING, foreword << "pit=" << pit << " row=" << i
|
Patch by Vincent that solves a number of problems related to the painting of a selection:
1. When a listing is inserted in a bit of text, the line above the listing is not drawn over the full width like it is done for lines above other insets. This is because InsetListing has a AlignLeft alignment. Now, if you start selecting downwards with the mouse in this empty area, strange selection drawings appear (see attachment).
This is caused by the fact that starting your selection at such a place, causes beg.boundary() to be true in TextMetrics::drawRowSelection(..). This is correct, but this value is true for _all_ selected lines. Now, the selection acts as if it is RTL text. Therefore, just like for end.boundary, this value needs to be reset for every line.
2. Starting your selection in an end margin often causes the selection in this end margin to be painted later. This is because when starting your selection in an end margin, you may have set a (possible empty) selection before really selecting the end margin. The problem is that the checksum (computed later) is the same for this empty selection and for the end margin selection. Therfore, we need a call to cur.setSelection() before evaluating cur.selection().
3. In the following two lines, it is assumed that there is only an end margin to be painted if the selection extends to the next paragraph. This is not true for the above described case of an AlignLeft Inset. Then, the margin has also be drawn within a paragraph
4. The end and begin margins are only painted when the selection extends into the following or previous paragraph. This difference is not resembled in the checksum if you first select a row completely and then procede to the next or previous paragraph as the selection remains at the end of a row. This also holds for the AlignLeft case. Therefore I added a term to the checksum to monitor whether the end and begin margins need to be drawn.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@26399 a592a061-630c-0410-9148-cb99ea01b6c8
2008-09-14 14:32:40 +00:00
|
|
|
<< " row_selection=" << row.selection()
|
2007-11-17 11:27:03 +00:00
|
|
|
<< " full_repaint=" << pi.full_repaint
|
|
|
|
<< " row_has_changed=" << row_has_changed);
|
2007-08-28 08:57:13 +00:00
|
|
|
}
|
2007-09-01 09:24:20 +00:00
|
|
|
|
|
|
|
// Backup full_repaint status and force full repaint
|
|
|
|
// for inner insets as the Row has been cleared out.
|
|
|
|
bool tmp = pi.full_repaint;
|
|
|
|
pi.full_repaint = true;
|
2010-11-25 13:16:30 +00:00
|
|
|
|
|
|
|
rp.paintSelection();
|
2007-08-30 13:19:24 +00:00
|
|
|
rp.paintAppendix();
|
|
|
|
rp.paintDepthBar();
|
|
|
|
rp.paintChangeBar();
|
2009-08-19 22:43:52 +00:00
|
|
|
bool const is_rtl = text_->isRTL(text_->getPar(pit));
|
|
|
|
if (i == 0 && !is_rtl)
|
2007-08-30 13:19:24 +00:00
|
|
|
rp.paintFirst();
|
2009-08-19 22:43:52 +00:00
|
|
|
if (i == nrows - 1 && is_rtl)
|
|
|
|
rp.paintLast();
|
2007-08-30 13:19:24 +00:00
|
|
|
rp.paintText();
|
2009-08-19 22:43:52 +00:00
|
|
|
if (i == nrows - 1 && !is_rtl)
|
2007-08-30 13:19:24 +00:00
|
|
|
rp.paintLast();
|
2009-08-19 22:43:52 +00:00
|
|
|
if (i == 0 && is_rtl)
|
|
|
|
rp.paintFirst();
|
2007-11-16 22:52:15 +00:00
|
|
|
y += row.descent();
|
2010-11-25 13:16:30 +00:00
|
|
|
|
2007-09-01 09:24:20 +00:00
|
|
|
// Restore full_repaint status.
|
|
|
|
pi.full_repaint = tmp;
|
2007-08-28 08:57:13 +00:00
|
|
|
}
|
|
|
|
// Re-enable screen drawing for future use of the painter.
|
2007-11-06 14:07:49 +00:00
|
|
|
pi.pain.setDrawingEnabled(original_drawing_state);
|
2007-08-28 08:57:13 +00:00
|
|
|
|
2007-11-28 22:12:03 +00:00
|
|
|
//LYXERR(Debug::PAINTING, ".");
|
2007-08-28 08:57:13 +00:00
|
|
|
}
|
2007-08-27 22:36:20 +00:00
|
|
|
|
2007-08-31 10:05:12 +00:00
|
|
|
|
2008-03-21 11:26:20 +00:00
|
|
|
void TextMetrics::completionPosAndDim(Cursor const & cur, int & x, int & y,
|
2008-03-15 12:22:28 +00:00
|
|
|
Dimension & dim) const
|
|
|
|
{
|
|
|
|
Cursor const & bvcur = cur.bv().cursor();
|
2008-03-21 11:26:20 +00:00
|
|
|
|
2008-03-15 12:22:28 +00:00
|
|
|
// get word in front of cursor
|
|
|
|
docstring word = text_->previousWord(bvcur.top());
|
|
|
|
DocIterator wordStart = bvcur;
|
|
|
|
wordStart.pos() -= word.length();
|
2008-03-21 11:26:20 +00:00
|
|
|
|
2008-03-15 12:22:28 +00:00
|
|
|
// get position on screen of the word start and end
|
2010-04-21 13:03:04 +00:00
|
|
|
//FIXME: Is it necessary to explicitly set this to false?
|
|
|
|
wordStart.boundary(false);
|
|
|
|
Point lxy = cur.bv().getPos(wordStart);
|
|
|
|
Point rxy = cur.bv().getPos(bvcur);
|
2008-03-21 11:26:20 +00:00
|
|
|
|
2008-03-15 12:22:28 +00:00
|
|
|
// calculate dimensions of the word
|
2013-06-14 17:24:29 +00:00
|
|
|
Row row;
|
|
|
|
row.pos(wordStart.pos());
|
|
|
|
row.endpos(bvcur.pos());
|
|
|
|
setRowHeight(row, bvcur.pit(), false);
|
|
|
|
dim = row.dimension();
|
2008-03-15 12:22:28 +00:00
|
|
|
dim.wid = abs(rxy.x_ - lxy.x_);
|
2008-03-21 11:26:20 +00:00
|
|
|
|
2008-03-15 12:22:28 +00:00
|
|
|
// calculate position of word
|
|
|
|
y = lxy.y_;
|
|
|
|
x = min(rxy.x_, lxy.x_);
|
2008-03-21 11:26:20 +00:00
|
|
|
|
2008-03-15 12:22:28 +00:00
|
|
|
//lyxerr << "wid=" << dim.width() << " x=" << x << " y=" << y << " lxy.x_=" << lxy.x_ << " rxy.x_=" << rxy.x_ << " word=" << word << std::endl;
|
|
|
|
//lyxerr << " wordstart=" << wordStart << " bvcur=" << bvcur << " cur=" << cur << std::endl;
|
|
|
|
}
|
|
|
|
|
2007-09-02 09:44:08 +00:00
|
|
|
//int TextMetrics::pos2x(pit_type pit, pos_type pos) const
|
2007-01-01 11:36:30 +00:00
|
|
|
//{
|
2007-08-18 21:10:45 +00:00
|
|
|
// ParagraphMetrics const & pm = par_metrics_[pit];
|
2007-01-01 11:36:30 +00:00
|
|
|
// Row const & r = pm.rows()[row];
|
|
|
|
// int x = 0;
|
|
|
|
// pos -= r.pos();
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
2006-12-29 23:54:48 +00:00
|
|
|
int defaultRowHeight()
|
|
|
|
{
|
2007-10-28 18:51:54 +00:00
|
|
|
return int(theFontMetrics(sane_font).maxHeight() * 1.2);
|
2006-12-29 23:54:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace lyx
|