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