3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
10 * \author Jean-Marc Lasgouttes
12 * Full author contact details are available in file CREDITS.
14 * Metrics for an on-screen text row.
21 #include "DocIterator.h"
23 #include "frontends/FontMetrics.h"
25 #include "support/debug.h"
26 #include "support/lassert.h"
35 using frontend::FontMetrics;
37 double Row::Element::pos2x(pos_type const i) const
39 bool const rtl = font.isVisibleRightToLeft();
41 // handle first the two bounds of the element
42 if ((!rtl && pos >= i) || (rtl && endpos <= i))
44 if ((!rtl && endpos <= i) || (rtl && pos >= i))
47 FontMetrics const & fm = theFontMetrics(font);
48 // FIXME Avoid caching of metrics there?
49 int const w = fm.width(str.substr(0, i - pos));
58 : separator(0), label_hfill(0), x(0),
59 sel_beg(-1), sel_end(-1),
60 begin_margin_sel(false), end_margin_sel(false),
61 changed_(false), crc_(0), pos_(0), end_(0)
65 void Row::setCrc(size_type crc) const
67 changed_ = crc != crc_;
72 void Row::pos(pos_type p)
78 void Row::endpos(pos_type p)
84 bool Row::isMarginSelected(bool left_margin, DocIterator const & beg,
85 DocIterator const & end) const
87 pos_type const sel_pos = left_margin ? sel_beg : sel_end;
88 pos_type const margin_pos = left_margin ? pos_ : end_;
90 // Is the chosen margin selected ?
91 if (sel_pos == margin_pos) {
92 if (beg.pos() == end.pos())
93 // This is a special case in which the space between after
94 // pos i-1 and before pos i is selected, i.e. the margins
95 // (see DocIterator::boundary_).
96 return beg.boundary() && !end.boundary();
97 else if (end.pos() == margin_pos)
98 // If the selection ends around the margin, it is only
99 // drawn if the cursor is after the margin.
100 return !end.boundary();
101 else if (beg.pos() == margin_pos)
102 // If the selection begins around the margin, it is
103 // only drawn if the cursor is before the margin.
104 return beg.boundary();
112 void Row::setSelectionAndMargins(DocIterator const & beg,
113 DocIterator const & end) const
115 setSelection(beg.pos(), end.pos());
118 end_margin_sel = isMarginSelected(false, beg, end);
119 begin_margin_sel = isMarginSelected(true, beg, end);
124 void Row::setSelection(pos_type beg, pos_type end) const
126 if (pos_ >= beg && pos_ <= end)
128 else if (beg > pos_ && beg <= end_)
133 if (end_ >= beg && end_ <= end)
135 else if (end < end_ && end >= pos_)
142 bool Row::selection() const
144 return sel_beg != -1 && sel_end != -1;
148 ostream & operator<<(ostream & os, Row::Element const & e)
150 if (e.font.isVisibleRightToLeft())
151 os << e.endpos << "<<" << e.pos << " ";
153 os << e.pos << ">>" << e.endpos << " ";
156 case Row::Element::STRING:
157 os << "STRING: `" << to_utf8(e.str) << "'";
159 case Row::Element::COMPLETION:
160 os << "COMPLETION: `" << to_utf8(e.str) << "'";
162 case Row::Element::INSET:
163 os << "INSET: " << to_utf8(e.inset->layoutName());
165 case Row::Element::SEPARATOR:
166 os << "SEPARATOR: " << e.dim.wid << "+" << e.extra;
168 case Row::Element::SPACE:
169 os << "SPACE: " << e.dim.wid;
176 ostream & operator<<(ostream & os, Row const & row)
178 os << " pos: " << row.pos_ << " end: " << row.end_
179 << " width: " << row.dim_.wid
180 << " ascent: " << row.dim_.asc
181 << " descent: " << row.dim_.des
182 << " separator: " << row.separator
183 << " label_hfill : " << row.label_hfill << "\n";
184 Row::Elements::const_iterator it = row.elements_.begin();
185 for ( ; it != row.elements_.end() ; ++it) {
186 os << "** " << *it << endl;
192 bool Row::sameString(Font const & f, Change const & ch) const
194 if (elements_.empty())
196 Element const & elt = elements_.back();
197 return elt.type == Element::STRING && !elt.final
198 && elt.font == f && elt.change == ch;
202 void Row::finalizeLast()
204 if (elements_.empty())
206 Element & elt = elements_.back();
211 if (elt.type == Element::STRING) {
212 elt.dim.wid = theFontMetrics(elt.font).width(elt.str);
213 dim_.wid += elt.dim.wid;
218 void Row::add(pos_type const pos, Inset const * ins, Dimension const & dim,
219 Font const & f, Change const & ch)
222 Element e(Element::INSET, pos, f, ch);
225 elements_.push_back(e);
230 void Row::add(pos_type const pos, char_type const c,
231 Font const & f, Change const & ch)
233 if (!sameString(f, ch)) {
235 Element e(Element::STRING, pos, f, ch);
236 elements_.push_back(e);
238 //lyxerr << "FONT " <<back().font.language() << endl;
240 back().endpos = pos + 1;
244 void Row::addCompletion(pos_type const pos, docstring const & s,
245 Font const & f, Change const & ch)
248 Element e(Element::COMPLETION, pos, f, ch);
250 // A completion has no size
252 elements_.push_back(e);
257 void Row::addSeparator(pos_type const pos, char_type const c,
258 Font const & f, Change const & ch)
261 Element e(Element::SEPARATOR, pos, f, ch);
263 e.dim.wid = theFontMetrics(f).width(c);
264 elements_.push_back(e);
265 dim_.wid += e.dim.wid;
269 void Row::addSpace(pos_type const pos, int const width,
270 Font const & f, Change const & ch)
273 Element e(Element::SPACE, pos, f, ch);
275 elements_.push_back(e);
276 dim_.wid += e.dim.wid;
282 dim_.wid -= elements_.back().dim.wid;
283 elements_.pop_back();
287 void Row::separate_back(pos_type const keep)
291 int i = elements_.size();
293 int new_wid = dim_.wid;
294 if (i > 0 && elements_[i - 1].isSeparator() && new_end > keep) {
296 new_end = elements_[i].pos;
297 new_wid -= elements_[i].dim.wid;
300 while (i > 0 && !elements_[i - 1].isSeparator() && new_end > keep) {
302 new_end = elements_[i].pos;
303 new_wid -= elements_[i].dim.wid;
309 elements_.erase(elements_.begin() + i, elements_.end());
313 void Row::reverseRtL()
316 pos_type const end = elements_.size();
319 while (i < end && !elements_[i].font.isRightToLeft())
324 // look for a RtL sequence
326 while (j < end && elements_[j].font.isRightToLeft())
328 reverse(elements_.begin() + i, elements_.begin() + j);