]> git.lyx.org Git - lyx.git/blob - src/cursor.C
rename a function
[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 Alejandro Aguilar Sierra
7  * \author Alfredo Braunstein
8  * \author André Pönitz
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "buffer.h"
16 #include "BufferView.h"
17 #include "cursor.h"
18 #include "debug.h"
19 #include "dispatchresult.h"
20 #include "funcrequest.h"
21 #include "iterators.h"
22 #include "lfuns.h"
23 #include "lyxfunc.h" // only for setMessage()
24 #include "lyxrc.h"
25 #include "lyxrow.h"
26 #include "lyxtext.h"
27 #include "paragraph.h"
28
29 #include "insets/updatableinset.h"
30 #include "insets/insettabular.h"
31 #include "insets/insettext.h"
32
33 #include "mathed/math_data.h"
34 #include "mathed/math_hullinset.h"
35 #include "mathed/math_support.h"
36
37 #include "support/limited_stack.h"
38 #include "support/std_sstream.h"
39
40 #include "frontends/LyXView.h"
41
42 #include <boost/assert.hpp>
43
44 using std::string;
45 using std::vector;
46 using std::endl;
47 #ifndef CXX_GLOBAL_CSTD
48 using std::isalpha;
49 #endif
50 using std::min;
51 using std::swap;
52
53
54
55 // our own cut buffer
56 limited_stack<string> theCutBuffer;
57
58
59 LCursor::LCursor(BufferView & bv)
60         : cursor_(1), anchor_(1), bv_(&bv), current_(0),
61           cached_y_(0), x_target_(-1),
62           selection_(false), mark_(false)
63 {}
64
65
66 void LCursor::reset()
67 {
68         cursor_.clear();
69         anchor_.clear();
70         cursor_.push_back(CursorSlice());
71         anchor_.push_back(CursorSlice());
72         current_ = 0;
73         cached_y_ = 0;
74         clearTargetX();
75         selection_ = false;
76         mark_ = false;
77 }
78
79
80 DispatchResult LCursor::dispatch(FuncRequest const & cmd0)
81 {
82         lyxerr << "\nLCursor::dispatch: cmd: " << cmd0 << endl << *this << endl;
83         FuncRequest cmd = cmd0;
84         disp_.update(true);
85         disp_.val(NONE);
86         for (current_ = cursor_.size() - 1; current_ >= 1; --current_) {
87                 // the inset's dispatch() is supposed to reset the update and
88                 // val flags if necessary 
89                 inset()->dispatch(*this, cmd);
90                 
91                 // "Mutate" the request for semi-handled requests that need
92                 // additional handling in outer levels.
93                 switch (disp_.val()) {
94                         case NONE:
95                                 // the inset handled the event fully
96                                 current_ = cursor_.size() - 1;
97                                 return DispatchResult(true, true);
98                         case FINISHED:
99                                 // the inset handled the event partially
100                                 cmd = FuncRequest(LFUN_FINISHED_LEFT);
101                                 break;
102                         case FINISHED_RIGHT:
103                                 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
104                                 break;
105                         case FINISHED_UP:
106                                 cmd = FuncRequest(LFUN_FINISHED_UP);
107                                 break;
108                         case FINISHED_DOWN:
109                                 cmd = FuncRequest(LFUN_FINISHED_DOWN);
110                                 break;
111                         default:
112                                 //lyxerr << "not handled on level " << current_
113                                 //      << " val: " << disp_.val() << endl;
114                                 break;
115                 }
116         }
117         BOOST_ASSERT(current_ == 0);
118         bv_->text()->dispatch(*this, cmd);
119         //lyxerr << "   result: " << res.val() << endl;
120         current_ = cursor_.size() - 1;
121         return disp_;
122 }
123
124
125 void LCursor::push(InsetBase * inset)
126 {
127         lyxerr << "LCursor::push()  inset: " << inset << endl;
128         cursor_.push_back(CursorSlice(inset));
129         anchor_.push_back(CursorSlice(inset));
130         ++current_;
131         updatePos();
132 }
133
134
135 void LCursor::pop(int depth)
136 {
137         while (int(cursor_.size()) > depth + 1)
138                 pop();
139         lyxerr << "LCursor::pop() result: " << *this << endl;
140 }
141
142
143 void LCursor::pop()
144 {
145         BOOST_ASSERT(cursor_.size() >= 1);
146         cursor_.pop_back();
147         anchor_.pop_back();
148         current_ = cursor_.size() - 1;
149 }
150
151
152 void LCursor::pushLeft(InsetBase * p)
153 {
154         BOOST_ASSERT(!cursor_.empty());
155         //lyxerr << "Entering inset " << t << " left" << endl;
156         push(p);
157         p->idxFirst(*this);
158 }
159
160
161 bool LCursor::popLeft()
162 {
163         BOOST_ASSERT(!cursor_.empty());
164         //lyxerr << "Leaving inset to the left" << endl;
165         if (depth() <= 1) {
166                 if (depth() == 1)
167                         inset()->notifyCursorLeaves(idx());
168                 return false;
169         }
170         inset()->notifyCursorLeaves(idx());
171         pop();
172         return true;
173 }
174
175
176 bool LCursor::popRight()
177 {
178         BOOST_ASSERT(!cursor_.empty());
179         //lyxerr << "Leaving inset to the right" << endl;
180         if (depth() <= 1) {
181                 if (depth() == 1)
182                         inset()->notifyCursorLeaves(idx());
183                 return false;
184         }
185         inset()->notifyCursorLeaves(idx());
186         pop();
187         ++pos();
188         return true;
189 }
190
191
192 CursorSlice & LCursor::current()
193 {
194         BOOST_ASSERT(!cursor_.empty());
195         //lyxerr << "accessing cursor slice " << current_
196         //      << ": " << cursor_[current_] << endl;
197         return cursor_[current_];
198 }
199
200
201 CursorSlice const & LCursor::current() const
202 {
203         //lyxerr << "accessing cursor slice " << current_
204         //      << ": " << cursor_[current_] << endl;
205         return cursor_[current_];
206 }
207
208
209 int LCursor::currentMode()
210 {
211         BOOST_ASSERT(!cursor_.empty());
212         for (int i = cursor_.size() - 1; i >= 1; --i) {
213                 int res = cursor_[i].inset()->currentMode();
214                 if (res != MathInset::UNDECIDED_MODE)
215                         return res;
216         }
217         return MathInset::TEXT_MODE;
218 }
219
220
221 LyXText * LCursor::innerText() const
222 {
223         BOOST_ASSERT(!cursor_.empty());
224         if (cursor_.size() > 1) {
225                 // go up until first non-0 text is hit
226                 // (innermost text is 0 in mathed)
227                 for (int i = cursor_.size() - 1; i >= 1; --i)
228                         if (cursor_[i].text())
229                                 return cursor_[i].text();
230         }
231         return bv_->text();
232 }
233
234
235 CursorSlice const & LCursor::innerTextSlice() const
236 {
237         BOOST_ASSERT(!cursor_.empty());
238         if (cursor_.size() > 1) {
239                 // go up until first non-0 text is hit
240                 // (innermost text is 0 in mathed)
241                 for (int i = cursor_.size() - 1; i >= 1; --i)
242                         if (cursor_[i].text())
243                                 return cursor_[i];
244         }
245         return cursor_[0];
246 }
247
248
249 void LCursor::updatePos()
250 {
251         BOOST_ASSERT(!cursor_.empty());
252         if (cursor_.size() > 1)
253                 cached_y_ = bv_->top_y() + cursor_.back().inset()->yo();
254                 //cached_y_ = cursor_.back().inset()->yo();
255 }
256
257
258 void LCursor::getDim(int & asc, int & des) const
259 {
260         BOOST_ASSERT(!cursor_.empty());
261         if (inMathed()) {
262                 BOOST_ASSERT(inset());
263                 BOOST_ASSERT(inset()->asMathInset());
264                 //inset()->asMathInset()->getCursorDim(asc, des);
265                 asc = 10;
266                 des = 10;
267         } else {
268                 Row const & row = textRow();
269                 asc = row.baseline();
270                 des = row.height() - asc;
271         }
272 }
273
274
275 void LCursor::getPos(int & x, int & y) const
276 {
277         BOOST_ASSERT(!cursor_.empty());
278         x = 0;
279         y = 0;
280         if (cursor_.size() == 1) {
281                 x = bv_->text()->cursorX(cursor_.front());
282                 y = bv_->text()->cursorY(cursor_.front());
283         } else {
284                 if (!inset()) {
285                         lyxerr << "#### LCursor::getPos: " << *this << endl;
286                         BOOST_ASSERT(inset());
287                 }
288                 inset()->getCursorPos(cursor_.back(), x, y);
289                 // getCursorPos gives _screen_ coordinates. We need to add
290                 // top_y to get document coordinates. This is hidden in cached_y_.
291                 //y += cached_y_ - inset()->yo();
292                 // The rest is non-obvious. The reason we have to have these
293                 // extra computation is that the getCursorPos() calls rely
294                 // on the inset's own knowledge of its screen position.
295                 // If we scroll up or down in a big enough increment,
296                 // inset->draw() is not called: this doesn't update
297                 // inset.yo_, so getCursor() returns an old value.
298                 // Ugly as you like.
299         }
300         //lyxerr << "#### LCursor::getPos: " << *this 
301         // << " x: " << x << " y: " << y << endl;
302 }
303
304
305 void LCursor::paste(string const & data)
306 {
307         dispatch(FuncRequest(LFUN_PASTE, data));
308 }
309
310
311 InsetBase * LCursor::innerInsetOfType(int code) const
312 {
313         for (int i = cursor_.size() - 1; i >= 1; --i)
314                 if (cursor_[i].inset_->lyxCode() == code)
315                         return cursor_[i].inset_;
316         return 0;
317 }
318
319
320 InsetTabular * LCursor::innerInsetTabular() const
321 {
322         return static_cast<InsetTabular *>(innerInsetOfType(InsetBase::TABULAR_CODE));
323 }
324
325
326 void LCursor::resetAnchor()
327 {
328         anchor_ = cursor_;
329 }
330
331
332 BufferView & LCursor::bv() const
333 {
334         return *bv_;
335 }
336
337
338 MathAtom const & LCursor::prevAtom() const
339 {
340         BOOST_ASSERT(pos() > 0);
341         return cell()[pos() - 1];
342 }
343
344
345 MathAtom & LCursor::prevAtom()
346 {
347         BOOST_ASSERT(pos() > 0);
348         return cell()[pos() - 1];
349 }
350
351
352 MathAtom const & LCursor::nextAtom() const
353 {
354         BOOST_ASSERT(pos() < lastpos());
355         return cell()[pos()];
356 }
357
358
359 MathAtom & LCursor::nextAtom()
360 {
361         BOOST_ASSERT(pos() < lastpos());
362         return cell()[pos()];
363 }
364
365
366 bool LCursor::posLeft()
367 {
368         if (pos() == 0)
369                 return false;
370         --pos();
371         return true;
372 }
373
374
375 bool LCursor::posRight()
376 {
377         if (pos() == lastpos())
378                 return false;
379         ++pos();
380         return true;
381 }
382
383
384 CursorSlice & LCursor::anchor()
385 {
386         return anchor_.back();
387 }
388
389
390 CursorSlice const & LCursor::anchor() const
391 {
392         return anchor_.back();
393 }
394
395
396 CursorSlice const & LCursor::selBegin() const
397 {
398         if (!selection())
399                 return cursor_.back();
400         return anchor() < cursor_.back() ? anchor() : cursor_.back();
401 }
402
403
404 CursorSlice & LCursor::selBegin()
405 {
406         if (!selection())
407                 return cursor_.back();
408         // can't use std::min as this returns a const ref
409         return anchor() < cursor_.back() ? anchor() : cursor_.back();
410 }
411
412
413 CursorSlice const & LCursor::selEnd() const
414 {
415         if (!selection())
416                 return cursor_.back();
417         return anchor() > cursor_.back() ? anchor() : cursor_.back();
418 }
419
420
421 CursorSlice & LCursor::selEnd()
422 {
423         if (!selection())
424                 return cursor_.back();
425         // can't use std::min as this returns a const ref
426         return anchor() > cursor_.back() ? anchor() : cursor_.back();
427 }
428
429
430 void LCursor::setSelection()
431 {
432         selection() = true;
433         // a selection with no contents is not a selection
434         if (par() == anchor().par() && pos() == anchor().pos())
435                 selection() = false;
436 }
437
438
439 void LCursor::setSelection(CursorBase const & where, size_t n)
440 {
441         selection() = true;
442         cursor_ = where;
443         anchor_ = where;
444         pos() += n;
445 }
446
447
448 void LCursor::clearSelection()
449 {
450         selection() = false;
451         mark() = false;
452         resetAnchor();
453         bv().unsetXSel();
454 }
455
456
457 int & LCursor::x_target()
458 {
459         return x_target_;
460 }
461
462
463 int LCursor::x_target() const
464 {
465         return x_target_;
466 }
467
468
469 void LCursor::clearTargetX()
470 {
471         x_target_ = -1;
472 }
473
474
475 LyXText * LCursor::text() const
476 {
477         return current_ ? current().text() : bv_->text();
478 }
479
480
481 Paragraph & LCursor::paragraph()
482 {
483         BOOST_ASSERT(inTexted());
484         return current_ ? current().paragraph() : *bv_->text()->getPar(par());
485 }
486
487
488 Paragraph const & LCursor::paragraph() const
489 {
490         BOOST_ASSERT(inTexted());
491         return current_ ? current().paragraph() : *bv_->text()->getPar(par());
492 }
493
494
495 Row & LCursor::textRow()
496 {
497         return *paragraph().getRow(pos());
498 }
499
500
501 Row const & LCursor::textRow() const
502 {
503         return *paragraph().getRow(pos());
504 }
505
506
507 LCursor::par_type LCursor::lastpar() const
508 {
509         return inMathed() ? 0 : text()->paragraphs().size() - 1;
510 }
511
512
513 LCursor::pos_type LCursor::lastpos() const
514 {
515         InsetBase * inset = current().inset();
516         return inset && inset->asMathInset() ? cell().size() : paragraph().size();
517 }
518
519
520 LCursor::row_type LCursor::crow() const
521 {
522         return paragraph().row(pos());
523 }
524
525
526 LCursor::row_type LCursor::lastcrow() const
527 {
528         return paragraph().rows.size();
529 }
530
531
532 size_t LCursor::nargs() const
533 {
534         // assume 1x1 grid for 'plain text'
535         return current_ ? current().nargs() : 1;
536 }
537
538
539 size_t LCursor::ncols() const
540 {
541         // assume 1x1 grid for 'plain text'
542         return current_ ? current().ncols() : 1;
543 }
544
545
546 size_t LCursor::nrows() const
547 {
548         // assume 1x1 grid for 'plain text'
549         return current_ ? current().nrows() : 1;
550 }
551
552
553 LCursor::row_type LCursor::row() const
554 {
555         BOOST_ASSERT(current_ > 0);
556         return current().row();
557 }
558
559
560 LCursor::col_type LCursor::col() const
561 {
562         BOOST_ASSERT(current_ > 0);
563         return current().col();
564 }
565
566
567 MathArray const & LCursor::cell() const
568 {
569         BOOST_ASSERT(current_ > 0);
570         return current().cell();
571 }
572
573
574 MathArray & LCursor::cell()
575 {
576         BOOST_ASSERT(current_ > 0);
577         return current().cell();
578 }
579
580
581 void LCursor::info(std::ostream & os) const
582 {
583         for (int i = 1, n = depth(); i < n; ++i) {
584                 cursor_[i].inset()->infoize(os);
585                 os << "  ";
586         }
587         if (pos() != 0)
588                 prevInset()->infoize2(os);
589         // overwite old message
590         os << "                    ";
591 }
592
593
594 namespace {
595
596 void region(CursorSlice const & i1, CursorSlice const & i2,
597         LCursor::row_type & r1, LCursor::row_type & r2,
598         LCursor::col_type & c1, LCursor::col_type & c2)
599 {
600         InsetBase * p = i1.inset();
601         c1 = p->col(i1.idx_);
602         c2 = p->col(i2.idx_);
603         if (c1 > c2)
604                 swap(c1, c2);
605         r1 = p->row(i1.idx_);
606         r2 = p->row(i2.idx_);
607         if (r1 > r2)
608                 swap(r1, r2);
609 }
610
611 }
612
613
614 string LCursor::grabSelection()
615 {
616         if (!selection())
617                 return string();
618
619         CursorSlice i1 = selBegin();
620         CursorSlice i2 = selEnd();
621
622         if (i1.idx_ == i2.idx_) {
623                 if (i1.inset()->asMathInset()) {
624                         MathArray::const_iterator it = i1.cell().begin();
625                         return asString(MathArray(it + i1.pos_, it + i2.pos_));
626                 } else {
627                         return "unknown selection 1";
628                 }
629         }
630
631         row_type r1, r2;
632         col_type c1, c2;
633         region(i1, i2, r1, r2, c1, c2);
634
635         string data;
636         if (i1.inset()->asMathInset()) {
637                 for (row_type row = r1; row <= r2; ++row) {
638                         if (row > r1)
639                                 data += "\\\\";
640                         for (col_type col = c1; col <= c2; ++col) {
641                                 if (col > c1)
642                                         data += '&';
643                                 data += asString(i1.asMathInset()->cell(i1.asMathInset()->index(row, col)));
644                         }
645                 }
646         } else {
647                 data = "unknown selection 2";
648         }
649         return data;
650 }
651
652
653 void LCursor::eraseSelection()
654 {
655         //lyxerr << "LCursor::eraseSelection" << endl;
656         CursorSlice const & i1 = selBegin();
657         CursorSlice const & i2 = selEnd();
658 #warning FIXME
659         if (i1.inset()->asMathInset()) {
660                 if (i1.idx_ == i2.idx_) {
661                         i1.cell().erase(i1.pos_, i2.pos_);
662                 } else {
663                         MathInset * p = i1.asMathInset();
664                         row_type r1, r2;
665                         col_type c1, c2;
666                         region(i1, i2, r1, r2, c1, c2);
667                         for (row_type row = r1; row <= r2; ++row)
668                                 for (col_type col = c1; col <= c2; ++col)
669                                         p->cell(p->index(row, col)).clear();
670                 }
671                 current() = i1;
672         } else {
673                 lyxerr << "can't erase this selection 1" << endl;
674         }
675         //lyxerr << "LCursor::eraseSelection end" << endl;
676 }
677
678
679 string LCursor::grabAndEraseSelection()
680 {
681         if (!selection())
682                 return string();
683         string res = grabSelection();
684         eraseSelection();
685         selection() = false;
686         return res;
687 }
688
689
690 void LCursor::selClear()
691 {
692         resetAnchor();
693         clearSelection();
694 }
695
696
697 void LCursor::selCopy()
698 {
699         if (selection()) {
700                 theCutBuffer.push(grabSelection());
701                 selection() = false;
702         } else {
703                 //theCutBuffer.erase();
704         }
705 }
706
707
708 void LCursor::selCut()
709 {
710         theCutBuffer.push(grabAndEraseSelection());
711 }
712
713
714 void LCursor::selDel()
715 {
716         //lyxerr << "LCursor::selDel" << endl;
717         if (selection()) {
718                 eraseSelection();
719                 selection() = false;
720         }
721 }
722
723
724 void LCursor::selPaste(size_t n)
725 {
726         selClearOrDel();
727         if (n < theCutBuffer.size())
728                 paste(theCutBuffer[n]);
729         //grabSelection();
730         selection() = false;
731 }
732
733
734 void LCursor::selHandle(bool sel)
735 {
736         //lyxerr << "LCursor::selHandle" << endl;
737         if (sel == selection())
738                 return;
739         resetAnchor();
740         selection() = sel;
741 }
742
743
744 void LCursor::selClearOrDel()
745 {
746         //lyxerr << "LCursor::selClearOrDel" << endl;
747         if (lyxrc.auto_region_delete)
748                 selDel();
749         else
750                 selection() = false;
751 }
752
753
754 std::ostream & operator<<(std::ostream & os, LCursor const & cur)
755 {
756         for (size_t i = 0, n = cur.cursor_.size(); i != n; ++i)
757                 os << " " << cur.cursor_[i] << " | " << cur.anchor_[i] << "\n";
758         os << " current: " << cur.current_ << endl;
759         os << " selection: " << cur.selection_ << endl;
760         return os;
761 }
762
763
764
765
766 //
767 // CursorBase
768 //
769
770
771 void increment(CursorBase & it)
772 {
773         CursorSlice & top = it.back();
774         MathArray & ar = top.asMathInset()->cell(top.idx_);
775
776         // move into the current inset if possible
777         // it is impossible for pos() == size()!
778         MathInset * n = 0;
779         if (top.pos() != top.lastpos())
780                 n = (ar.begin() + top.pos_)->nucleus();
781         if (n && n->isActive()) {
782                 it.push_back(CursorSlice(n));
783                 return;
784         }
785
786         // otherwise move on one cell back if possible
787         if (top.pos() < top.lastpos()) {
788                 // pos() == lastpos() is valid!
789                 ++top.pos_;
790                 return;
791         }
792
793         // otherwise try to move on one cell if possible
794         while (top.idx() < top.lastidx()) {
795                 ++top.idx_;
796                 if (top.asMathInset()->validCell(top.idx_)) {
797                         top.pos_ = 0;
798                         return;
799                 }
800         }
801
802         // otherwise leave array, move on one back
803         // this might yield pos() == size(), but that's a ok.
804         it.pop_back();
805         // it certainly invalidates top
806         ++it.back().pos_;
807 }
808
809
810 CursorBase ibegin(InsetBase * p)
811 {
812         CursorBase it;
813         it.push_back(CursorSlice(p));
814         return it;
815 }
816
817
818 CursorBase iend(InsetBase * p)
819 {
820         CursorBase it;
821         it.push_back(CursorSlice(p));
822         CursorSlice & cur = it.back();
823         cur.idx() = cur.lastidx();
824         cur.pos() = cur.lastpos();
825         return it;
826 }
827
828
829
830
831 ///////////////////////////////////////////////////////////////////
832 //
833 // The part below is the non-integrated rest of the original math
834 // cursor. This should be either generalized for texted or moved
835 // back to the math insets.
836 //
837 ///////////////////////////////////////////////////////////////////
838
839 #include "mathed/math_braceinset.h"
840 #include "mathed/math_charinset.h"
841 #include "mathed/math_commentinset.h"
842 #include "mathed/math_factory.h"
843 #include "mathed/math_gridinset.h"
844 #include "mathed/math_macroarg.h"
845 #include "mathed/math_macrotemplate.h"
846 #include "mathed/math_mathmlstream.h"
847 #include "mathed/math_scriptinset.h"
848 #include "mathed/math_spaceinset.h"
849 #include "mathed/math_support.h"
850 #include "mathed/math_unknowninset.h"
851
852 //#define FILEDEBUG 1
853
854
855 bool LCursor::isInside(InsetBase const * p)
856 {
857         for (unsigned i = 0; i < depth(); ++i)
858                 if (cursor_[i].inset() == p)
859                         return true;
860         return false;
861 }
862
863
864 bool LCursor::openable(MathAtom const & t)
865 {
866         if (!t->isActive())
867                 return false;
868
869         if (t->lock())
870                 return false;
871
872         if (!selection())
873                 return true;
874
875         // we can't move into anything new during selection
876         if (depth() == anchor_.size())
877                 return false;
878         if (!ptr_cmp(t.nucleus(), anchor_[depth()].inset()))
879                 return false;
880
881         return true;
882 }
883
884
885 bool LCursor::inNucleus()
886 {
887         return inset()->asMathInset()->asScriptInset() && idx() == 2;
888 }
889
890
891 bool LCursor::left()
892 {
893         autocorrect() = false;
894         clearTargetX();
895         if (inMacroMode()) {
896                 macroModeClose();
897                 return true;
898         }
899
900         if (pos() != 0 && openable(prevAtom())) {
901                 posLeft();
902                 push(nextAtom().nucleus());
903                 inset()->idxLast(*this);
904                 return true;
905         }
906
907         return posLeft() || idxLeft() || popLeft() || selection();
908 }
909
910
911 bool LCursor::right()
912 {
913         autocorrect() = false;
914         clearTargetX();
915         if (inMacroMode()) {
916                 macroModeClose();
917                 return true;
918         }
919
920         if (pos() != lastpos() && openable(nextAtom())) {
921                 pushLeft(nextAtom().nucleus());
922                 inset()->idxFirst(*this);
923                 return true;
924         }
925
926         return posRight() || idxRight() || popRight() || selection();
927 }
928
929
930 bool positionable(CursorBase const & cursor, CursorBase const & anchor)
931 {
932         // avoid deeper nested insets when selecting
933         if (cursor.size() > anchor.size())
934                 return false;
935
936         // anchor might be deeper, should have same path then
937         for (size_t i = 0; i < cursor.size(); ++i)
938                 if (cursor[i].inset() != anchor[i].inset())
939                         return false;
940
941         // position should be ok.
942         return true;
943 }
944
945
946 void LCursor::setScreenPos(int x, int y)
947 {
948         bool res = bruteFind(x, y, formula()->xlow(), formula()->xhigh(),
949                 formula()->ylow(), formula()->yhigh());
950         if (!res) {
951                 // this can happen on creation of "math-display"
952                 idx() = 0;
953                 pos() = 0;
954         }
955         clearTargetX();
956 }
957
958
959
960 bool LCursor::home()
961 {
962         autocorrect() = false;
963         macroModeClose();
964         if (!inset()->idxHome(*this))
965                 return popLeft();
966         clearTargetX();
967         return true;
968 }
969
970
971 bool LCursor::end()
972 {
973         autocorrect() = false;
974         macroModeClose();
975         if (!inset()->idxEnd(*this))
976                 return popRight();
977         clearTargetX();
978         return true;
979 }
980
981
982 void LCursor::plainErase()
983 {
984         cell().erase(pos());
985 }
986
987
988 void LCursor::markInsert()
989 {
990         cell().insert(pos(), MathAtom(new MathCharInset(0)));
991 }
992
993
994 void LCursor::markErase()
995 {
996         cell().erase(pos());
997 }
998
999
1000 void LCursor::plainInsert(MathAtom const & t)
1001 {
1002         cell().insert(pos(), t);
1003         ++pos();
1004 }
1005
1006
1007 void LCursor::insert(string const & str)
1008 {
1009         lyxerr << "LCursor::insert str '" << str << "'" << endl;
1010         selClearOrDel();
1011 #if 0
1012         for (string::const_iterator it = str.begin(); it != str.end(); ++it)
1013                 plainInsert(MathAtom(new MathCharInset(*it)));
1014 #else
1015         MathArray ar;
1016         asArray(str, ar);
1017         insert(ar);
1018 #endif
1019 }
1020
1021
1022 void LCursor::insert(char c)
1023 {
1024         //lyxerr << "LCursor::insert char '" << c << "'" << endl;
1025         selClearOrDel();
1026         plainInsert(MathAtom(new MathCharInset(c)));
1027 }
1028
1029
1030 void LCursor::insert(MathAtom const & t)
1031 {
1032         //lyxerr << "LCursor::insert MathAtom: " << endl;
1033         macroModeClose();
1034         selClearOrDel();
1035         plainInsert(t);
1036 }
1037
1038
1039 void LCursor::insert(InsetBase * inset)
1040 {
1041         if (inMathed())
1042                 insert(MathAtom(inset));
1043         else
1044                 text()->insertInset(*this, inset);
1045 }
1046
1047
1048 void LCursor::niceInsert(string const & t)
1049 {
1050         MathArray ar;
1051         asArray(t, ar);
1052         if (ar.size() == 1)
1053                 niceInsert(ar[0]);
1054         else
1055                 insert(ar);
1056 }
1057
1058
1059 void LCursor::niceInsert(MathAtom const & t)
1060 {
1061         macroModeClose();
1062         string safe = grabAndEraseSelection();
1063         plainInsert(t);
1064         // enter the new inset and move the contents of the selection if possible
1065         if (t->isActive()) {
1066                 posLeft();
1067                 // be careful here: don't use 'pushLeft(t)' as this we need to
1068                 // push the clone, not the original
1069                 pushLeft(nextAtom().nucleus());
1070                 paste(safe);
1071         }
1072 }
1073
1074
1075 void LCursor::insert(MathArray const & ar)
1076 {
1077         macroModeClose();
1078         if (selection())
1079                 eraseSelection();
1080         cell().insert(pos(), ar);
1081         pos() += ar.size();
1082 }
1083
1084
1085 bool LCursor::backspace()
1086 {
1087         autocorrect() = false;
1088
1089         if (selection()) {
1090                 selDel();
1091                 return true;
1092         }
1093
1094         if (pos() == 0) {
1095                 if (inset()->nargs() == 1 && depth() == 1 && lastpos() == 0)
1096                         return false;
1097                 pullArg();
1098                 return true;
1099         }
1100
1101         if (inMacroMode()) {
1102                 MathUnknownInset * p = activeMacro();
1103                 if (p->name().size() > 1) {
1104                         p->setName(p->name().substr(0, p->name().size() - 1));
1105                         return true;
1106                 }
1107         }
1108
1109         if (pos() != 0 && prevAtom()->nargs() > 0) {
1110                 // let's require two backspaces for 'big stuff' and
1111                 // highlight on the first
1112                 selection() = true;
1113                 left();
1114         } else {
1115                 --pos();
1116                 plainErase();
1117         }
1118         return true;
1119 }
1120
1121
1122 bool LCursor::erase()
1123 {
1124         autocorrect() = false;
1125         if (inMacroMode())
1126                 return true;
1127
1128         if (selection()) {
1129                 selDel();
1130                 return true;
1131         }
1132
1133         // delete empty cells if possible
1134         if (pos() == lastpos() && inset()->idxDelete(idx()))
1135                 return true;
1136
1137         // special behaviour when in last position of cell
1138         if (pos() == lastpos()) {
1139                 bool one_cell = inset()->nargs() == 1;
1140                 if (one_cell && depth() == 1 && lastpos() == 0)
1141                         return false;
1142                 // remove markup
1143                 if (one_cell)
1144                         pullArg();
1145                 else
1146                         inset()->idxGlue(idx());
1147                 return true;
1148         }
1149
1150         if (pos() != lastpos() && inset()->nargs() > 0) {
1151                 selection() = true;
1152                 right();
1153         } else {
1154                 plainErase();
1155         }
1156
1157         return true;
1158 }
1159
1160
1161 bool LCursor::up()
1162 {
1163         macroModeClose();
1164         CursorBase save = cursor_;
1165         if (goUpDown(true))
1166                 return true;
1167         cursor_ = save;
1168         autocorrect() = false;
1169         return selection();
1170 }
1171
1172
1173 bool LCursor::down()
1174 {
1175         macroModeClose();
1176         CursorBase save = cursor_;
1177         if (goUpDown(false))
1178                 return true;
1179         cursor_ = save;
1180         autocorrect() = false;
1181         return selection();
1182 }
1183
1184
1185 void LCursor::macroModeClose()
1186 {
1187         if (!inMacroMode())
1188                 return;
1189         MathUnknownInset * p = activeMacro();
1190         p->finalize();
1191         string s = p->name();
1192         --pos();
1193         cell().erase(pos());
1194
1195         // do nothing if the macro name is empty
1196         if (s == "\\")
1197                 return;
1198
1199         string const name = s.substr(1);
1200
1201         // prevent entering of recursive macros
1202         if (formula()->lyxCode() == InsetOld::MATHMACRO_CODE
1203                         && formula()->getInsetName() == name)
1204                 lyxerr << "can't enter recursive macro" << endl;
1205
1206         niceInsert(createMathInset(name));
1207 }
1208
1209
1210 string LCursor::macroName()
1211 {
1212         return inMacroMode() ? activeMacro()->name() : string();
1213 }
1214
1215
1216 void LCursor::handleNest(MathAtom const & a, int c)
1217 {
1218         //lyxerr << "LCursor::handleNest: " << c << endl;
1219         MathAtom t = a;
1220         asArray(grabAndEraseSelection(), t.nucleus()->cell(c));
1221         insert(t);
1222         posLeft();
1223         pushLeft(nextAtom().nucleus());
1224 }
1225
1226
1227 int LCursor::targetX() const
1228 {
1229         if (x_target() != -1)
1230                 return x_target();
1231         int x = 0;
1232         int y = 0;
1233         getPos(x, y);
1234         return x;
1235 }
1236
1237
1238 MathHullInset * LCursor::formula() const
1239 {
1240         for (int i = cursor_.size() - 1; i >= 1; --i) {
1241                 MathInset * inset = cursor_[i].inset()->asMathInset();
1242                 if (inset && inset->asHullInset())
1243                         return static_cast<MathHullInset *>(inset);
1244         }
1245         return 0;
1246 }
1247
1248
1249 void LCursor::adjust(pos_type from, int diff)
1250 {
1251         if (pos() > from)
1252                 pos() += diff;
1253         if (anchor().pos_ > from)
1254                 anchor().pos_ += diff;
1255         // just to be on the safe side
1256         // theoretically unecessary
1257         normalize();
1258 }
1259
1260
1261 bool LCursor::inMacroMode() const
1262 {
1263         if (!pos() != 0)
1264                 return false;
1265         MathUnknownInset const * p = prevAtom()->asUnknownInset();
1266         return p && !p->final();
1267 }
1268
1269
1270 MathUnknownInset * LCursor::activeMacro()
1271 {
1272         return inMacroMode() ? prevAtom().nucleus()->asUnknownInset() : 0;
1273 }
1274
1275
1276 bool LCursor::inMacroArgMode() const
1277 {
1278         return pos() > 0 && prevAtom()->getChar() == '#';
1279 }
1280
1281
1282 MathGridInset * LCursor::enclosingGrid(idx_type & idx) const
1283 {
1284         for (MathInset::difference_type i = depth() - 1; i >= 0; --i) {
1285                 MathInset * m = cursor_[i].inset()->asMathInset();
1286                 if (!m)
1287                         return 0;
1288                 MathGridInset * p = m->asGridInset();
1289                 if (p) {
1290                         idx = cursor_[i].idx_;
1291                         return p;
1292                 }
1293         }
1294         return 0;
1295 }
1296
1297
1298 void LCursor::pullArg()
1299 {
1300 #warning Look here
1301         MathArray ar = cell();
1302         if (popLeft() && inMathed()) {
1303                 plainErase();
1304                 cell().insert(pos(), ar);
1305                 resetAnchor();
1306         } else {
1307                 //formula()->mutateToText();
1308         }
1309 }
1310
1311
1312 void LCursor::touch()
1313 {
1314 #warning look here
1315 #if 0
1316         CursorBase::const_iterator it = cursor_.begin();
1317         CursorBase::const_iterator et = cursor_.end();
1318         for ( ; it != et; ++it)
1319                 it->cell().touch();
1320 #endif
1321 }
1322
1323
1324 void LCursor::normalize()
1325 {
1326         if (idx() >= nargs()) {
1327                 lyxerr << "this should not really happen - 1: "
1328                        << idx() << ' ' << nargs()
1329                        << " in: " << inset() << endl;
1330         }
1331         idx() = min(idx(), lastidx());
1332
1333         if (pos() > lastpos()) {
1334                 lyxerr << "this should not really happen - 2: "
1335                         << pos() << ' ' << lastpos() <<  " in idx: " << idx()
1336                        << " in atom: '";
1337                 WriteStream wi(lyxerr, false, true);
1338                 inset()->asMathInset()->write(wi);
1339                 lyxerr << endl;
1340         }
1341         pos() = min(pos(), lastpos());
1342 }
1343
1344
1345 char LCursor::valign()
1346 {
1347         idx_type idx;
1348         MathGridInset * p = enclosingGrid(idx);
1349         return p ? p->valign() : '\0';
1350 }
1351
1352
1353 char LCursor::halign()
1354 {
1355         idx_type idx;
1356         MathGridInset * p = enclosingGrid(idx);
1357         return p ? p->halign(idx % p->ncols()) : '\0';
1358 }
1359
1360
1361 bool LCursor::goUpDown(bool up)
1362 {
1363         // Be warned: The 'logic' implemented in this function is highly
1364         // fragile. A distance of one pixel or a '<' vs '<=' _really
1365         // matters. So fiddle around with it only if you think you know
1366         // what you are doing!
1367   int xo = 0;
1368         int yo = 0;
1369         getPos(xo, yo);
1370
1371         // check if we had something else in mind, if not, this is the future goal
1372         if (x_target() == -1)
1373                 x_target() = xo;
1374         else
1375                 xo = x_target();
1376
1377         // try neigbouring script insets
1378         if (!selection()) {
1379                 // try left
1380                 if (pos() != 0) {
1381                         MathScriptInset const * p = prevAtom()->asScriptInset();
1382                         if (p && p->has(up)) {
1383                                 --pos();
1384                                 push(inset());
1385                                 idx() = up; // the superscript has index 1
1386                                 pos() = lastpos();
1387                                 //lyxerr << "updown: handled by scriptinset to the left" << endl;
1388                                 return true;
1389                         }
1390                 }
1391
1392                 // try right
1393                 if (pos() != lastpos()) {
1394                         MathScriptInset const * p = nextAtom()->asScriptInset();
1395                         if (p && p->has(up)) {
1396                                 push(inset());
1397                                 idx() = up;
1398                                 pos() = 0;
1399                                 //lyxerr << "updown: handled by scriptinset to the right" << endl;
1400                                 return true;
1401                         }
1402                 }
1403         }
1404
1405         // try current cell for e.g. text insets
1406         if (inset()->idxUpDown2(*this, up))
1407                 return true;
1408
1409         //xarray().boundingBox(xlow, xhigh, ylow, yhigh);
1410         //if (up)
1411         //      yhigh = yo - 4;
1412         //else
1413         //      ylow = yo + 4;
1414         //if (bruteFind(xo, yo, xlow, xhigh, ylow, yhigh)) {
1415         //      lyxerr << "updown: handled by brute find in the same cell" << endl;
1416         //      return true;
1417         //}
1418
1419         // try to find an inset that knows better then we
1420         while (1) {
1421                 //lyxerr << "updown: We are in " << inset() << " idx: " << idx() << endl;
1422                 // ask inset first
1423                 if (inset()->idxUpDown(*this, up)) {
1424                         // try to find best position within this inset
1425                         if (!selection())
1426                                 bruteFind2(xo, yo);
1427                         return true;
1428                 }
1429
1430                 // no such inset found, just take something "above"
1431                 //lyxerr << "updown: handled by strange case" << endl;
1432                 if (!popLeft()) {
1433                         return
1434                                 bruteFind(xo, yo,
1435                                         formula()->xlow(),
1436                                         formula()->xhigh(),
1437                                         up ? formula()->ylow() : yo + 4,
1438                                         up ? yo - 4 : formula()->yhigh()
1439                                 );
1440                 }
1441
1442                 // any improvement so far?
1443                 int xnew, ynew;
1444                 getPos(xnew, ynew);
1445                 if (up ? ynew < yo : ynew > yo)
1446                         return true;
1447         }
1448 }
1449
1450
1451 bool LCursor::bruteFind(int x, int y, int xlow, int xhigh, int ylow, int yhigh)
1452 {
1453         CursorBase best_cursor;
1454         double best_dist = 1e10;
1455
1456         CursorBase it = ibegin(formula());
1457         CursorBase et = iend(formula());
1458         while (1) {
1459                 // avoid invalid nesting when selecting
1460                 if (!selection() || positionable(it, anchor_)) {
1461                         int xo, yo;
1462                         CursorSlice & cur = it.back();
1463                         cur.inset()->getCursorPos(cur, xo, yo);
1464                         if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) {
1465                                 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
1466                                 //lyxerr << "x: " << x << " y: " << y << " d: " << endl;
1467                                 // '<=' in order to take the last possible position
1468                                 // this is important for clicking behind \sum in e.g. '\sum_i a'
1469                                 if (d <= best_dist) {
1470                                         best_dist   = d;
1471                                         best_cursor = it;
1472                                 }
1473                         }
1474                 }
1475
1476                 if (it == et)
1477                         break;
1478                 increment(it);
1479         }
1480
1481         if (best_dist < 1e10)
1482                 cursor_ = best_cursor;
1483         return best_dist < 1e10;
1484 }
1485
1486
1487 void LCursor::bruteFind2(int x, int y)
1488 {
1489         double best_dist = 1e10;
1490
1491         CursorBase it = cursor_;
1492         it.back().pos() = 0;
1493         CursorBase et = cursor_;
1494         et.back().pos() = et.back().asMathInset()->cell(et.back().idx_).size();
1495         for (int i = 0; ; ++i) {
1496                 int xo, yo;
1497                 CursorSlice & cur = it.back();
1498                 cur.inset()->getCursorPos(cur, xo, yo);
1499                 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
1500                 // '<=' in order to take the last possible position
1501                 // this is important for clicking behind \sum in e.g. '\sum_i a'
1502                 lyxerr << "i: " << i << " d: " << d << " best: " << best_dist << endl;
1503                 if (d <= best_dist) {
1504                         best_dist = d;
1505                         cursor_ = it;
1506                 }
1507                 if (it == et)
1508                         break;
1509                 increment(it);
1510         }
1511 }
1512
1513
1514 bool LCursor::idxLineLast()
1515 {
1516         idx() -= idx() % ncols();
1517         idx() += ncols() - 1;
1518         pos() = lastpos();
1519         return true;
1520 }
1521
1522
1523 bool LCursor::idxLeft()
1524 {
1525         return inset()->idxLeft(*this);
1526 }
1527
1528
1529 bool LCursor::idxRight()
1530 {
1531         return inset()->idxRight(*this);
1532 }
1533
1534
1535 bool LCursor::script(bool up)
1536 {
1537         // Hack to get \\^ and \\_ working
1538         lyxerr << "handling script: up: " << up << endl;
1539         if (inMacroMode() && macroName() == "\\") {
1540                 if (up)
1541                         niceInsert(createMathInset("mathcircumflex"));
1542                 else
1543                         interpret('_');
1544                 return true;
1545         }
1546
1547         macroModeClose();
1548         string safe = grabAndEraseSelection();
1549         if (inNucleus()) {
1550                 // we are in a nucleus of a script inset, move to _our_ script
1551                 inset()->asMathInset()->asScriptInset()->ensure(up);
1552                 idx() = up;
1553                 pos() = 0;
1554         } else if (pos() != 0 && prevAtom()->asScriptInset()) {
1555                 --pos();
1556                 nextAtom().nucleus()->asScriptInset()->ensure(up);
1557                 push(nextInset());
1558                 idx() = up;
1559                 pos() = lastpos();
1560         } else if (pos() != 0) {
1561                 --pos();
1562                 cell()[pos()] = MathAtom(new MathScriptInset(nextAtom(), up));
1563                 push(nextInset());
1564                 idx() = up;
1565                 pos() = 0;
1566         } else {
1567                 plainInsert(MathAtom(new MathScriptInset(up)));
1568                 --pos();
1569                 nextAtom().nucleus()->asScriptInset()->ensure(up);
1570                 push(nextInset());
1571                 idx() = up;
1572                 pos() = 0;
1573         }
1574         paste(safe);
1575         return true;
1576 }
1577
1578
1579 bool LCursor::interpret(char c)
1580 {
1581         //lyxerr << "interpret 2: '" << c << "'" << endl;
1582         clearTargetX();
1583         if (inMacroArgMode()) {
1584                 posLeft();
1585                 plainErase();
1586 #warning FIXME
1587 #if 0
1588                 int n = c - '0';
1589                 MathMacroTemplate const * p = formula()->asMacroTemplate();
1590                 if (p && 1 <= n && n <= p->numargs())
1591                         insert(MathAtom(new MathMacroArgument(c - '0')));
1592                 else {
1593                         insert(createMathInset("#"));
1594                         interpret(c); // try again
1595                 }
1596 #endif
1597                 return true;
1598         }
1599
1600         // handle macroMode
1601         if (inMacroMode()) {
1602                 string name = macroName();
1603                 //lyxerr << "interpret name: '" << name << "'" << endl;
1604
1605                 if (isalpha(c)) {
1606                         activeMacro()->setName(activeMacro()->name() + c);
1607                         return true;
1608                 }
1609
1610                 // handle 'special char' macros
1611                 if (name == "\\") {
1612                         // remove the '\\'
1613                         backspace();
1614                         if (c == '\\') {
1615                                 if (currentMode() == MathInset::TEXT_MODE)
1616                                         niceInsert(createMathInset("textbackslash"));
1617                                 else
1618                                         niceInsert(createMathInset("backslash"));
1619                         } else if (c == '{') {
1620                                 niceInsert(MathAtom(new MathBraceInset));
1621                         } else {
1622                                 niceInsert(createMathInset(string(1, c)));
1623                         }
1624                         return true;
1625                 }
1626
1627                 // leave macro mode and try again if necessary
1628                 macroModeClose();
1629                 if (c == '{')
1630                         niceInsert(MathAtom(new MathBraceInset));
1631                 else if (c != ' ')
1632                         interpret(c);
1633                 return true;
1634         }
1635
1636         // This is annoying as one has to press <space> far too often.
1637         // Disable it.
1638
1639         if (0) {
1640                 // leave autocorrect mode if necessary
1641                 if (autocorrect() && c == ' ') {
1642                         autocorrect() = false;
1643                         return true;
1644                 }
1645         }
1646
1647         // just clear selection on pressing the space bar
1648         if (selection() && c == ' ') {
1649                 selection() = false;
1650                 return true;
1651         }
1652
1653         selClearOrDel();
1654
1655         if (c == '\\') {
1656                 //lyxerr << "starting with macro" << endl;
1657                 insert(MathAtom(new MathUnknownInset("\\", false)));
1658                 return true;
1659         }
1660
1661         if (c == '\n') {
1662                 if (currentMode() == MathInset::TEXT_MODE)
1663                         insert(c);
1664                 return true;
1665         }
1666
1667         if (c == ' ') {
1668                 if (currentMode() == MathInset::TEXT_MODE) {
1669                         // insert spaces in text mode,
1670                         // but suppress direct insertion of two spaces in a row
1671                         // the still allows typing  '<space>a<space>' and deleting the 'a', but
1672                         // it is better than nothing...
1673                         if (!pos() != 0 || prevAtom()->getChar() != ' ')
1674                                 insert(c);
1675                         return true;
1676                 }
1677                 if (pos() != 0 && prevAtom()->asSpaceInset()) {
1678                         prevAtom().nucleus()->asSpaceInset()->incSpace();
1679                         return true;
1680                 }
1681                 if (popRight())
1682                         return true;
1683                 // if are at the very end, leave the formula
1684                 return pos() != lastpos();
1685         }
1686
1687         if (c == '_') {
1688                 script(false);
1689                 return true;
1690         }
1691
1692         if (c == '^') {
1693                 script(true);
1694                 return true;
1695         }
1696
1697         if (c == '{' || c == '}' || c == '#' || c == '&' || c == '$') {
1698                 niceInsert(createMathInset(string(1, c)));
1699                 return true;
1700         }
1701
1702         if (c == '%') {
1703                 niceInsert(MathAtom(new MathCommentInset));
1704                 return true;
1705         }
1706
1707         // try auto-correction
1708         //if (autocorrect() && hasPrevAtom() && math_autocorrect(prevAtom(), c))
1709         //      return true;
1710
1711         // no special circumstances, so insert the character without any fuss
1712         insert(c);
1713         autocorrect() = true;
1714         return true;
1715 }
1716
1717
1718 void LCursor::lockToggle()
1719 {
1720         if (pos() != lastpos()) {
1721                 // toggle previous inset ...
1722                 nextAtom().nucleus()->lock(!nextAtom()->lock());
1723         } else if (popLeft() && pos() != lastpos()) {
1724                 // ... or enclosing inset if we are in the last inset position
1725                 nextAtom().nucleus()->lock(!nextAtom()->lock());
1726                 ++pos();
1727         }
1728 }
1729
1730
1731 CursorSlice LCursor::normalAnchor()
1732 {
1733         if (anchor_.size() < depth()) {
1734                 resetAnchor();
1735                 lyxerr << "unusual Anchor size" << endl;
1736         }
1737         //lyx::BOOST_ASSERT(Anchor_.size() >= cursor.depth());
1738         // use Anchor on the same level as Cursor
1739         CursorSlice normal = anchor_[current_];
1740 #if 0
1741         if (depth() < anchor_.size() && !(normal < xx())) {
1742                 // anchor is behind cursor -> move anchor behind the inset
1743                 ++normal.pos_;
1744         }
1745 #endif
1746         return normal;
1747 }
1748
1749
1750 /*
1751 DispatchResult dispatch(LCursor & cur, FuncRequest const & cmd)
1752 {
1753         // mouse clicks are somewhat special
1754         // check
1755         switch (cmd.action) {
1756         case LFUN_MOUSE_PRESS:
1757         case LFUN_MOUSE_MOTION:
1758         case LFUN_MOUSE_RELEASE:
1759         case LFUN_MOUSE_DOUBLE: {
1760                 CursorSlice & pos = cursor_.back();
1761                 int x = 0;
1762                 int y = 0;
1763                 getPos(x, y);
1764                 if (x < cmd.x && pos() != 0) {
1765                         DispatchResult const res = prevAtom().nucleus()->dispatch(cmd);
1766                         if (res.dispatched())
1767                                 return res;
1768                 }
1769                 if (x > cmd.x && pos() != lastpos()) {
1770                         DispatchResult const res = inset()->dispatch(cmd);
1771                         if (res.dispatched())
1772                                 return res;
1773                 }
1774         }
1775         default:
1776         break;
1777         }
1778 }
1779 */
1780
1781
1782 void LCursor::handleFont(string const & font)
1783 {
1784         lyxerr << "LCursor::handleFont: " << font << endl;
1785         string safe;
1786         if (selection()) {
1787                 macroModeClose();
1788                 safe = grabAndEraseSelection();
1789         }
1790
1791         if (lastpos() != 0) {
1792                 // something left in the cell
1793                 if (pos() == 0) {
1794                         // cursor in first position
1795                         popLeft();
1796                 } else if (pos() == lastpos()) {
1797                         // cursor in last position
1798                         popRight();
1799                 } else {
1800                         // cursor in between. split cell
1801                         MathArray::iterator bt = cell().begin();
1802                         MathAtom at = createMathInset(font);
1803                         at.nucleus()->cell(0) = MathArray(bt, bt + pos());
1804                         cell().erase(bt, bt + pos());
1805                         popLeft();
1806                         plainInsert(at);
1807                 }
1808         } else {
1809                 // nothing left in the cell
1810                 pullArg();
1811                 plainErase();
1812         }
1813         insert(safe);
1814 }
1815
1816
1817 bool LCursor::inMathed() const
1818 {
1819         return formula();
1820 }
1821
1822
1823 bool LCursor::inTexted() const
1824 {
1825         return !formula();
1826 }
1827
1828
1829 InsetBase * LCursor::nextInset()
1830 {
1831         if (pos() == lastpos())
1832                 return 0;
1833         if (inMathed()) 
1834                 return nextAtom().nucleus();
1835         return paragraph().isInset(pos()) ? paragraph().getInset(pos()) : 0;
1836 }
1837
1838
1839 InsetBase * LCursor::prevInset()
1840 {
1841         if (pos() == 0)
1842                 return 0;
1843         if (inMathed()) 
1844                 return prevAtom().nucleus();
1845         return paragraph().isInset(pos() - 1) ? paragraph().getInset(pos() - 1) : 0;
1846 }
1847
1848
1849 InsetBase const * LCursor::prevInset() const
1850 {
1851         if (pos() == 0)
1852                 return 0;
1853         if (inMathed()) 
1854                 return prevAtom().nucleus();
1855         return paragraph().isInset(pos() - 1) ? paragraph().getInset(pos() - 1) : 0;
1856 }
1857
1858
1859 void LCursor::message(string const & msg) const
1860 {
1861         bv().owner()->getLyXFunc().setMessage(msg);
1862 }
1863
1864
1865 void LCursor::errorMessage(string const & msg) const
1866 {
1867         bv().owner()->getLyXFunc().setErrorMessage(msg);
1868 }
1869
1870
1871 string LCursor::selectionAsString(bool label) const
1872 {
1873         if (!selection())
1874                 return string();
1875
1876         if (inTexted()) {
1877                 Buffer const & buffer = *bv().buffer();
1878
1879                 // should be const ...
1880                 ParagraphList::iterator startpit = text()->getPar(selBegin());
1881                 ParagraphList::iterator endpit = text()->getPar(selEnd());
1882                 size_t const startpos = selBegin().pos();
1883                 size_t const endpos = selEnd().pos();
1884
1885                 if (startpit == endpit)
1886                         return startpit->asString(buffer, startpos, endpos, label);
1887
1888                 // First paragraph in selection
1889                 string result =
1890                         startpit->asString(buffer, startpos, startpit->size(), label) + "\n\n";
1891
1892                 // The paragraphs in between (if any)
1893                 ParagraphList::iterator pit = startpit;
1894                 for (++pit; pit != endpit; ++pit)
1895                         result += pit->asString(buffer, 0, pit->size(), label) + "\n\n";
1896
1897                 // Last paragraph in selection
1898                 result += endpit->asString(buffer, 0, endpos, label);
1899
1900                 return result;
1901         }
1902
1903 #warning an mathed?
1904         return string();
1905 }
1906
1907
1908 string LCursor::currentState()
1909 {
1910         if (inMathed()) {
1911                 std::ostringstream os;
1912                 info(os);
1913                 return os.str();
1914         }
1915         return text() ? text()->currentState(*this) : string();
1916 }
1917
1918
1919 // only used by the spellchecker
1920 void LCursor::replaceWord(string const & replacestring)
1921 {
1922         LyXText * t = text();
1923         BOOST_ASSERT(t);
1924
1925         t->replaceSelectionWithString(*this, replacestring);
1926         t->setSelectionRange(*this, replacestring.length());
1927
1928         // Go back so that replacement string is also spellchecked
1929         for (string::size_type i = 0; i < replacestring.length() + 1; ++i)
1930                 t->cursorLeft(*this, true);
1931 }
1932
1933
1934 void LCursor::update()
1935 {
1936         bv().update();
1937 }
1938
1939
1940 string LCursor::getPossibleLabel()
1941 {
1942         return inMathed() ? "eq:" : text()->getPossibleLabel(*this);
1943 }
1944
1945
1946 void LCursor::notdispatched()
1947 {
1948         disp_.dispatched(false);
1949 }
1950
1951
1952 void LCursor::dispatched(dispatch_result_t res)
1953 {
1954         disp_.val(res);
1955 }
1956
1957
1958 void LCursor::noupdate()
1959 {
1960         disp_.update(false);
1961 }