]> git.lyx.org Git - lyx.git/blob - src/cursor.C
Convert the Search/Replace dialog to the cleaner MCV scheme.
[lyx.git] / src / cursor.C
1 /**
2  * \file cursor.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author André Pönitz
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "buffer.h"
14 #include "BufferView.h"
15 #include "cursor.h"
16 #include "debug.h"
17 #include "dispatchresult.h"
18 #include "funcrequest.h"
19 #include "iterators.h"
20 #include "lfuns.h"
21 #include "lyxtext.h"
22 #include "paragraph.h"
23 #include "lyxrow.h"
24
25 #include "insets/updatableinset.h"
26 #include "insets/insettabular.h"
27 #include "insets/insettext.h"
28
29 #include "mathed/math_data.h"
30 #include "mathed/math_inset.h"
31
32 #include <boost/assert.hpp>
33
34 using std::vector;
35 using std::endl;
36
37
38 LCursor::LCursor(BufferView & bv)
39         : cursor_(1), anchor_(1), bv_(&bv), current_(0),
40           cached_y_(0), x_target_(-1),
41           selection_(false), mark_(false)
42 {}
43
44
45 DispatchResult LCursor::dispatch(FuncRequest const & cmd0)
46 {
47         lyxerr << "\nLCursor::dispatch: " << *this << endl;
48         FuncRequest cmd = cmd0;
49
50         for (int i = cursor_.size() - 1; i >= 1; --i) {
51                 current_ = i;
52                 CursorSlice const & citem = cursor_[i];
53                 lyxerr << "trying to dispatch to inset " << citem.inset_ << endl;
54                 DispatchResult res = inset()->dispatch(*this, cmd);
55                 if (res.dispatched()) {
56                         lyxerr << " successfully dispatched to inset " << citem.inset_ << endl;
57                         return DispatchResult(true, true);
58                 }
59                 // remove one level of cursor
60                 switch (res.val()) {
61                         case FINISHED:
62                                 pop(i);
63                                 cmd = FuncRequest(LFUN_FINISHED_LEFT);
64                                 break;
65                         case FINISHED_RIGHT:
66                                 pop(i);
67                                 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
68                                 break;
69                         case FINISHED_UP:
70                                 pop(i);
71                                 cmd = FuncRequest(LFUN_FINISHED_UP);
72                                 break;
73                         case FINISHED_DOWN:
74                                 pop(i);
75                                 cmd = FuncRequest(LFUN_FINISHED_DOWN);
76                                 break;
77                         default:
78                                 lyxerr << "not handled on level " << i << " val: " << res.val() << endl;
79                                 break;
80                 }
81         }
82         lyxerr << "trying to dispatch to main text " << bv_->text() << endl;
83         DispatchResult res = bv_->text()->dispatch(*this, cmd);
84         lyxerr << "   result: " << res.val() << endl;
85
86         if (!res.dispatched()) {
87                 lyxerr << "trying to dispatch to bv " << bv_ << endl;
88                 bool sucess = bv_->dispatch(cmd);
89                 lyxerr << "   result: " << sucess << endl;
90                 res.dispatched(sucess);
91         }
92
93         return res;
94 }
95
96
97 void LCursor::push(InsetBase * inset)
98 {
99         lyxerr << "LCursor::push()  inset: " << inset << endl;
100         cursor_.push_back(CursorSlice(inset));
101         anchor_.push_back(CursorSlice(inset));
102         ++current_;
103         updatePos();
104 }
105
106
107 void LCursor::pop(int depth)
108 {
109         lyxerr << "LCursor::pop() to depth " << depth << endl;
110         while (cursor_.size() > depth)
111                 pop();
112 }
113
114
115 void LCursor::pop()
116 {
117         lyxerr << "LCursor::pop() a level" << endl;
118         //BOOST_ASSERT(!cursor_.empty());
119         if (cursor_.size() <= 1)
120                 lyxerr << "### TRYING TO POP FROM EMPTY CURSOR" << endl;
121         else {
122                 cursor_.pop_back();
123                 anchor_.pop_back();
124                 current_ = cursor_.size() - 1;
125         }
126         lyxerr << "LCursor::pop() current now: " << current_ << endl;
127 }
128
129
130 CursorSlice & LCursor::current()
131 {
132         //lyxerr << "accessing cursor slice " << current_
133         //      << ": " << cursor_[current_] << endl;
134         return cursor_[current_];
135 }
136
137
138 CursorSlice const & LCursor::current() const
139 {
140         //lyxerr << "accessing cursor slice " << current_
141         //      << ": " << cursor_[current_] << endl;
142         return cursor_[current_];
143 }
144
145
146 LyXText * LCursor::innerText() const
147 {
148         //lyxerr << "LCursor::innerText()  depth: " << cursor_.size() << endl;
149         if (cursor_.size() > 1) {
150                 // go up until first non-0 text is hit
151                 // (innermost text is 0 e.g. for mathed and the outer tabular level)
152                 for (int i = cursor_.size() - 1; i >= 1; --i)
153                         if (cursor_[i].text())
154                                 return cursor_[i].text();
155         }
156         return bv_->text();
157 }
158
159
160 void LCursor::updatePos()
161 {
162         if (cursor_.size() > 1)
163                 cached_y_ = bv_->top_y() + cursor_.back().inset()->y();
164 }
165
166
167 void LCursor::getDim(int & asc, int & desc) const
168 {
169         LyXText * txt = innerText();
170
171         if (txt) {
172                 Row const & row = *txt->cursorRow();
173                 asc = row.baseline();
174                 desc = row.height() - asc;
175         } else {
176                 asc = 10;
177                 desc = 10;
178                 //innerInset()->getCursorDim(asc, desc);
179         }
180 }
181
182
183 void LCursor::getPos(int & x, int & y) const
184 {
185         if (cursor_.size() <= 1) {
186                 x = bv_->text()->cursorX();
187                 y = bv_->text()->cursorY();
188 //              y -= bv_->top_y();
189         } else {
190                 if (inset()->asUpdatableInset()) {
191                         // Would be nice to clean this up to make some understandable sense...
192                         // Non-obvious. The reason we have to have these
193                         // extra checks is that the ->getCursor() calls rely
194                         // on the inset's own knowledge of its screen position.
195                         // If we scroll up or down in a big enough increment, the
196                         // inset->draw() is not called: this doesn't update
197                         // inset.top_baseline, so getCursor() returns an old value.
198                         // Ugly as you like.
199                         inset()->asUpdatableInset()->getCursorPos(cursor_.back().idx_, x, y);
200                         x += inset()->asUpdatableInset()->x();
201                         y += cached_y_;
202                 } else if (inset()->asMathInset()) {
203 #warning FIXME
204                         x = 100;
205                         y = 100;
206                 } else {
207                         // this should not happen
208                         BOOST_ASSERT(false);
209                 }
210         }
211 }
212
213
214 InsetBase * LCursor::innerInsetOfType(int code) const
215 {
216         for (int i = cursor_.size() - 1; i >= 1; --i)
217                 if (cursor_[i].inset_->lyxCode() == code)
218                         return cursor_[i].inset_;
219         return 0;
220 }
221
222
223 InsetTabular * LCursor::innerInsetTabular() const
224 {
225         return static_cast<InsetTabular *>(innerInsetOfType(InsetBase::TABULAR_CODE));
226 }
227
228
229 void LCursor::resetAnchor()
230 {
231         anchor_ = cursor_;
232 }
233
234
235 BufferView & LCursor::bv() const
236 {
237         return *bv_;
238 }
239
240
241 MathAtom const & LCursor::prevAtom() const
242 {
243         BOOST_ASSERT(pos() > 0);
244         return cell()[pos() - 1];
245 }
246
247
248 MathAtom & LCursor::prevAtom()
249 {
250         BOOST_ASSERT(pos() > 0);
251         return cell()[pos() - 1];
252 }
253
254
255 MathAtom const & LCursor::nextAtom() const
256 {
257         BOOST_ASSERT(pos() < lastpos());
258         return cell()[pos()];
259 }
260
261
262 MathAtom & LCursor::nextAtom()
263 {
264         BOOST_ASSERT(pos() < lastpos());
265         return cell()[pos()];
266 }
267
268
269 bool LCursor::posLeft()
270 {
271         if (pos() == 0)
272                 return false;
273         --pos();
274         return true;
275 }
276
277
278 bool LCursor::posRight()
279 {
280         if (pos() == lastpos())
281                 return false;
282         ++pos();
283         return true;
284 }
285
286
287 CursorSlice & LCursor::anchor()
288 {
289         return anchor_.back();
290 }
291
292
293 CursorSlice const & LCursor::anchor() const
294 {
295         return anchor_.back();
296 }
297
298
299 CursorSlice const & LCursor::selStart() const
300 {
301         if (!selection())
302                 return cursor_.back();
303         // can't use std::min as this creates a new object
304         return anchor() < cursor_.back() ? anchor() : cursor_.back();
305 }
306
307
308 CursorSlice const & LCursor::selEnd() const
309 {
310         if (!selection())
311                 return cursor_.back();
312         return anchor() > cursor_.back() ? anchor() : cursor_.back();
313 }
314
315
316 CursorSlice & LCursor::selStart()
317 {
318         if (!selection())
319                 return cursor_.back();
320         return anchor() < cursor_.back() ? anchor() : cursor_.back();
321 }
322
323
324 CursorSlice & LCursor::selEnd()
325 {
326         if (selection())
327                 return cursor_.back();
328         return anchor() > cursor_.back() ? anchor() : cursor_.back();
329 }
330
331
332 void LCursor::setSelection()
333 {
334         selection() = true;
335         // a selection with no contents is not a selection
336         if (par() == anchor().par() && pos() == anchor().pos())
337                 selection() = false;
338 }
339
340
341 void LCursor::clearSelection()
342 {
343         selection() = false;
344         mark() = false;
345         resetAnchor();
346         bv().unsetXSel();
347 }
348
349
350
351 //
352 // CursorBase
353 //
354
355
356 void increment(CursorBase & it)
357 {
358         CursorSlice & top = it.back();
359         MathArray & ar = top.asMathInset()->cell(top.idx_);
360
361         // move into the current inset if possible
362         // it is impossible for pos() == size()!
363         MathInset * n = 0;
364         if (top.pos() != top.lastpos())
365                 n = (ar.begin() + top.pos_)->nucleus();
366         if (n && n->isActive()) {
367                 it.push_back(CursorSlice(n));
368                 return;
369         }
370
371         // otherwise move on one cell back if possible
372         if (top.pos() < top.lastpos()) {
373                 // pos() == lastpos() is valid!
374                 ++top.pos_;
375                 return;
376         }
377
378         // otherwise try to move on one cell if possible
379         while (top.idx_ + 1 < top.asMathInset()->nargs()) {
380                 // idx() == nargs() is _not_ valid!
381                 ++top.idx_;
382                 if (top.asMathInset()->validCell(top.idx_)) {
383                         top.pos_ = 0;
384                         return;
385                 }
386         }
387
388         // otherwise leave array, move on one back
389         // this might yield pos() == size(), but that's a ok.
390         it.pop_back();
391         // it certainly invalidates top
392         ++it.back().pos_;
393 }
394
395
396 CursorBase ibegin(InsetBase * p)
397 {
398         CursorBase it;
399         it.push_back(CursorSlice(p));
400         return it;
401 }
402
403
404 CursorBase iend(InsetBase * p)
405 {
406         CursorBase it;
407         it.push_back(CursorSlice(p));
408         CursorSlice & top = it.back();
409         top.idx_ = top.asMathInset()->nargs() - 1;
410         top.pos_ = top.asMathInset()->cell(top.idx_).size();
411         return it;
412 }
413
414
415 void LCursor::x_target(int x)
416 {
417         x_target_ = x;
418 }
419
420
421 int LCursor::x_target() const
422 {
423         return x_target_;
424 }
425
426
427 Paragraph & LCursor::paragraph()
428 {
429         return current_ ? current().paragraph() : *bv_->text()->getPar(par());
430 }
431
432
433 Paragraph const & LCursor::paragraph() const
434 {
435         return current_ ? current().paragraph() : *bv_->text()->getPar(par());
436 }
437
438
439 LCursor::pos_type LCursor::lastpos() const
440 {
441         return current_ ? current().lastpos() : bv_->text()->getPar(par())->size();
442 }
443
444
445 size_t LCursor::nargs() const
446 {
447         // assume 1x1 grid for 'plain text'
448         return current_ ? current().nargs() : 1;
449 }
450
451
452 size_t LCursor::ncols() const
453 {
454         // assume 1x1 grid for 'plain text'
455         return current_ ? current().ncols() : 1;
456 }
457
458
459 size_t LCursor::nrows() const
460 {
461         // assume 1x1 grid for 'plain text'
462         return current_ ? current().nrows() : 1;
463 }
464
465
466 LCursor::row_type LCursor::row() const
467 {
468         BOOST_ASSERT(current_ > 0);
469         return current().row();
470 }
471
472
473 LCursor::col_type LCursor::col() const
474 {
475         BOOST_ASSERT(current_ > 0);
476         return current().col();
477 }
478
479
480 MathArray const & LCursor::cell() const
481 {
482         BOOST_ASSERT(current_ > 0);
483         return current().cell();
484 }
485
486
487 MathArray & LCursor::cell()
488 {
489         BOOST_ASSERT(current_ > 0);
490         return current().cell();
491 }
492
493
494 void LCursor::info(std::ostream & os)
495 {
496         for (int i = 1, n = depth(); i < n; ++i) {
497                 cursor_[i].inset()->infoize(os);
498                 os << "  ";
499         }
500 #warning FIXME
501         //if (pos() != 0)
502         //      prevAtom()->infoize2(os);
503         // overwite old message
504         os << "                    ";
505 }
506
507
508 std::ostream & operator<<(std::ostream & os, LCursor const & cur)
509 {
510         os << "\n";
511         for (size_t i = 0, n = cur.cursor_.size(); i != n; ++i)
512                 os << "  (" << cur.cursor_[i] << " | " << cur.anchor_[i] << "\n";
513         return os;
514 }