]> git.lyx.org Git - lyx.git/blob - src/Row.cpp
ce1a440cc68f370f500d9f0d0d66877696124165
[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 unknown
7  * \author Lars Gullik Bjønnes
8  * \author John Levon
9  * \author André Pönitz
10  * \author Jürgen Vigna
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
27 #include <ostream>
28
29 using namespace std;
30
31 namespace lyx {
32
33
34 Row::Row()
35         : separator(0), label_hfill(0), x(0),
36         sel_beg(-1), sel_end(-1),
37         begin_margin_sel(false), end_margin_sel(false),
38         changed_(false), crc_(0), pos_(0), end_(0)
39 {}
40
41
42 void Row::setCrc(size_type crc) const
43 {
44         changed_ = crc != crc_;
45         crc_ = crc;
46 }
47
48
49 void Row::pos(pos_type p)
50 {
51         pos_ = p;
52 }
53
54
55 void Row::endpos(pos_type p)
56 {
57         end_ = p;
58 }
59
60
61 bool Row::isMarginSelected(bool left_margin, DocIterator const & beg,
62                 DocIterator const & end) const
63 {
64         pos_type const sel_pos = left_margin ? sel_beg : sel_end;
65         pos_type const margin_pos = left_margin ? pos_ : end_;
66
67         // Is the chosen margin selected ?
68         if (sel_pos == margin_pos) {
69                 if (beg.pos() == end.pos())
70                         // This is a special case in which the space between after
71                         // pos i-1 and before pos i is selected, i.e. the margins
72                         // (see DocIterator::boundary_).
73                         return beg.boundary() && !end.boundary();
74                 else if (end.pos() == margin_pos)
75                         // If the selection ends around the margin, it is only
76                         // drawn if the cursor is after the margin.
77                         return !end.boundary();
78                 else if (beg.pos() == margin_pos)
79                         // If the selection begins around the margin, it is
80                         // only drawn if the cursor is before the margin.
81                         return beg.boundary();
82                 else
83                         return true;
84         }
85         return false;
86 }
87
88
89 void Row::setSelectionAndMargins(DocIterator const & beg,
90                 DocIterator const & end) const
91 {
92         setSelection(beg.pos(), end.pos());
93
94         if (selection()) {
95                 end_margin_sel = isMarginSelected(false, beg, end);
96                 begin_margin_sel = isMarginSelected(true, beg, end);
97         }
98 }
99
100
101 void Row::setSelection(pos_type beg, pos_type end) const
102 {
103         if (pos_ >= beg && pos_ <= end)
104                 sel_beg = pos_;
105         else if (beg > pos_ && beg <= end_)
106                 sel_beg = beg;
107         else
108                 sel_beg = -1;
109
110         if (end_ >= beg && end_ <= end)
111                 sel_end = end_;
112         else if (end < end_ && end >= pos_)
113                 sel_end = end;
114         else
115                 sel_end = -1;
116 }
117
118
119 bool Row::selection() const
120 {
121         return sel_beg != -1 && sel_end != -1;
122 }
123
124 ostream & operator<<(ostream & os, Row const & row)
125 {
126         os << " pos: " << row.pos_ << " end: " << row.end_
127            << " width: " << row.dim_.wid
128            << " ascent: " << row.dim_.asc
129            << " descent: " << row.dim_.des << "\n";
130         Row::Elements::const_iterator it = row.elements_.begin();
131         for ( ; it != row.elements_.end() ; ++it) {
132                 switch (it->type) {
133                 case Row::Element::STRING:
134                         os << "**STRING: " << to_utf8(it->str) << endl;
135                         break;
136                 case Row::Element::INSET:
137                         os << "**INSET: " << to_utf8(it->inset->layoutName()) << endl;
138                         break;
139                 case Row::Element::SEPARATOR:
140                         os << "**SEPARATOR: " << endl;
141                         break;
142                 case Row::Element::SPACE:
143                         os << "**SPACE: " << it->dim.wid << endl;
144                         break;
145                 }
146         }
147         return os;
148 }
149
150
151 bool Row::sameString(Font const & f, Change const & ch) const
152 {
153         if (elements_.empty())
154                 return false;
155         Element const & elt = elements_.back();
156         return elt.type == Element::STRING && !elt.final
157                    && elt.font == f && elt.change == ch;
158 }
159
160
161 void Row::finalizeLast()
162 {
163         if (elements_.empty())
164                 return;
165         Element & elt = elements_.back();
166         if (elt.final)
167                 return;
168         elt.final = true;
169
170         if (elt.type == Element::STRING) {
171                 elt.dim.wid = theFontMetrics(elt.font).width(elt.str);
172                 dim_.wid += elt.dim.wid;
173         }
174 }
175
176
177 void Row::add(pos_type const pos, Inset const * ins, Dimension const & dim,
178               Font const & f, Change const & ch)
179 {
180         finalizeLast();
181         Element e(Element::INSET, pos, f, ch);
182         e.inset = ins;
183         e.dim = dim;
184         elements_.push_back(e);
185         dim_.wid += dim.wid;
186 }
187
188
189 void Row::add(pos_type const pos, char_type const c,
190               Font const & f, Change const & ch)
191 {
192         if (!sameString(f, ch)) {
193                 finalizeLast();
194                 Element e(Element::STRING, pos, f, ch);
195                 elements_.push_back(e);
196         }
197         //lyxerr << "FONT " <<back().font.language() << endl;
198         back().str += c;
199         back().endpos = pos + 1;
200 }
201
202
203 void Row::add(pos_type const pos, docstring const & s,
204               Font const & f, Change const & ch)
205 {
206         if (!sameString(f, ch)) {
207                 finalizeLast();
208                 Element e(Element::STRING, pos, f, ch);
209                 elements_.push_back(e);
210         }
211         back().str += s;
212         back().endpos = pos + 1;
213 }
214
215
216 void Row::addSeparator(pos_type const pos, char_type const c,
217                        Font const & f, Change const & ch)
218 {
219         finalizeLast();
220         Element e(Element::SEPARATOR, pos, f, ch);
221         e.str += c;
222         e.dim.wid = theFontMetrics(f).width(c);
223         elements_.push_back(e);
224         dim_.wid += e.dim.wid;
225 }
226
227
228 void Row::addSpace(pos_type const pos, int const width,
229                    Font const & f, Change const & ch)
230 {
231         finalizeLast();
232         Element e(Element::SEPARATOR, pos, f, ch);
233         e.dim.wid = width;
234         elements_.push_back(e);
235         dim_.wid += e.dim.wid;
236 }
237
238
239 void Row::pop_back()
240 {
241         dim_.wid -= elements_.back().dim.wid;
242         elements_.pop_back();
243 }
244
245
246 void Row::separate_back(pos_type const keep)
247 {
248         if (empty())
249                 return;
250         int i = elements_.size();
251         int new_end = end_;
252         int new_wid = dim_.wid;
253         if (i > 0 && elements_[i - 1].isLineSeparator() && new_end > keep) {
254                 --i;
255                 new_end = elements_[i].pos;
256                 new_wid -= elements_[i].dim.wid;
257         }
258
259         while (i > 0 && !elements_[i - 1].isLineSeparator() && new_end > keep) {
260                 --i;
261                 new_end = elements_[i].pos;
262                 new_wid -= elements_[i].dim.wid;
263         }
264         if (i == 0)
265                 return;
266         end_ = new_end;
267         dim_.wid = new_wid;
268         elements_.erase(elements_.begin() + i, elements_.end());
269 }
270
271
272 void Row::reverseRtL()
273 {
274         pos_type i = 0;
275         pos_type const end = elements_.size();
276         while (i < end) {
277                 // skip LtR elements
278                 while (!elements_[i].font.isRightToLeft() && i < end)
279                         ++i;
280                 if (i >= end)
281                         break;
282
283                 // look for a RtL sequence
284                 pos_type j = i;
285                 while (elements_[j].font.isRightToLeft() && j < end)
286                         ++j;
287                 reverse(elements_.begin() + i, elements_.begin() + j);
288                 i = j;
289         }
290 }
291
292 } // namespace lyx