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