]> git.lyx.org Git - lyx.git/blob - src/mathed/math_cursor.C
"Better" font handling. I've not found a cause for John's crash, but I'd
[lyx.git] / src / mathed / math_cursor.C
1 /*
2  *  File:        math_cursor.C
3  *  Purpose:     Interaction for mathed
4  *  Author:      Alejandro Aguilar Sierra <asierra@servidor.unam.mx>
5  *  Created:     January 1996
6  *  Description: Math interaction for a WYSIWYG math editor.
7  *
8  *  Dependencies: Xlib, XForms
9  *
10  *  Copyright: 1996, Alejandro Aguilar Sierra
11  *
12  *   Version: 0.8beta, Math & Lyx project.
13  *
14  *   You are free to use and modify this code under the terms of
15  *   the GNU General Public Licence version 2 or later.
16  */
17
18 #include <config.h>
19 #include <lyxrc.h>
20
21 #ifdef __GNUG__
22 #pragma implementation
23 #endif
24
25 #include "support/lstrings.h"
26 #include "support/LAssert.h"
27 #include "debug.h"
28 #include "frontends/Painter.h"
29 #include "math_cursor.h"
30 #include "formulabase.h"
31 #include "funcrequest.h"
32 #include "math_autocorrect.h"
33 #include "math_braceinset.h"
34 #include "math_commentinset.h"
35 #include "math_charinset.h"
36 #include "math_extern.h"
37 #include "math_factory.h"
38 #include "math_fontinset.h"
39 #include "math_gridinset.h"
40 #include "math_iterator.h"
41 #include "math_macroarg.h"
42 #include "math_macrotemplate.h"
43 #include "math_mathmlstream.h"
44 #include "math_scriptinset.h"
45 #include "math_spaceinset.h"
46 #include "math_support.h"
47 #include "math_unknowninset.h"
48
49 #include <algorithm>
50 #include <cctype>
51
52 //#define FILEDEBUG 1
53
54 using std::endl;
55 using std::min;
56 using std::max;
57 using std::swap;
58 using std::vector;
59 using std::ostringstream;
60 using std::isalpha;
61
62
63 // matheds own cut buffer
64 string theCutBuffer;
65
66
67 MathCursor::MathCursor(InsetFormulaBase * formula, bool front)
68         :       formula_(formula), autocorrect_(false), selection_(false)
69 {
70         front ? first() : last();
71         Anchor_ = Cursor_;
72 }
73
74
75 MathCursor::~MathCursor()
76 {
77   // ensure that 'notifyCursorLeave' is called
78   while (popLeft())
79     ;
80 }
81
82
83 void MathCursor::push(MathAtom & t)
84 {
85         Cursor_.push_back(MathCursorPos(t.nucleus()));
86 }
87
88
89 void MathCursor::pushLeft(MathAtom & t)
90 {
91         //lyxerr << "Entering atom " << t << " left\n";
92         push(t);
93         t->idxFirst(idx(), pos());
94 }
95
96
97 void MathCursor::pushRight(MathAtom & t)
98 {
99         //lyxerr << "Entering atom " << t << " right\n";
100         posLeft();
101         push(t);
102         t->idxLast(idx(), pos());
103 }
104
105
106 bool MathCursor::popLeft()
107 {
108         //cerr << "Leaving atom to the left\n";
109         if (depth() <= 1) {
110                 if (depth() == 1)
111                         par()->notifyCursorLeaves(idx());
112                 return false;
113         }
114         par()->notifyCursorLeaves(idx());
115         Cursor_.pop_back();
116         return true;
117 }
118
119
120 bool MathCursor::popRight()
121 {
122         //cerr << "Leaving atom "; par()->write(cerr, false); cerr << " right\n";
123         if (depth() <= 1) {
124                 if (depth() == 1)
125                         par()->notifyCursorLeaves(idx());
126                 return false;
127         }
128         par()->notifyCursorLeaves(idx());
129         Cursor_.pop_back();
130         posRight();
131         return true;
132 }
133
134
135
136 #if FILEDEBUG
137         void MathCursor::dump(char const * what) const
138         {
139                 lyxerr << "MC: " << what << "\n";
140                 lyxerr << " Cursor: " << depth() << "\n";
141                 for (unsigned i = 0; i < depth(); ++i)
142                         lyxerr << "    i: " << i << " " << Cursor_[i] << "\n";
143                 lyxerr << " Anchor: " << Anchor_.size() << "\n";
144                 for (unsigned i = 0; i < Anchor_.size(); ++i)
145                         lyxerr << "    i: " << i << " " << Anchor_[i] << "\n";
146                 lyxerr  << " sel: " << selection_ << "\n";
147         }
148 #else
149         void MathCursor::dump(char const *) const {}
150 #endif
151
152
153 bool MathCursor::isInside(MathInset const * p) const
154 {
155         for (unsigned i = 0; i < depth(); ++i)
156                 if (Cursor_[i].par_ == p)
157                         return true;
158         return false;
159 }
160
161
162 bool MathCursor::openable(MathAtom const & t, bool sel) const
163 {
164         if (!t->isActive())
165                 return false;
166
167         if (t->lock())
168                 return false;
169
170         if (sel) {
171                 // we can't move into anything new during selection
172                 if (depth() == Anchor_.size())
173                         return false;
174                 if (t.operator->() != Anchor_[depth()].par_)
175                         return false;
176         }
177         return true;
178 }
179
180
181 bool MathCursor::inNucleus() const
182 {
183         return par()->asScriptInset() && idx() == 2;
184 }
185
186
187 bool MathCursor::posLeft()
188 {
189         if (pos() == 0)
190                 return false;
191         --pos();
192         return true;
193 }
194
195
196 bool MathCursor::posRight()
197 {
198         if (pos() == size())
199                 return false;
200         ++pos();
201         return true;
202 }
203
204
205 bool MathCursor::left(bool sel)
206 {
207         dump("Left 1");
208         autocorrect_ = false;
209         targetx_ = -1; // "no target"
210         if (inMacroMode()) {
211                 macroModeClose();
212                 return true;
213         }
214         selHandle(sel);
215
216         if (hasPrevAtom() && openable(prevAtom(), sel)) {
217                 pushRight(prevAtom());
218                 return true;
219         }
220
221         return posLeft() || idxLeft() || popLeft() || selection_;
222 }
223
224
225 bool MathCursor::right(bool sel)
226 {
227         dump("Right 1");
228         autocorrect_ = false;
229         targetx_ = -1; // "no target"
230         if (inMacroMode()) {
231                 macroModeClose();
232                 return true;
233         }
234         selHandle(sel);
235
236         if (hasNextAtom() && openable(nextAtom(), sel)) {
237                 pushLeft(nextAtom());
238                 return true;
239         }
240
241         return posRight() || idxRight() || popRight() || selection_;
242 }
243
244
245 void MathCursor::first()
246 {
247         Cursor_.clear();
248         push(formula_->par());
249         par()->idxFirst(idx(), pos());
250 }
251
252
253 void MathCursor::last()
254 {
255         Cursor_.clear();
256         push(formula_->par());
257         par()->idxLast(idx(), pos());
258 }
259
260
261 bool positionable
262         (MathIterator const & cursor, MathIterator const & anchor)
263 {
264         // avoid deeper nested insets when selecting
265         if (cursor.size() > anchor.size())
266                 return false;
267
268         // anchor might be deeper, should have same path then
269         for (MathIterator::size_type i = 0; i < cursor.size(); ++i)
270                 if (cursor[i].par_ != anchor[i].par_)
271                         return false;
272
273         // position should be ok.
274         return true;
275 }
276
277
278 void MathCursor::setPos(int x, int y)
279 {
280         dump("setPos 1");
281         bool res = bruteFind(x, y,
282                 formula()->xlow(), formula()->xhigh(),
283                 formula()->ylow(), formula()->yhigh());
284         if (!res) {
285                 // this can happen on creation of "math-display"
286                 dump("setPos 1.5");
287                 first();
288         }
289         targetx_ = -1; // "no target"
290         dump("setPos 2");
291 }
292
293
294
295 bool MathCursor::home(bool sel)
296 {
297         dump("home 1");
298         autocorrect_ = false;
299         selHandle(sel);
300         macroModeClose();
301         if (!par()->idxHome(idx(), pos()))
302                 return popLeft();
303         dump("home 2");
304         targetx_ = -1; // "no target"
305         return true;
306 }
307
308
309 bool MathCursor::end(bool sel)
310 {
311         dump("end 1");
312         autocorrect_ = false;
313         selHandle(sel);
314         macroModeClose();
315         if (!par()->idxEnd(idx(), pos()))
316                 return popRight();
317         dump("end 2");
318         targetx_ = -1; // "no target"
319         return true;
320 }
321
322
323 void MathCursor::plainErase()
324 {
325         array().erase(pos());
326 }
327
328
329 void MathCursor::markInsert()
330 {
331         //lyxerr << "inserting mark\n";
332         array().insert(pos(), MathAtom(new MathCharInset(0)));
333 }
334
335
336 void MathCursor::markErase()
337 {
338         //lyxerr << "deleting mark\n";
339         array().erase(pos());
340 }
341
342
343 void MathCursor::plainInsert(MathAtom const & t)
344 {
345         dump("plainInsert");
346         array().insert(pos(), t);
347         ++pos();
348 }
349
350
351 void MathCursor::insert(string const & str)
352 {
353         //lyxerr << "inserting '" << str << "'\n";
354         selClearOrDel();
355         for (string::const_iterator it = str.begin(); it != str.end(); ++it)
356                 plainInsert(MathAtom(new MathCharInset(*it)));
357 }
358
359
360 void MathCursor::insert(char c)
361 {
362         //lyxerr << "inserting '" << c << "'\n";
363         selClearOrDel();
364         plainInsert(MathAtom(new MathCharInset(c)));
365 }
366
367
368 void MathCursor::insert(MathAtom const & t)
369 {
370         macroModeClose();
371         selClearOrDel();
372         plainInsert(t);
373 }
374
375
376 void MathCursor::niceInsert(string const & t)
377 {
378         MathArray ar = asArray(t);
379         if (ar.size() == 1)
380                 niceInsert(ar[0]);
381         else 
382                 insert(ar);
383 }
384
385
386 void MathCursor::niceInsert(MathAtom const & t)
387 {
388         macroModeClose();
389         string safe = grabAndEraseSelection();
390         plainInsert(t);
391         // enter the new inset and move the contents of the selection if possible
392         if (t->isActive()) {
393                 posLeft();
394                 pushLeft(nextAtom());
395                 paste(safe);
396         }
397 }
398
399
400 void MathCursor::insert(MathArray const & ar)
401 {
402         macroModeClose();
403         if (selection_)
404                 eraseSelection();
405         array().insert(pos(), ar);
406         pos() += ar.size();
407 }
408
409
410 void MathCursor::paste(string const & data)
411 {
412         dispatch(FuncRequest(LFUN_PASTE, data));
413 }
414
415
416 void MathCursor::backspace()
417 {
418         autocorrect_ = false;
419
420         if (selection_) {
421                 selDel();
422                 return;
423         }
424
425         if (pos() == 0) {
426                 pullArg();
427                 return;
428         }
429
430         if (inMacroMode()) {
431                 MathUnknownInset * p = activeMacro();
432                 if (p->name().size() > 1) {
433                         p->setName(p->name().substr(0, p->name().size() - 1));
434                         return;
435                 }
436         }
437
438         --pos();
439         plainErase();
440 }
441
442
443 void MathCursor::erase()
444 {
445         autocorrect_ = false;
446         if (inMacroMode())
447                 return;
448
449         if (selection_) {
450                 selDel();
451                 return;
452         }
453
454         // delete empty cells if possible
455         if (array().empty())
456                 if (par()->idxDelete(idx()))
457                         return;
458
459         // old behaviour when in last position of cell
460         if (pos() == size()) {
461                 par()->idxGlue(idx());
462                 return;
463         }
464
465         plainErase();
466 }
467
468
469 bool MathCursor::up(bool sel)
470 {
471         dump("up 1");
472         macroModeClose();
473         selHandle(sel);
474         MathIterator save = Cursor_;
475         if (goUpDown(true))
476                 return true;
477         Cursor_ = save;
478         autocorrect_ = false;
479         return selection_;
480 }
481
482
483 bool MathCursor::down(bool sel)
484 {
485         dump("down 1");
486         macroModeClose();
487         selHandle(sel);
488         MathIterator save = Cursor_;
489         if (goUpDown(false))
490                 return true;
491         Cursor_ = save;
492         autocorrect_ = false;
493         return selection_;
494 }
495
496
497 void MathCursor::macroModeClose()
498 {
499         if (!inMacroMode())
500                 return;
501         MathUnknownInset * p = activeMacro();
502         p->finalize();
503         string s = p->name();
504         --pos();
505         array().erase(pos());
506
507         // do nothing if the macro name is empty
508         if (s == "\\")
509                 return;
510
511         string name = s.substr(1);
512
513         // prevent entering of recursive macros
514         if (formula()->lyxCode() == Inset::MATHMACRO_CODE
515                         && formula()->getInsetName() == name)
516                 lyxerr << "can't enter recursive macro\n";
517
518         niceInsert(createMathInset(name));
519 }
520
521
522 string MathCursor::macroName() const
523 {
524         return inMacroMode() ? activeMacro()->name() : string();
525 }
526
527
528 void MathCursor::selClear()
529 {
530         Anchor_.clear();
531         selection_ = false;
532 }
533
534
535 void MathCursor::selCopy()
536 {
537         dump("selCopy");
538         if (selection_) {
539                 theCutBuffer = grabSelection();
540                 selection_ = false;
541         } else {
542                 theCutBuffer.erase();
543         }
544 }
545
546
547 void MathCursor::selCut()
548 {
549         dump("selCut");
550         theCutBuffer = grabAndEraseSelection();
551 }
552
553
554 void MathCursor::selDel()
555 {
556         dump("selDel");
557         if (selection_) {
558                 eraseSelection();
559                 selection_ = false;
560         }
561 }
562
563
564 void MathCursor::selPaste()
565 {
566         dump("selPaste");
567         selClearOrDel();
568         paste(theCutBuffer);
569         //grabSelection();
570         selection_ = false;
571 }
572
573
574 void MathCursor::selHandle(bool sel)
575 {
576         if (sel == selection_)
577                 return;
578         //clear();
579         Anchor_ = Cursor_;
580         selection_ = sel;
581 }
582
583
584 void MathCursor::selStart()
585 {
586         dump("selStart 1");
587         //clear();
588         Anchor_ = Cursor_;
589         selection_ = true;
590         dump("selStart 2");
591 }
592
593
594 void MathCursor::selClearOrDel()
595 {
596         if (lyxrc.auto_region_delete)
597                 selDel();
598         else
599                 selection_ = false;
600 }
601
602
603 void MathCursor::drawSelection(MathPainterInfo & pi) const
604 {
605         if (!selection_)
606                 return;
607         MathCursorPos i1;
608         MathCursorPos i2;
609         getSelection(i1, i2);
610         i1.par_->drawSelection(pi, i1.idx_, i1.pos_, i2.idx_, i2.pos_);
611 }
612
613
614 void MathCursor::handleNest(MathAtom const & a)
615 {
616         MathAtom at = a;
617         at.nucleus()->cell(0) = asArray(grabAndEraseSelection());
618         insert(at);
619         pushRight(prevAtom());
620 }
621
622
623 void MathCursor::getPos(int & x, int & y) const
624 {
625         par()->getPos(idx(), pos(), x, y);
626 }
627
628
629 int MathCursor::targetX() const
630 {
631         if (targetx_ != -1)
632                 return targetx_;
633         int x = 0, y = 0;
634         getPos(x, y);
635         return x;
636 }
637
638
639 MathInset * MathCursor::par() const
640 {
641         return cursor().par_;
642 }
643
644
645 InsetFormulaBase * MathCursor::formula() const
646 {
647         return formula_;
648 }
649
650
651 MathCursor::idx_type MathCursor::idx() const
652 {
653         return cursor().idx_;
654 }
655
656
657 MathCursor::idx_type & MathCursor::idx()
658 {
659         return cursor().idx_;
660 }
661
662
663 MathCursor::pos_type MathCursor::pos() const
664 {
665         return cursor().pos_;
666 }
667
668
669 void MathCursor::adjust(pos_type from, size_type size)
670 {
671         if (cursor().pos_ > from)
672                 cursor().pos_ += size;
673         if (Anchor_.back().pos_ > from)
674                 Anchor_.back().pos_ += size;
675         // just to be on the safe side
676         // theoretically unecessary
677         normalize();
678 }
679
680
681 MathCursor::pos_type & MathCursor::pos()
682 {
683         return cursor().pos_;
684 }
685
686
687 bool MathCursor::inMacroMode() const
688 {
689         if (!hasPrevAtom())
690                 return false;
691         MathUnknownInset const * p = prevAtom()->asUnknownInset();
692         return p && !p->final();
693 }
694
695
696 MathUnknownInset * MathCursor::activeMacro()
697 {
698         return inMacroMode() ? prevAtom().nucleus()->asUnknownInset() : 0;
699 }
700
701
702 MathUnknownInset const * MathCursor::activeMacro() const
703 {
704         return inMacroMode() ? prevAtom()->asUnknownInset() : 0;
705 }
706
707
708 bool MathCursor::inMacroArgMode() const
709 {
710         return pos() > 0 && prevAtom()->getChar() == '#';
711 }
712
713
714 bool MathCursor::selection() const
715 {
716         return selection_;
717 }
718
719
720 MathGridInset * MathCursor::enclosingGrid(MathCursor::idx_type & idx) const
721 {
722         for (MathInset::difference_type i = depth() - 1; i >= 0; --i) {
723                 MathGridInset * p = Cursor_[i].par_->asGridInset();
724                 if (p) {
725                         idx = Cursor_[i].idx_;
726                         return p;
727                 }
728         }
729         return 0;
730 }
731
732
733 void MathCursor::popToHere(MathInset const * p)
734 {
735         while (depth() && Cursor_.back().par_ != p)
736                 Cursor_.pop_back();
737 }
738
739
740 void MathCursor::popToEnclosingGrid()
741 {
742         while (depth() && !Cursor_.back().par_->asGridInset())
743                 Cursor_.pop_back();
744 }
745
746
747 void MathCursor::popToEnclosingHull()
748 {
749         while (depth() && !Cursor_.back().par_->asHullInset())
750                 Cursor_.pop_back();
751 }
752
753
754 void MathCursor::pullArg()
755 {
756         dump("pullarg");
757         MathArray a = array();
758         if (popLeft()) {
759                 plainErase();
760                 array().insert(pos(), a);
761                 Anchor_ = Cursor_;
762         } else {
763                 formula()->mutateToText();
764         }
765 }
766
767
768 void MathCursor::touch()
769 {
770         MathIterator::const_iterator it = Cursor_.begin();
771         MathIterator::const_iterator et = Cursor_.end();
772         for ( ; it != et; ++it)
773                 it->cell().touch();
774 }
775
776
777 void MathCursor::normalize()
778 {
779         if (idx() >= par()->nargs()) {
780                 lyxerr << "this should not really happen - 1: "
781                        << idx() << " " << par()->nargs() << " in: " << par() << "\n";
782                 dump("error 2");
783         }
784         idx() = min(idx(), par()->nargs() - 1);
785
786         if (pos() > size()) {
787                 lyxerr << "this should not really happen - 2: "
788                         << pos() << " " << size() <<  " in idx: " << idx()
789                         << " in atom: '";
790                 WriteStream wi(lyxerr, false, true);
791                 par()->write(wi);
792                 lyxerr << "\n";
793                 dump("error 4");
794         }
795         pos() = min(pos(), size());
796 }
797
798
799 MathCursor::size_type MathCursor::size() const
800 {
801         return array().size();
802 }
803
804
805 bool MathCursor::hasPrevAtom() const
806 {
807         return pos() > 0;
808 }
809
810
811 bool MathCursor::hasNextAtom() const
812 {
813         return pos() < size();
814 }
815
816
817 MathAtom const & MathCursor::prevAtom() const
818 {
819         lyx::Assert(pos() > 0);
820         return array()[pos() - 1];
821 }
822
823
824 MathAtom & MathCursor::prevAtom()
825 {
826         lyx::Assert(pos() > 0);
827         return array()[pos() - 1];
828 }
829
830
831 MathAtom const & MathCursor::nextAtom() const
832 {
833         lyx::Assert(pos() < size());
834         return array()[pos()];
835 }
836
837
838 MathAtom & MathCursor::nextAtom()
839 {
840         lyx::Assert(pos() < size());
841         return array()[pos()];
842 }
843
844
845 MathArray & MathCursor::array() const
846 {
847         static MathArray dummy;
848
849         if (idx() >= par()->nargs()) {
850                 lyxerr << "############  idx_ " << idx() << " not valid\n";
851                 return dummy;
852         }
853
854         if (depth() == 0) {
855                 lyxerr << "############  depth() == 0 not valid\n";
856                 return dummy;
857         }
858
859         return cursor().cell();
860 }
861
862
863 void MathCursor::idxNext()
864 {
865         par()->idxNext(idx(), pos());
866 }
867
868
869 void MathCursor::idxPrev()
870 {
871         par()->idxPrev(idx(), pos());
872 }
873
874
875 char MathCursor::valign() const
876 {
877         idx_type idx;
878         MathGridInset * p = enclosingGrid(idx);
879         return p ? p->valign() : '\0';
880 }
881
882
883 char MathCursor::halign() const
884 {
885         idx_type idx;
886         MathGridInset * p = enclosingGrid(idx);
887         return p ? p->halign(idx % p->ncols()) : '\0';
888 }
889
890
891 void MathCursor::getSelection(MathCursorPos & i1, MathCursorPos & i2) const
892 {
893         MathCursorPos anc = normalAnchor();
894         if (anc < cursor()) {
895                 i1 = anc;
896                 i2 = cursor();
897         } else {
898                 i1 = cursor();
899                 i2 = anc;
900         }
901 }
902
903
904 MathCursorPos & MathCursor::cursor()
905 {
906         lyx::Assert(depth());
907         return Cursor_.back();
908 }
909
910
911 MathCursorPos const & MathCursor::cursor() const
912 {
913         lyx::Assert(depth());
914         return Cursor_.back();
915 }
916
917
918 bool MathCursor::goUpDown(bool up)
919 {
920         // Be warned: The 'logic' implemented in this function is highly fragile.
921         // A distance of one pixel or a '<' vs '<=' _really_ matters.
922         // So fiddle around with it only if you know what you are doing!
923   int xo = 0;
924         int yo = 0;
925         getPos(xo, yo);
926
927         // check if we had something else in mind, if not, this is the future goal
928         if (targetx_ == -1)
929                 targetx_ = xo;
930         else
931                 xo = targetx_;
932
933         // try neigbouring script insets
934         // try left
935         if (hasPrevAtom()) {
936                 MathScriptInset const * p = prevAtom()->asScriptInset();
937                 if (p && p->has(up)) {
938                         --pos();
939                         push(nextAtom());
940                         idx() = up; // the superscript has index 1
941                         pos() = size();
942                         ///lyxerr << "updown: handled by scriptinset to the left\n";
943                         return true;
944                 }
945         }
946
947         // try right
948         if (hasNextAtom()) {
949                 MathScriptInset const * p = nextAtom()->asScriptInset();
950                 if (p && p->has(up)) {
951                         push(nextAtom());
952                         idx() = up;
953                         pos() = 0;
954                         ///lyxerr << "updown: handled by scriptinset to the right\n";
955                         return true;
956                 }
957         }
958
959         // try current cell
960         //xarray().boundingBox(xlow, xhigh, ylow, yhigh);
961         //if (up)
962         //      yhigh = yo - 4;
963         //else
964         //      ylow = yo + 4;
965         //if (bruteFind(xo, yo, xlow, xhigh, ylow, yhigh)) {
966         //      lyxerr << "updown: handled by brute find in the same cell\n";
967         //      return true;
968         //}
969
970         // try to find an inset that knows better then we
971         while (1) {
972                 ///lyxerr << "updown: We are in " << *par() << " idx: " << idx() << '\n';
973                 // ask inset first
974                 if (par()->idxUpDown(idx(), pos(), up, targetx_)) {
975                         // try to find best position within this inset
976                         if (!selection())
977                                 bruteFind2(xo, yo);
978                         return true;
979                 }
980
981                 // no such inset found, just take something "above"
982                 ///lyxerr << "updown: handled by strange case\n";
983                 if (!popLeft())
984                         return
985                                 bruteFind(xo, yo,
986                                         formula()->xlow(),
987                                         formula()->xhigh(),
988                                         up ? formula()->ylow() : yo + 4,
989                                         up ? yo - 4 : formula()->yhigh()
990                                 );
991
992                 // any improvement so far?
993                 int xnew, ynew;
994                 getPos(xnew, ynew);
995                 if (up ? ynew < yo : ynew > yo)
996                         return true;
997         }
998 }
999
1000
1001 bool MathCursor::bruteFind
1002         (int x, int y, int xlow, int xhigh, int ylow, int yhigh)
1003 {
1004         MathIterator best_cursor;
1005         double best_dist = 1e10;
1006
1007         MathIterator it = ibegin(formula()->par().nucleus());
1008         MathIterator et = iend(formula()->par().nucleus());
1009         while (1) {
1010                 // avoid invalid nesting when selecting
1011                 if (!selection_ || positionable(it, Anchor_)) {
1012                         int xo, yo;
1013                         it.back().getPos(xo, yo);
1014                         if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) {
1015                                 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
1016                                 // '<=' in order to take the last possible position
1017                                 // this is important for clicking behind \sum in e.g. '\sum_i a'
1018                                 if (d <= best_dist) {
1019                                         best_dist   = d;
1020                                         best_cursor = it;
1021                                 }
1022                         }
1023                 }
1024
1025                 if (it == et)
1026                         break;
1027                 ++it;
1028         }
1029
1030         if (best_dist < 1e10)
1031                 Cursor_ = best_cursor;
1032         return best_dist < 1e10;
1033 }
1034
1035
1036 void MathCursor::bruteFind2(int x, int y)
1037 {
1038         double best_dist = 1e10;
1039
1040         MathIterator it = Cursor_;
1041         it.back().setPos(0);
1042         MathIterator et = Cursor_;
1043         et.back().setPos(it.cell().size());
1044         while (1) {
1045                 int xo, yo;
1046                 it.back().getPos(xo, yo);
1047                 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
1048                 // '<=' in order to take the last possible position
1049                 // this is important for clicking behind \sum in e.g. '\sum_i a'
1050                 if (d <= best_dist) {
1051                         best_dist = d;
1052                         Cursor_   = it;
1053                 }
1054                 if (it == et)
1055                         break;
1056                 ++it;
1057         }
1058 }
1059
1060
1061 bool MathCursor::idxLineLast()
1062 {
1063         idx() -= idx() % par()->ncols();
1064         idx() += par()->ncols() - 1;
1065         pos() = size();
1066         return true;
1067 }
1068
1069 bool MathCursor::idxLeft()
1070 {
1071         return par()->idxLeft(idx(), pos());
1072 }
1073
1074
1075 bool MathCursor::idxRight()
1076 {
1077         return par()->idxRight(idx(), pos());
1078 }
1079
1080
1081 bool MathCursor::script(bool up)
1082 {
1083         // Hack to get \\^ and \\_ working
1084         if (inMacroMode() && macroName() == "\\") {
1085                 if (up)
1086                         niceInsert(createMathInset("mathcircumflex"));
1087                 else
1088                         interpret('_');
1089                 return true;
1090         }
1091
1092         macroModeClose();
1093         string safe = grabAndEraseSelection();
1094         if (inNucleus()) {
1095                 // we are in a nucleus of a script inset, move to _our_ script
1096                 par()->asScriptInset()->ensure(up);
1097                 idx() = up;
1098                 pos() = 0;
1099         } else if (hasPrevAtom() && prevAtom()->asScriptInset()) {
1100                 prevAtom().nucleus()->asScriptInset()->ensure(up);
1101                 pushRight(prevAtom());
1102                 idx() = up;
1103                 pos() = size();
1104         } else if (hasPrevAtom()) {
1105                 --pos();
1106                 array()[pos()] = MathAtom(new MathScriptInset(nextAtom(), up));
1107                 pushLeft(nextAtom());
1108                 idx() = up;
1109                 pos() = 0;
1110         } else {
1111                 plainInsert(MathAtom(new MathScriptInset(up)));
1112                 prevAtom().nucleus()->asScriptInset()->ensure(up);
1113                 pushRight(prevAtom());
1114                 idx() = up;
1115                 pos() = 0;
1116         }
1117         paste(safe);
1118         dump("1");
1119         return true;
1120 }
1121
1122
1123 bool MathCursor::interpret(char c)
1124 {
1125         //lyxerr << "interpret 2: '" << c << "'\n";
1126         targetx_ = -1; // "no target"
1127         if (inMacroArgMode()) {
1128                 --pos();
1129                 plainErase();
1130                 int n = c - '0';
1131                 MathMacroTemplate const * p = formula()->par()->asMacroTemplate();
1132                 if (p && 1 <= n && n <= p->numargs())
1133                         insert(MathAtom(new MathMacroArgument(c - '0')));
1134                 else {
1135                         insert(createMathInset("#"));
1136                         interpret(c); // try again
1137                 }
1138                 return true;
1139         }
1140
1141         // handle macroMode
1142         if (inMacroMode()) {
1143                 string name = macroName();
1144                 //lyxerr << "interpret name: '" << name << "'\n";
1145
1146                 if (isalpha(c)) {
1147                         activeMacro()->setName(activeMacro()->name() + c);
1148                         return true;
1149                 }
1150
1151                 // handle 'special char' macros
1152                 if (name == "\\") {
1153                         // remove the '\\'
1154                         backspace();
1155                         if (c == '\\') {
1156                                 if (currentMode() == MathInset::TEXT_MODE)
1157                                         niceInsert(createMathInset("textbackslash"));
1158                                 else
1159                                         niceInsert(createMathInset("backslash"));
1160                         } else if (c == '{') {
1161                                 niceInsert(MathAtom(new MathBraceInset));
1162                         } else {
1163                                 niceInsert(createMathInset(string(1, c)));
1164                         }
1165                         return true;
1166                 }
1167
1168                 // leave macro mode and try again if necessary
1169                 macroModeClose();
1170                 if (c == '{')
1171                         niceInsert(MathAtom(new MathBraceInset));
1172                 else if (c != ' ')
1173                         interpret(c);
1174                 return true;
1175         }
1176
1177         // This is annoying as one has to press <space> far too often.
1178         // Disable it.
1179
1180         if (0) {
1181                 // leave autocorrect mode if necessary
1182                 if (autocorrect_ && c == ' ') {
1183                         autocorrect_ = false;
1184                         return true;
1185                 }
1186         }
1187
1188         // just clear selection on pressing the space bar
1189         if (selection_ && c == ' ') {
1190                 selection_ = false;
1191                 return true;
1192         }
1193
1194         selClearOrDel();
1195
1196         if (c == '\\') {
1197                 //lyxerr << "starting with macro\n";
1198                 insert(MathAtom(new MathUnknownInset("\\", false)));
1199                 return true;
1200         }
1201
1202         if (c == '\n') {
1203                 if (currentMode() == MathInset::TEXT_MODE) 
1204                         insert(c);
1205                 return true;
1206         }
1207
1208         if (c == ' ') {
1209                 if (currentMode() == MathInset::TEXT_MODE) {
1210                         // insert spaces in text mode,
1211                         // but suppress direct insertion of two spaces in a row
1212                         // the still allows typing  '<space>a<space>' and deleting the 'a', but
1213                         // it is better than nothing...
1214                         if (!hasPrevAtom() || prevAtom()->getChar() != ' ')
1215                                 insert(c);
1216                         return true;
1217                 }
1218                 if (hasPrevAtom() && prevAtom()->asSpaceInset()) {
1219                         prevAtom().nucleus()->asSpaceInset()->incSpace();
1220                         return true;
1221                 }
1222                 if (popRight())
1223                         return true;
1224                 // if are at the very end, leave the formula
1225                 return pos() != size();
1226         }
1227
1228         if (c == '#') {
1229                 insert(c);
1230                 return true;
1231         }
1232
1233         if (c == '{' || c == '}') {
1234                 niceInsert(createMathInset(string(1, c)));
1235                 return true;
1236         }
1237
1238         if (c == '$') {
1239                 insert(createMathInset("$"));
1240                 return true;
1241         }
1242
1243         if (c == '%') {
1244                 niceInsert(MathAtom(new MathCommentInset));
1245                 return true;
1246         }
1247
1248         // try auto-correction
1249         //if (autocorrect_ && hasPrevAtom() && math_autocorrect(prevAtom(), c))
1250         //      return true;
1251
1252         // no special circumstances, so insert the character without any fuss
1253         insert(c);
1254         autocorrect_ = true;
1255         return true;
1256 }
1257
1258
1259 void MathCursor::setSelection(MathIterator const & where, size_type n)
1260 {
1261         selection_ = true;
1262         Anchor_ = where;
1263         Cursor_ = where;
1264         cursor().pos_ += n;
1265 }
1266
1267
1268 void MathCursor::insetToggle()
1269 {
1270         if (hasNextAtom()) {
1271                 // toggle previous inset ...
1272                 nextAtom().nucleus()->lock(!nextAtom()->lock());
1273         } else if (popLeft() && hasNextAtom()) {
1274                 // ... or enclosing inset if we are in the last inset position
1275                 nextAtom().nucleus()->lock(!nextAtom()->lock());
1276                 posRight();
1277         }
1278 }
1279
1280
1281 string MathCursor::info() const
1282 {
1283         ostringstream os;
1284         os << "Math editor mode.  ";
1285         for (int i = 0, n = depth(); i < n; ++i) {
1286                 Cursor_[i].par_->infoize(os);
1287                 os << "  ";
1288         }
1289         if (hasPrevAtom())
1290                 if (prevAtom()->asSymbolInset() || prevAtom()->asScriptInset())
1291                         prevAtom()->infoize(os);
1292         os << "                    ";
1293         return os.str().c_str(); // .c_str() needed for lyxstring
1294 }
1295
1296
1297 unsigned MathCursor::depth() const
1298 {
1299         return Cursor_.size();
1300 }
1301
1302
1303
1304
1305 namespace {
1306
1307 void region(MathCursorPos const & i1, MathCursorPos const & i2,
1308         MathInset::row_type & r1, MathInset::row_type & r2,
1309         MathInset::col_type & c1, MathInset::col_type & c2)
1310 {
1311         MathInset * p = i1.par_;
1312         c1 = p->col(i1.idx_);
1313         c2 = p->col(i2.idx_);
1314         if (c1 > c2)
1315                 swap(c1, c2);
1316         r1 = p->row(i1.idx_);
1317         r2 = p->row(i2.idx_);
1318         if (r1 > r2)
1319                 swap(r1, r2);
1320 }
1321
1322 }
1323
1324
1325 string MathCursor::grabSelection() const
1326 {
1327         if (!selection_)
1328                 return string();
1329
1330         MathCursorPos i1;
1331         MathCursorPos i2;
1332         getSelection(i1, i2);
1333
1334         if (i1.idx_ == i2.idx_) {
1335                 MathArray::const_iterator it = i1.cell().begin();
1336                 return asString(MathArray(it + i1.pos_, it + i2.pos_));
1337         }
1338
1339         row_type r1, r2;
1340         col_type c1, c2;
1341         region(i1, i2, r1, r2, c1, c2);
1342
1343         string data;
1344         for (row_type row = r1; row <= r2; ++row) {
1345                 if (row > r1)
1346                         data += "\\\\";
1347                 for (col_type col = c1; col <= c2; ++col) {
1348                         if (col > c1)
1349                                 data += "&";
1350                         data += asString(i1.par_->cell(i1.par_->index(row, col)));
1351                 }
1352         }
1353         return data;
1354 }
1355
1356
1357 void MathCursor::eraseSelection()
1358 {
1359         MathCursorPos i1;
1360         MathCursorPos i2;
1361         getSelection(i1, i2);
1362         if (i1.idx_ == i2.idx_)
1363                 i1.cell().erase(i1.pos_, i2.pos_);
1364         else {
1365                 MathInset * p = i1.par_;
1366                 row_type r1, r2;
1367                 col_type c1, c2;
1368                 region(i1, i2, r1, r2, c1, c2);
1369                 for (row_type row = r1; row <= r2; ++row)
1370                         for (col_type col = c1; col <= c2; ++col)
1371                                 p->cell(p->index(row, col)).clear();
1372         }
1373         cursor() = i1;
1374 }
1375
1376
1377 string MathCursor::grabAndEraseSelection()
1378 {
1379         if (!selection_)
1380                 return string();
1381         string res = grabSelection();
1382         eraseSelection();
1383         selection_ = false;
1384         return res;
1385 }
1386
1387
1388 MathCursorPos MathCursor::normalAnchor() const
1389 {
1390         if (Anchor_.size() < depth()) {
1391                 Anchor_ = Cursor_;
1392                 lyxerr << "unusual Anchor size\n";
1393         }
1394         //lyx::Assert(Anchor_.size() >= cursor.depth());
1395         // use Anchor on the same level as Cursor
1396         MathCursorPos normal = Anchor_[depth() - 1];
1397         if (depth() < Anchor_.size() && !(normal < cursor())) {
1398                 // anchor is behind cursor -> move anchor behind the inset
1399                 ++normal.pos_;
1400         }
1401         return normal;
1402 }
1403
1404
1405 MathInset::result_type MathCursor::dispatch(FuncRequest const & cmd)
1406 {
1407         for (int i = Cursor_.size() - 1; i >= 0; --i) {
1408                 MathCursorPos & pos = Cursor_[i];
1409                 MathInset::result_type
1410                         res = pos.par_->dispatch(cmd, pos.idx_, pos.pos_);
1411                 if (res != MathInset::UNDISPATCHED) {
1412                         if (res == MathInset::DISPATCHED_POP) {
1413                                 Cursor_.shrink(i + 1);
1414                                 selClear();
1415                         }
1416                         return res;
1417                 }
1418         }
1419         return MathInset::UNDISPATCHED;
1420 }
1421
1422
1423 MathInset::mode_type MathCursor::currentMode() const
1424 {
1425         for (int i = Cursor_.size() - 1; i >= 0; --i) {
1426                 MathInset::mode_type res = Cursor_[i].par_->currentMode();
1427                 if (res != MathInset::UNDECIDED_MODE)
1428                         return res;
1429         }
1430         return MathInset::UNDECIDED_MODE;
1431 }
1432
1433
1434 void MathCursor::handleFont(string const & font)
1435 {
1436         string safe;
1437         if (selection()) {
1438                 macroModeClose();
1439                 safe = grabAndEraseSelection();
1440         }
1441
1442         if (array().size()) {
1443                 // something left in the cell
1444                 if (pos() == 0) {
1445                         // cursor in first position
1446                         popLeft();
1447                 } else if (pos() == array().size()) {
1448                         // cursor in last position
1449                         popRight();
1450                 } else {
1451                         // cursor in between. split cell
1452                         MathArray::iterator bt = array().begin();
1453                         MathAtom at = createMathInset(font);
1454                         at.nucleus()->cell(0) = MathArray(bt, bt + pos());
1455                         cursor().cell().erase(bt, bt + pos());
1456                         popLeft();
1457                         plainInsert(at);
1458                 }
1459         } else {
1460                 // nothing left in the cell
1461                 pullArg();
1462                 plainErase();
1463         }
1464         insert(safe);
1465 }
1466
1467
1468 void releaseMathCursor(BufferView * bv)
1469 {
1470         if (!mathcursor)
1471                 return;
1472         mathcursor->formula()->hideInsetCursor(bv);
1473         delete mathcursor;
1474         mathcursor = 0;
1475 }
1476