]> git.lyx.org Git - lyx.git/blob - src/Row.cpp
Get rid of regex_constants::match_partial
[lyx.git] / src / Row.cpp
1 /**
2  * \file Row.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author John Levon
8  * \author André Pönitz
9  * \author Jürgen Vigna
10  * \author Jean-Marc Lasgouttes
11  *
12  * Full author contact details are available in file CREDITS.
13  *
14  * Metrics for an on-screen text row.
15  */
16
17 #include <config.h>
18
19 #include "Row.h"
20
21 #include "DocIterator.h"
22
23 #include "frontends/FontMetrics.h"
24
25 #include "support/debug.h"
26 #include "support/lassert.h"
27
28 #include <algorithm>
29 #include <ostream>
30
31 #include <boost/next_prior.hpp>
32
33 using namespace std;
34
35 namespace lyx {
36
37 using frontend::FontMetrics;
38
39 double Row::Element::pos2x(pos_type const i) const
40 {
41         // This can happen with inline completion when clicking on the
42         // row after the completion.
43         if (i < pos || i > endpos)
44                 return 0;
45
46         bool const rtl = font.isVisibleRightToLeft();
47
48         double w = 0;
49         //handle first the two bounds of the element
50         if (i == endpos && !(inset && inset->lyxCode() == SEPARATOR_CODE))
51                 w = rtl ? 0 : full_width();
52         else if (i == pos || type != STRING)
53                 w = rtl ? full_width() : 0;
54         else {
55                 FontMetrics const & fm = theFontMetrics(font);
56                 w = fm.pos2x(str, i - pos, font.isVisibleRightToLeft());
57         }
58
59         return w;
60 }
61
62
63 pos_type Row::Element::x2pos(int &x) const
64 {
65         //lyxerr << "x2pos: x=" << x << " w=" << width() << " " << *this;
66         bool const rtl = font.isVisibleRightToLeft();
67         size_t i = 0;
68
69         switch (type) {
70         case STRING: {
71                 FontMetrics const & fm = theFontMetrics(font);
72                 i = fm.x2pos(str, x, rtl);
73                 break;
74         }
75         case VIRTUAL:
76                 // those elements are actually empty (but they have a width)
77                 i = 0;
78                 x = rtl ? int(full_width()) : 0;
79                 break;
80         case SEPARATOR:
81         case INSET:
82         case SPACE:
83                 // those elements contain only one position. Round to
84                 // the closest side.
85                 if (x > full_width()) {
86                         x = int(full_width());
87                         i = !rtl;
88                 } else {
89                         x = 0;
90                         i = rtl;
91                 }
92
93         }
94         //lyxerr << "=> p=" << pos + i << " x=" << x << endl;
95         return pos + i;
96
97 }
98
99
100 bool Row::Element::breakAt(int w)
101 {
102         if (type != STRING || dim.wid <= w)
103                 return false;
104
105         bool const rtl = font.isVisibleRightToLeft();
106         if (rtl)
107                 w = dim.wid - w;
108         pos_type new_pos = x2pos(w);
109         if (new_pos == pos)
110                 return false;
111         str = str.substr(0, new_pos - pos);
112         if (rtl)
113                 dim.wid -= w;
114         else
115                 dim.wid = w;
116         endpos = new_pos;
117         return true;
118 }
119
120
121 pos_type Row::Element::left_pos() const
122 {
123         return font.isVisibleRightToLeft() ? endpos : pos;
124 }
125
126
127 pos_type Row::Element::right_pos() const
128 {
129         return font.isVisibleRightToLeft() ? pos : endpos;
130 }
131
132
133 Row::Row()
134         : separator(0), label_hfill(0), left_margin(0), right_margin(0),
135           sel_beg(-1), sel_end(-1),
136           begin_margin_sel(false), end_margin_sel(false),
137           changed_(false), crc_(0), pos_(0), end_(0), right_boundary_(false)
138 {}
139
140
141 void Row::setCrc(size_type crc) const
142 {
143         changed_ = crc != crc_;
144         crc_ = crc;
145 }
146
147
148 bool Row::isMarginSelected(bool left_margin, DocIterator const & beg,
149                 DocIterator const & end) const
150 {
151         pos_type const sel_pos = left_margin ? sel_beg : sel_end;
152         pos_type const margin_pos = left_margin ? pos_ : end_;
153
154         // Is the chosen margin selected ?
155         if (sel_pos == margin_pos) {
156                 if (beg.pos() == end.pos())
157                         // This is a special case in which the space between after
158                         // pos i-1 and before pos i is selected, i.e. the margins
159                         // (see DocIterator::boundary_).
160                         return beg.boundary() && !end.boundary();
161                 else if (end.pos() == margin_pos)
162                         // If the selection ends around the margin, it is only
163                         // drawn if the cursor is after the margin.
164                         return !end.boundary();
165                 else if (beg.pos() == margin_pos)
166                         // If the selection begins around the margin, it is
167                         // only drawn if the cursor is before the margin.
168                         return beg.boundary();
169                 else
170                         return true;
171         }
172         return false;
173 }
174
175
176 void Row::setSelectionAndMargins(DocIterator const & beg,
177                 DocIterator const & end) const
178 {
179         setSelection(beg.pos(), end.pos());
180
181         if (selection()) {
182                 end_margin_sel = isMarginSelected(false, beg, end);
183                 begin_margin_sel = isMarginSelected(true, beg, end);
184         }
185 }
186
187
188 void Row::setSelection(pos_type beg, pos_type end) const
189 {
190         if (pos_ >= beg && pos_ <= end)
191                 sel_beg = pos_;
192         else if (beg > pos_ && beg <= end_)
193                 sel_beg = beg;
194         else
195                 sel_beg = -1;
196
197         if (end_ >= beg && end_ <= end)
198                 sel_end = end_;
199         else if (end < end_ && end >= pos_)
200                 sel_end = end;
201         else
202                 sel_end = -1;
203 }
204
205
206 bool Row::selection() const
207 {
208         return sel_beg != -1 && sel_end != -1;
209 }
210
211
212 ostream & operator<<(ostream & os, Row::Element const & e)
213 {
214         if (e.font.isVisibleRightToLeft())
215                 os << e.endpos << "<<" << e.pos << " ";
216         else
217                 os << e.pos << ">>" << e.endpos << " ";
218
219         switch (e.type) {
220         case Row::STRING:
221                 os << "STRING: `" << to_utf8(e.str) << "', ";
222                 break;
223         case Row::VIRTUAL:
224                 os << "VIRTUAL: `" << to_utf8(e.str) << "', ";
225                 break;
226         case Row::INSET:
227                 os << "INSET: " << to_utf8(e.inset->layoutName()) << ", ";
228                 break;
229         case Row::SEPARATOR:
230                 os << "SEPARATOR: extra=" << e.extra << ", ";
231                 break;
232         case Row::SPACE:
233                 os << "SPACE: ";
234                 break;
235         }
236         os << "width=" << e.full_width();
237         return os;
238 }
239
240
241 ostream & operator<<(ostream & os, Row const & row)
242 {
243         os << " pos: " << row.pos_ << " end: " << row.end_
244            << " left_margin: " << row.left_margin
245            << " width: " << row.dim_.wid
246            << " right_margin: " << row.right_margin
247            << " ascent: " << row.dim_.asc
248            << " descent: " << row.dim_.des
249            << " separator: " << row.separator
250            << " label_hfill: " << row.label_hfill 
251            << " row_boundary: " << row.right_boundary() << "\n";
252         double x = row.left_margin;
253         Row::Elements::const_iterator it = row.elements_.begin();
254         for ( ; it != row.elements_.end() ; ++it) {
255                 os << "x=" << x << " => " << *it << endl;
256                 x += it->full_width();
257         }
258         return os;
259 }
260
261
262 bool Row::sameString(Font const & f, Change const & ch) const
263 {
264         if (elements_.empty())
265                 return false;
266         Element const & elt = elements_.back();
267         return elt.type == STRING && !elt.final
268                    && elt.font == f && elt.change == ch;
269 }
270
271
272 void Row::finalizeLast()
273 {
274         if (elements_.empty())
275                 return;
276         Element & elt = elements_.back();
277         if (elt.final)
278                 return;
279         elt.final = true;
280
281         if (elt.type == STRING) {
282                 elt.dim.wid = theFontMetrics(elt.font).width(elt.str);
283                 dim_.wid += elt.dim.wid;
284         }
285 }
286
287
288 void Row::add(pos_type const pos, Inset const * ins, Dimension const & dim,
289               Font const & f, Change const & ch)
290 {
291         finalizeLast();
292         Element e(INSET, pos, f, ch);
293         e.inset = ins;
294         e.dim = dim;
295         elements_.push_back(e);
296         dim_.wid += dim.wid;
297 }
298
299
300 void Row::add(pos_type const pos, char_type const c,
301               Font const & f, Change const & ch)
302 {
303         if (!sameString(f, ch)) {
304                 finalizeLast();
305                 Element e(STRING, pos, f, ch);
306                 elements_.push_back(e);
307         }
308         back().str += c;
309         back().endpos = pos + 1;
310 }
311
312
313 void Row::addVirtual(pos_type const pos, docstring const & s,
314                      Font const & f, Change const & ch)
315 {
316         finalizeLast();
317         Element e(VIRTUAL, pos, f, ch);
318         e.str = s;
319         e.dim.wid = theFontMetrics(f).width(s);
320         dim_.wid += e.dim.wid;
321         e.endpos = pos;
322         elements_.push_back(e);
323         finalizeLast();
324 }
325
326
327 void Row::addSeparator(pos_type const pos, char_type const c,
328                        Font const & f, Change const & ch)
329 {
330         finalizeLast();
331         Element e(SEPARATOR, pos, f, ch);
332         e.str += c;
333         e.dim.wid = theFontMetrics(f).width(c);
334         elements_.push_back(e);
335         dim_.wid += e.dim.wid;
336 }
337
338
339 void Row::addSpace(pos_type const pos, int const width,
340                    Font const & f, Change const & ch)
341 {
342         finalizeLast();
343         Element e(SPACE, pos, f, ch);
344         e.dim.wid = width;
345         elements_.push_back(e);
346         dim_.wid += e.dim.wid;
347 }
348
349
350 void Row::pop_back()
351 {
352         dim_.wid -= elements_.back().dim.wid;
353         elements_.pop_back();
354 }
355
356
357 void Row::shortenIfNeeded(pos_type const keep, int const w)
358 {
359         if (empty() || width() <= w)
360                 return;
361
362         Elements::iterator const beg = elements_.begin();
363         Elements::iterator const end = elements_.end();
364         Elements::iterator last_sep = elements_.end();
365         int last_width = 0;
366         int wid = left_margin;
367
368         Elements::iterator cit = beg;
369         for ( ; cit != end ; ++cit) {
370                 if (cit->type == SEPARATOR && cit->pos >= keep) {
371                         last_sep = cit;
372                         last_width = wid;
373                 }
374                 if (wid + cit->dim.wid > w)
375                         break;
376                 wid += cit->dim.wid;
377         }
378
379         if (last_sep != end) {
380                 // We have found a suitable separator. This is the
381                 // common case.
382                 end_ = last_sep->endpos;
383                 dim_.wid = last_width;
384                 elements_.erase(last_sep, end);
385                 return;
386         }
387
388         if (cit == end) {
389                 // This should not happen since the row is too long.
390                 LYXERR0("Something is wrong cannot shorten row: " << *this);
391                 return;
392         }
393
394         if (cit != beg && cit->type == VIRTUAL) {
395                 // It is not possible to separate a virtual element from the
396                 // previous one.
397                 --cit;
398                 wid -= cit->dim.wid;
399         }
400
401         if (cit != beg) {
402                 // There is no separator, but several elements (probably
403                 // insets) have been added. We can cut at this place.
404                 end_ = cit->pos;
405                 dim_.wid = wid;
406                 elements_.erase(cit, end);
407                 return;
408         }
409
410         /* If we are here, it means that we have not found a separator
411          * to shorten the row. There is one case where we can do
412          * something: when we have one big string, maybe with some
413          * other things after it.
414          */
415         if (cit->breakAt(w - left_margin)) {
416                 end_ = cit->endpos;
417                 dim_.wid = left_margin + cit->dim.wid;
418                 // If there are other elements, they should be removed.
419                 elements_.erase(boost::next(cit), end);
420         }
421 }
422
423
424 void Row::reverseRTL(bool const rtl_par)
425 {
426         pos_type i = 0;
427         pos_type const end = elements_.size();
428         while (i < end) {
429                 // gather a sequence of elements with the same direction
430                 bool const rtl = elements_[i].font.isVisibleRightToLeft();
431                 pos_type j = i;
432                 while (j < end && elements_[j].font.isVisibleRightToLeft() == rtl)
433                         ++j;
434                 // if the direction is not the same as the paragraph
435                 // direction, the sequence has to be reverted.
436                 if (rtl != rtl_par)
437                         reverse(elements_.begin() + i, elements_.begin() + j);
438                 i = j;
439         }
440         // If the paragraph itself is RTL, reverse everything
441         if (rtl_par)
442                 reverse(elements_.begin(), elements_.end());
443 }
444
445 } // namespace lyx