1 // -*- C++ -*-
2 /**
3  * \file DocIterator.h
4  * This file is part of LyX, the document processor.
5  * Licence details can be found in the file COPYING.
6  *
7  * \author André Pönitz
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11 
12 #ifndef DOCITERATOR_H
13 #define DOCITERATOR_H
14 
15 #include "CursorSlice.h"
16 
17 #include <vector>
18 #include <algorithm>
19 
20 namespace lyx {
21 
22 class DocIterator;
23 class Encoding;
24 class LyXErr;
25 class MathAtom;
26 class Paragraph;
27 class Text;
28 class InsetIterator;
29 class FontSpan;
30 
31 DocIterator doc_iterator_begin(Buffer const * buf, Inset const * inset = 0);
32 DocIterator doc_iterator_end(Buffer const * buf, Inset const * inset = 0);
33 
34 
35 class DocIterator
36 {
37 public:
38 	/// type for cell number in inset
39 	typedef CursorSlice::idx_type idx_type;
40 	/// type for row indices
41 	typedef CursorSlice::row_type row_type;
42 	/// type for col indices
43 	typedef CursorSlice::col_type col_type;
44 
45 public:
46 	///
47 	DocIterator();
48 	///
49 	explicit DocIterator(Buffer *buf);
50 
51 	/// access to owning buffer
buffer()52 	Buffer * buffer() const { return buffer_; }
53 	/// access to owning buffer
setBuffer(Buffer * buf)54 	void setBuffer(Buffer * buf) { buffer_ = buf; }
55 
56 	/// Clone this for given \p buffer.
57 	/// \p buffer must be a clone of buffer_.
58 	DocIterator clone(Buffer * buffer) const;
59 
60 	/// access slice at position \p i
61 	CursorSlice const & operator[](size_t i) const { return slices_[i]; }
62 	/// access slice at position \p i
63 	CursorSlice & operator[](size_t i) { return slices_[i]; }
64 	/// chop a few slices from the iterator
resize(size_t i)65 	void resize(size_t i) { slices_.resize(i); }
66 
67 	/// is the iterator valid?
68 	explicit operator bool() const { return !empty(); }
69 
70 	/// does this iterator have any content?
empty()71 	bool empty() const { return slices_.empty(); }
72 	/// is this the end position?
atEnd()73 	bool atEnd() const { return slices_.empty(); }
74 	/// is this the last possible position?
atLastPos()75 	bool atLastPos() const { return pit() == lastpit() && pos() == lastpos(); }
76 
77 	/// checks the cursor slices for disabled spell checker insets
78 	bool allowSpellCheck() const;
79 
80 	//
81 	// access to slice at tip
82 	//
83 	/// access to tip
top()84 	CursorSlice & top() { return slices_.back(); }
85 	/// access to tip
top()86 	CursorSlice const & top() const { return slices_.back(); }
87 	/// access to outermost slice
bottom()88 	CursorSlice & bottom() { return slices_.front(); }
89 	/// access to outermost slice
bottom()90 	CursorSlice const & bottom() const { return slices_.front(); }
91 	/// how many nested insets do we have?
depth()92 	size_t depth() const { return slices_.size(); }
93 	/// the containing inset
inset()94 	Inset & inset() const { return top().inset(); }
95 	/// return the cell of the inset this cursor is in
idx()96 	idx_type idx() const { return top().idx(); }
97 	/// return the cell of the inset this cursor is in
idx()98 	idx_type & idx() { return top().idx(); }
99 	/// return the last possible cell in this inset
100 	idx_type lastidx() const;
101 	/// return the paragraph this cursor is in
pit()102 	pit_type pit() const { return top().pit(); }
103 	/// return the paragraph this cursor is in
pit()104 	pit_type & pit() { return top().pit(); }
105 	/// return the last possible paragraph in this inset
106 	pit_type lastpit() const;
107 	/// return the position within the paragraph
pos()108 	pos_type pos() const { return top().pos(); }
109 	/// return the position within the paragraph
pos()110 	pos_type & pos() { return top().pos(); }
111 	/// return the last position within the paragraph
112 	pos_type lastpos() const;
113 
114 	/// return the number of embedded cells
115 	size_t nargs() const;
116 	/// return the number of embedded cells
117 	size_t ncols() const;
118 	/// return the number of embedded cells
119 	size_t nrows() const;
120 	/// return the grid row of the top cell
121 	row_type row() const;
122 	/// return the last row of the top grid
lastrow()123 	row_type lastrow() const { return nrows() - 1; }
124 	/// return the grid column of the top cell
125 	col_type col() const;
126 	/// return the last column of the top grid
lastcol()127 	col_type lastcol() const { return ncols() - 1; }
128 	/// the inset just behind the cursor
129 	Inset * nextInset() const;
130 	/// the inset just in front of the cursor
131 	Inset * prevInset() const;
132 	///
boundary()133 	bool boundary() const { return boundary_; }
134 	///
boundary(bool b)135 	void boundary(bool b) { boundary_ = b; }
136 
137 	// the two methods below have been inlined out because of
138 	// profiling results under linux when opening a document.
139 	/// are we in mathed?.
inMathed()140 	bool inMathed() const
141 	{ return !empty() && inset().inMathed(); }
142 	/// are we in texted?.
inTexted()143 	bool inTexted() const
144 	{ return !empty() && !inset().inMathed(); }
145 	/// are we in regexp-mode ?
146 	bool inRegexped() const;
147 
148 	//
149 	// math-specific part
150 	//
151 	/// return the mathed cell this cursor is in
152 	MathData & cell() const;
153 	/// the mathatom left of the cursor
154 	MathAtom & prevAtom() const;
155 	/// the mathatom right of the cursor
156 	MathAtom & nextAtom() const;
157 
158 	// text-specific part
159 	//
160 	/// the paragraph we're in in text mode.
161 	/// \warning only works within text!
162 	Paragraph & paragraph() const;
163 	/// the paragraph we're in in any case.
164 	/// This method will give the containing paragraph even
165 	/// if not in text mode (ex: in mathed).
166 	Paragraph & innerParagraph() const;
167 	/// return the inner text slice.
168 	CursorSlice const & innerTextSlice() const;
169 	// convert a DocIterator into an argument to LFUN_PARAGRAPH_GOTO
170 	docstring paragraphGotoArgument() const;
171 	/// returns a DocIterator for the containing text inset
172 	DocIterator getInnerText() const;
173 	/// the first and last positions of a word at top cursor slice
174 	/// \warning only works within text!
175 	FontSpan locateWord(word_location const loc) const;
176 	///
177 	Text * text() const;
178 	/// the containing inset or the cell, respectively
179 	Inset * realInset() const;
180 	///
181 	Inset * innerInsetOfType(int code) const;
182 	///
183 	Text * innerText() const;
184 
185 	//
186 	// elementary moving
187 	//
188 	/// move one step backwards
189 	bool posBackward();
190 	/// move one step forward
191 	bool posForward();
192 	/**
193 	 * move on one logical position, descend into nested insets
194 	 * including collapsed insets
195 	 */
196 	void forwardPos();
197 	/**
198 	 * move on one logical position, descend into nested insets
199 	 * skip collapsed insets
200 	 */
201 	void forwardPosIgnoreCollapsed();
202 	/// move on one physical character or inset
203 	void forwardChar();
204 	/// move on one paragraph
205 	void forwardPar();
206 	/// move on one inset
207 	void forwardInset();
208 	/// move backward one logical position
209 	void backwardPos();
210 	/// move backward one physical character or inset
211 	void backwardChar();
212 	/// move backward one paragraph
213 	void backwardPar();
214 	/// move backward one inset
215 	/// not used currently, uncomment if you need it
216 	//void backwardInset();
217 
218 	/// are we some 'extension' (i.e. deeper nested) of the given iterator
219 	bool hasPart(DocIterator const & it) const;
220 
221 	/// output
222 	friend std::ostream &
223 	operator<<(std::ostream & os, DocIterator const & cur);
224 	friend LyXErr & operator<<(LyXErr & os, DocIterator const & it);
225 	///
226 	friend bool operator==(DocIterator const &, DocIterator const &);
227 	friend bool operator<(DocIterator const &, DocIterator const &);
228 	friend bool operator>(DocIterator const &, DocIterator const &);
229 	friend bool operator<=(DocIterator const &, DocIterator const &);
230 	///
231 	friend class StableDocIterator;
232 //protected:
233 	///
clear()234 	void clear() { slices_.clear(); }
235 	///
push_back(CursorSlice const & sl)236 	void push_back(CursorSlice const & sl) { slices_.push_back(sl); }
237 	///
pop_back()238 	void pop_back() { slices_.pop_back(); }
239 	/// recompute the inset parts of the cursor from the document data
240 	void updateInsets(Inset * inset);
241 	/// fix DocIterator in circumstances that should never happen.
242 	/// \return true if the DocIterator was fixed.
243 	bool fixIfBroken();
244 	/// Repopulate the slices insets from bottom to top. Useful
245 	/// for stable iterators or Undo data.
246 	void sanitize();
247 
248 	/// find index of CursorSlice with &cell() == &cell (or -1 if not found)
249 	int find(MathData const & cell) const;
250 	/// find index of CursorSlice with inset() == inset (or -1 of not found)
251 	int find(Inset const * inset) const;
252 	/// cut off CursorSlices with index > above and store cut off slices in cut.
253 	void cutOff(int above, std::vector<CursorSlice> & cut);
254 	/// cut off CursorSlices with index > above
255 	void cutOff(int above);
256 	/// push CursorSlices on top
257 	void append(std::vector<CursorSlice> const & x);
258 	/// push one CursorSlice on top and set its index and position
259 	void append(idx_type idx, pos_type pos);
260 
261 	///
262 	Encoding const * getEncoding() const;
263 
264 private:
265 	friend class InsetIterator;
266 	friend DocIterator doc_iterator_begin(Buffer const * buf, Inset const * inset);
267 	friend DocIterator doc_iterator_end(Buffer const * buf, Inset const * inset);
268 	///
269 	explicit DocIterator(Buffer * buf, Inset * inset);
270 	/**
271 	 * Normally, when the cursor is at position i, it is painted *before*
272 	 * the character at position i. However, what if we want the cursor
273 	 * painted *after* position i? That's what boundary_ is for: if
274 	 * boundary_==true, the cursor is painted *after* position i-1, instead
275 	 * of before position i.
276 	 *
277 	 * Note 1: Usually, after i-1 or before i are actually the same place!
278 	 * However, this is not the case when i-1 and i are not painted
279 	 * contiguously, and in these cases we sometimes do want to have control
280 	 * over whether to paint before i or after i-1.
281 	 * Some concrete examples of where this happens:
282 	 * a. i-1 at the end of one row, i at the beginning of next row
283 	 * b. in bidi text, at transitions between RTL and LTR or vice versa
284 	 *
285 	 * Note 2: Why i and i-1? Why, if boundary_==false means: *before* i,
286 	 * couldn't boundary_==true mean: *after* i?
287 	 * Well, the reason is this: cursor position is not used only for
288 	 * painting the cursor, but it also affects other things, for example:
289 	 * where the next insertion will be placed (it is inserted at the current
290 	 * position, pushing anything at the current position and beyond forward).
291 	 * Now, when the current position is i and boundary_==true, insertion would
292 	 * happen *before* i. If the cursor, however, were painted *after* i, that
293 	 * would be very unnatural...
294 	 */
295 	bool boundary_;
296 	///
internalData()297 	std::vector<CursorSlice> const & internalData() const { return slices_; }
298 	///
299 	std::vector<CursorSlice> slices_;
300 	///
301 	Inset * inset_;
302 	///
303 	Buffer * buffer_;
304 };
305 
306 
307 inline bool operator==(DocIterator const & di1, DocIterator const & di2)
308 {
309 	return di1.slices_ == di2.slices_;
310 }
311 
312 
313 inline bool operator!=(DocIterator const & di1, DocIterator const & di2)
314 {
315 	return !(di1 == di2);
316 }
317 
318 
319 inline
320 bool operator<(DocIterator const & p, DocIterator const & q)
321 {
322 	size_t depth = std::min(p.depth(), q.depth());
323 	for (size_t i = 0 ; i < depth ; ++i) {
324 		if (p[i] != q[i])
325 			return p[i] < q[i];
326 	}
327 	return p.depth() < q.depth();
328 }
329 
330 
331 inline
332 bool operator>(DocIterator const & p, DocIterator const & q)
333 {
334 	return q < p;
335 }
336 
337 
338 inline
339 bool operator<=(DocIterator const & p, DocIterator const & q)
340 {
341 	return !(q < p);
342 }
343 
344 
345 inline
346 bool operator>=(DocIterator const & p, DocIterator const & q)
347 {
348 	return !(p < q);
349 }
350 
351 
352 // The difference to a ('non stable') DocIterator is the removed
353 // (overwritten by 0...) part of the CursorSlice data items. So this thing
354 // is suitable for external storage, but not for iteration as such.
355 
356 class StableDocIterator
357 {
358 public:
359 	///
StableDocIterator()360 	StableDocIterator() {}
361 	/// non-explicit intended
362 	StableDocIterator(const DocIterator & it);
363 	///
364 	DocIterator asDocIterator(Buffer * buf) const;
365 	///
size()366 	size_t size() const { return data_.size(); }
367 	///  return the position within the paragraph
pos()368 	pos_type pos() const { return data_.back().pos(); }
369 	///  return the position within the paragraph
pos()370 	pos_type & pos() { return data_.back().pos(); }
371 	///
372 	friend std::ostream &
373 	operator<<(std::ostream & os, StableDocIterator const & cur);
374 	///
375 	friend std::istream &
376 	operator>>(std::istream & is, StableDocIterator & cur);
377 	///
378 	friend bool
379 	operator==(StableDocIterator const &, StableDocIterator const &);
380 private:
381 	std::vector<CursorSlice> data_;
382 };
383 
384 } // namespace lyx
385 
386 #endif // DOCITERATOR_H
387