]> git.lyx.org Git - lyx.git/blob - src/iterators.C
add lockPath and a few helpers
[lyx.git] / src / iterators.C
1 /* \file iterators.C
2  * This file is part of LyX, the document processor.
3  * Licence details can be found in the file COPYING.
4  *
5  * \author unknown
6  * \author Lars Gullik Bjønnes
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11
12 #include <config.h>
13
14 #include "iterators.h"
15 #include "paragraph.h"
16 #include "PosIterator.h"
17 #include "cursor.h"
18 #include "BufferView.h"
19 #include "funcrequest.h"
20 #include "dispatchresult.h"
21
22
23
24 #include "insets/inset.h"
25 #include "insets/updatableinset.h"
26 #include "insets/insettext.h"
27
28 #include <boost/next_prior.hpp>
29 #include <boost/optional.hpp>
30
31 using boost::next;
32 using boost::optional;
33 using std::vector;
34
35 ///
36 /// ParPosition
37 ///
38
39 class ParPosition {
40 public:
41         ///
42         ParPosition(ParagraphList::iterator p, ParagraphList const & pl);
43         ///
44         ParagraphList::iterator pit;
45         ///
46         ParagraphList const * plist;
47         ///
48         optional<InsetList::iterator> it;
49         ///
50         optional<int> index;
51 };
52
53
54 ParPosition::ParPosition(ParagraphList::iterator p, ParagraphList const & pl)
55         : pit(p), plist(&pl)
56 {
57         if (p != const_cast<ParagraphList&>(pl).end()) {
58                 it.reset(p->insetlist.begin());
59         }
60 }
61
62
63 bool operator==(ParPosition const & pos1, ParPosition const & pos2)
64 {
65         return pos1.pit == pos2.pit;
66 }
67
68
69 bool operator!=(ParPosition const & pos1, ParPosition const & pos2)
70 {
71         return !(pos1 == pos2);
72 }
73
74
75 ///
76 /// ParIterator
77 ///
78
79 struct ParIterator::Pimpl {
80         typedef vector<ParPosition> PosHolder;
81         PosHolder positions;
82 };
83
84 ParIterator::ParIterator(ParagraphList::iterator pit, ParagraphList const & pl)
85         : pimpl_(new Pimpl)
86 {
87         pimpl_->positions.push_back(ParPosition(pit, pl));
88 }
89
90
91 ParIterator::~ParIterator()
92 {}
93
94
95 ParIterator::ParIterator(ParIterator const & pi)
96         : pimpl_(new Pimpl(*pi.pimpl_))
97 {}
98
99
100 void ParIterator::operator=(ParIterator const & pi)
101 {
102         ParIterator tmp(pi);
103         pimpl_.swap(tmp.pimpl_);
104 }
105
106
107 ParIterator & ParIterator::operator++()
108 {
109         while (!pimpl_->positions.empty()) {
110                 ParPosition & p = pimpl_->positions.back();
111
112                 // Does the current inset contain more "cells" ?
113                 if (p.index) {
114                         ++(*p.index);
115                         ParagraphList * plist = (*p.it)->inset->getParagraphs(*p.index);
116                         if (plist && !plist->empty()) {
117                                 pimpl_->positions.push_back(ParPosition(plist->begin(), *plist));
118                                 return *this;
119                         }
120                         ++(*p.it);
121                 } else
122                         // The following line is needed because the value of
123                         // p.it may be invalid if inset was added/removed to
124                         // the paragraph pointed by the iterator
125                         p.it.reset(p.pit->insetlist.begin());
126
127                 // Try to find the next inset that contains paragraphs
128                 InsetList::iterator end = p.pit->insetlist.end();
129                 for (; *p.it != end; ++(*p.it)) {
130                         ParagraphList * plist = (*p.it)->inset->getParagraphs(0);
131                         if (plist && !plist->empty()) {
132                                 p.index.reset(0);
133                                 pimpl_->positions.push_back(ParPosition(plist->begin(), *plist));
134                                 return *this;
135                         }
136                 }
137
138                 // Try to go to the next paragarph
139                 if (next(p.pit) != const_cast<ParagraphList*>(p.plist)->end()
140                     || pimpl_->positions.size() == 1) {
141                         ++p.pit;
142                         p.index.reset();
143                         p.it.reset();
144
145                         return *this;
146                 }
147
148                 // Drop end and move up in the stack.
149                 pimpl_->positions.pop_back();
150         }
151         return *this;
152 }
153
154
155 LyXText * ParIterator::text() const
156 {
157         //lyxerr << "positions.size: " << pimpl_->positions.size() << std::endl;
158         if (pimpl_->positions.size() <= 1)
159                 return 0;
160
161         ParPosition const & pos = pimpl_->positions[pimpl_->positions.size() - 2];
162         return (*pos.it)->inset->getText(*pos.index);
163 }
164
165
166 InsetOld * ParIterator::inset() const
167 {
168         //lyxerr << "positions.size: " << pimpl_->positions.size() << std::endl;
169         if (pimpl_->positions.size() <= 1)
170                 return 0;
171
172         ParPosition const & pos = pimpl_->positions[pimpl_->positions.size() - 2];
173         return (*pos.it)->inset;
174 }
175
176
177 int ParIterator::index() const
178 {
179         if (pimpl_->positions.size() <= 1)
180                 return 0;
181
182         return *(pimpl_->positions[pimpl_->positions.size() - 2].index);
183 }
184
185
186 void ParIterator::asCursor(Cursor & cursor) const
187 {
188         cursor.data_.clear();
189         for (size_t i = 1, n = size(); i < n; ++i) {
190                 ParPosition const & pos = pimpl_->positions[i - 1];
191                 CursorItem item;
192                 item.inset_ = (*pos.it)->inset;
193                 item.idx_   = (*pos.index);
194                 item.text_  = (*pos.it)->inset->getText(*pos.index);
195                 item.par_   = 0;
196                 item.pos_   = 0;
197                 cursor.data_.push_back(item);
198         }
199 }
200
201
202 Paragraph & ParIterator::operator*() const
203 {
204         return *pimpl_->positions.back().pit;
205 }
206
207
208 ParagraphList::iterator ParIterator::pit() const
209 {
210         return pimpl_->positions.back().pit;
211 }
212
213
214 ParagraphList::iterator ParIterator::operator->() const
215 {
216         return pimpl_->positions.back().pit;
217 }
218
219
220 ParagraphList::iterator ParIterator::outerPar() const
221 {
222         return pimpl_->positions[0].pit;
223 }
224
225
226 size_t ParIterator::size() const
227 {
228         return pimpl_->positions.size();
229 }
230
231
232 ParagraphList & ParIterator::plist() const
233 {
234         return *const_cast<ParagraphList*>(pimpl_->positions.back().plist);
235 }
236
237
238 bool operator==(ParIterator const & iter1, ParIterator const & iter2)
239 {
240         return iter1.pimpl_->positions == iter2.pimpl_->positions;
241 }
242
243
244 bool operator!=(ParIterator const & iter1, ParIterator const & iter2)
245 {
246         return !(iter1 == iter2);
247 }
248
249
250 ///
251 /// ParConstIterator
252 ///
253
254
255 struct ParConstIterator::Pimpl {
256         typedef vector<ParPosition> PosHolder;
257         PosHolder positions;
258 };
259
260
261 ParConstIterator::ParConstIterator(ParagraphList::iterator pit,
262                                    ParagraphList const & pl)
263         : pimpl_(new Pimpl)
264 {
265         pimpl_->positions.push_back(ParPosition(pit, pl));
266 }
267
268
269 ParConstIterator::~ParConstIterator()
270 {}
271
272
273 ParConstIterator::ParConstIterator(ParConstIterator const & pi)
274         : pimpl_(new Pimpl(*pi.pimpl_))
275 {}
276
277
278 ParConstIterator & ParConstIterator::operator++()
279 {
280         while (!pimpl_->positions.empty()) {
281                 ParPosition & p = pimpl_->positions.back();
282
283                 // Does the current inset contain more "cells" ?
284                 if (p.index) {
285                         ++(*p.index);
286                         ParagraphList * plist = (*p.it)->inset->getParagraphs(*p.index);
287                         if (plist && !plist->empty()) {
288                                 pimpl_->positions.push_back(ParPosition(plist->begin(), *plist));
289                                 return *this;
290                         }
291                         ++(*p.it);
292                 } else
293                         // The following line is needed because the value of
294                         // p.it may be invalid if inset was added/removed to
295                         // the paragraph pointed by the iterator
296                         p.it.reset(p.pit->insetlist.begin());
297
298                 // Try to find the next inset that contains paragraphs
299                 InsetList::iterator end = p.pit->insetlist.end();
300                 for (; *p.it != end; ++(*p.it)) {
301                         ParagraphList * plist = (*p.it)->inset->getParagraphs(0);
302                         if (plist && !plist->empty()) {
303                                 p.index.reset(0);
304                                 pimpl_->positions.push_back(ParPosition(plist->begin(), *plist));
305                                 return *this;
306                         }
307                 }
308
309                 // Try to go to the next paragarph
310                 if (next(p.pit) != const_cast<ParagraphList*>(p.plist)->end()
311                     || pimpl_->positions.size() == 1) {
312                         ++p.pit;
313                         p.index.reset();
314                         p.it.reset();
315
316                         return *this;
317                 }
318
319                 // Drop end and move up in the stack.
320                 pimpl_->positions.pop_back();
321         }
322
323         return *this;
324 }
325
326
327 Paragraph const & ParConstIterator::operator*() const
328 {
329         return *pimpl_->positions.back().pit;
330 }
331
332
333 ParagraphList::const_iterator ParConstIterator::pit() const
334 {
335         return pimpl_->positions.back().pit;
336 }
337
338
339 ParagraphList::const_iterator ParConstIterator::operator->() const
340 {
341         return pimpl_->positions.back().pit;
342 }
343
344
345 ParagraphList const & ParConstIterator::plist() const
346 {
347         return *pimpl_->positions.back().plist;
348 }
349
350
351 size_t ParConstIterator::size() const
352 {
353         return pimpl_->positions.size();
354 }
355
356
357 bool operator==(ParConstIterator const & iter1, ParConstIterator const & iter2)
358 {
359         return iter1.pimpl_->positions == iter2.pimpl_->positions;
360 }
361
362
363 bool operator!=(ParConstIterator const & iter1, ParConstIterator const & iter2)
364 {
365         return !(iter1 == iter2);
366 }
367
368
369 PosIterator ParIterator::asPosIterator(lyx::pos_type pos) const
370 {
371         PosIterator p;
372         
373         int const last = size() - 1;
374         for (int i = 0; i < last; ++i) {
375                 ParPosition & pp = pimpl_->positions[i];
376                 p.stack_.push(PosIteratorItem(const_cast<ParagraphList *>(pp.plist), pp.pit, (*pp.it)->pos, *pp.index + 1));
377         }
378         ParPosition const & pp = pimpl_->positions[last];
379         p.stack_.push(PosIteratorItem(const_cast<ParagraphList *>(pp.plist),
380                                       pp.pit, pos, 0));
381         return p;
382 }
383
384
385 void ParIterator::lockPath(BufferView * bv) const
386 {
387         bv->insetUnlock();
388         int last = size() - 1;
389         for (int i = 0; i < last; ++i) {
390                 UpdatableInset * outer = dynamic_cast<UpdatableInset *>((*pimpl_->positions[i].it)->inset);
391                 FuncRequest cmd(bv, LFUN_INSET_EDIT);
392                 outer->dispatch(cmd);
393                 LyXText * txt = outer->getText(*pimpl_->positions[i].index);
394                 InsetText * inner = txt->inset_owner;
395                 // deep vodoo magic: on a table, the edit call locks the first
396                 // cell and further lock calls get lost there.
397                 // We have to unlock it to then lock the correct one.
398                 if (outer != inner) {
399                         outer->insetUnlock(bv);
400                         outer->lockInsetInInset(bv, inner);
401                         inner->dispatch(FuncRequest(bv, LFUN_INSET_EDIT));
402                 }
403         }
404 }