]> git.lyx.org Git - lyx.git/blob - src/mathed/math_cursor.C
Fix "pullArg" behaviour when pressing <Backspace> at the beginning of an inset
[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 #ifdef __GNUG__
19 #pragma implementation
20 #endif
21
22 #include <config.h>
23 #include <algorithm>
24 #include <cctype>
25
26 #include "debug.h"
27 #include "LColor.h"
28 #include "Painter.h"
29 #include "mathed/support.h"
30 #include "formulabase.h"
31 #include "math_cursor.h"
32 #include "math_arrayinset.h"
33 #include "math_bigopinset.h"
34 #include "math_decorationinset.h"
35 #include "math_deliminset.h"
36 #include "math_dotsinset.h"
37 #include "math_fracinset.h"
38 #include "math_funcinset.h"
39 #include "math_gridinset.h"
40 #include "math_macro.h"
41 #include "math_macroarg.h"
42 #include "math_macrotable.h"
43 #include "math_macrotemplate.h"
44 #include "math_matrixinset.h"
45 #include "math_rootinset.h"
46 #include "math_spaceinset.h"
47 #include "math_sqrtinset.h"
48 #include "support/lstrings.h"
49 #include "math_scriptinset.h"
50 #include "math_parser.h"
51
52 using std::endl;
53 using std::min;
54 using std::max;
55 using std::isalnum;
56
57 #define RECTANGULAR_SELECT 1
58
59 namespace {
60
61 struct Selection
62 {
63         void grab(MathCursor const & cursor)
64         {
65                 data_.clear();
66                 MathCursorPos i1;
67                 MathCursorPos i2;
68                 cursor.getSelection(i1, i2); 
69                 if (i1.idx_ == i2.idx_)
70                         data_.push_back(MathArray(i1.cell(), i1.pos_, i2.pos_));
71                 else {
72 #ifdef RECTANGULAR_SELECT
73                         std::vector<int> indices = i1.par_->idxBetween(i1.idx_, i2.idx_);
74                         for (unsigned i = 0; i < indices.size(); ++i)
75                                 data_.push_back(i1.cell(indices[i]));
76 #else
77                         data_.push_back(MathArray(i1.cell(), i1.pos_, i1.cell().size()));
78                         for (int i = i1.idx_ + 1; i < i2.idx_; ++i)
79                                 data_.push_back(i1.cell(i));
80                         data_.push_back(MathArray(i2.cell(), 0, i2.pos_));
81 #endif
82                 }
83         }
84
85         void erase(MathCursor & cursor)
86         {
87                 MathCursorPos i1;
88                 MathCursorPos i2;
89                 cursor.getSelection(i1, i2); 
90                 if (i1.idx_ == i2.idx_) {
91                         i1.cell().erase(i1.pos_, i2.pos_);
92                 } else {
93 #ifdef RECTANGULAR_SELECT
94                         std::vector<int> indices = i1.par_->idxBetween(i1.idx_, i2.idx_);
95                         for (unsigned i = 0; i < indices.size(); ++i)
96                                 i1.cell(indices[i]).erase();
97 #else
98                         i1.cell().erase(i1.pos_, i1.cell().size());
99                         for (int i = i1.idx_ + 1; i < i2.idx_; ++i)
100                                 i1.cell(i).erase();
101                         i2.cell().erase(0, i2.pos_);
102
103                         int from = i1.cell().size() ? i1.idx_ + 1 : i1.idx_; 
104                         int to   = i2.cell().size() ? i2.idx_ : i2.idx_ + 1; 
105                         i1.par_->idxDeleteRange(from, to);
106 #endif
107                 }
108                 cursor.cursor() = i1;
109         }
110
111         void paste(MathCursor & cursor) const
112         {
113 #ifdef RECTANGULAR_SELECT
114                 cursor.cursor().cell().push_back(glue());
115 #else
116                 unsigned na  = cursor.cursor().par_->nargs();
117                 unsigned idx = cursor.cursor().idx_;
118                 unsigned end = std::min(idx + data_.size(), na);
119                 for (int i = 0; i < end - idx; ++i)
120                         cursor.cursor().cell(idx + i).push_back(data_[i]);
121                 for (unsigned i = end - idx; i < data_.size(); ++i)
122                         cursor.cursor().cell(end - 1).push_back(data_[i]);
123 #endif
124         }
125
126         // glues selection to one cell
127         MathArray glue() const
128         {
129                 MathArray ar;
130                 for (unsigned i = 0; i < data_.size(); ++i)
131                         ar.push_back(data_[i]);
132                 return ar;
133         }
134
135         void clear()
136         {
137                 data_.clear();
138         }
139
140         
141
142         std::vector<MathArray> data_;
143 };
144
145
146 Selection theSelection;
147
148
149 bool IsMacro(short tok, int id)
150 {
151         return tok != LM_TK_STACK &&
152                tok != LM_TK_FRAC &&
153                tok != LM_TK_SQRT &&
154                tok != LM_TK_DECORATION &&
155                tok != LM_TK_SPACE &&
156                tok != LM_TK_DOTS &&
157                tok != LM_TK_FUNCLIM &&
158                tok != LM_TK_BIGSYM &&
159                !(tok == LM_TK_SYM && id < 255);
160 }
161
162
163 std::ostream & operator<<(std::ostream & os, MathCursorPos const & p)
164 {
165         os << "(par: " << p.par_ << " idx: " << p.idx_
166            << " pos: " << p.pos_ << ")";
167         return os;
168 }
169
170 }
171
172
173 MathCursor::MathCursor(InsetFormulaBase * formula)
174         : formula_(formula)
175 {
176         lastcode   = LM_TC_MIN;
177         macro_mode = false;
178         selection  = false;
179         first();
180 }
181
182
183 void MathCursor::push(MathInset * par, bool first)
184 {
185         MathCursorPos p;
186         p.par_ = par;
187         if (first)
188                 par->idxFirst(p.idx_, p.pos_);
189         else
190                 par->idxLast(p.idx_, p.pos_);
191         Cursor_.push_back(p);
192 }
193
194
195 bool MathCursor::pop()
196 {
197         if (Cursor_.size() <= 1)
198                 return false;
199         Cursor_.pop_back();
200         return true;
201 }
202
203
204 MathInset * MathCursor::parInset(int i) const
205 {
206         return Cursor_[i].par_;
207 }
208
209
210 void MathCursor::dump(char const * what) const
211 {
212         return;
213
214         lyxerr << "MC: " << what << "\n";
215         for (unsigned i = 0; i < Cursor_.size(); ++i)
216                 lyxerr << "  i: " << i 
217                         << " pos: " << Cursor_[i].pos_
218                         << " idx: " << Cursor_[i].idx_
219                         << " par: " << Cursor_[i].par_ << "\n";
220
221         //lyxerr        << " sel: " << selection << " data: " << array() << "\n";
222 }
223
224
225 void MathCursor::seldump(char const *) const
226 {
227         //lyxerr << "SEL: " << str << ": '" << theSelection << "'\n";
228         //dump("   Pos");
229         return;
230
231         //lyxerr << "\n\n\\n=================vvvvvvvvvvvvv=======================   "
232         //      <<  str << "\ntheSelection: " << theSelection;
233         //for (unsigned int i = 0; i < Cursor_.size(); ++i) 
234         //      lyxerr << Cursor_[i].par_ << "\n'" << Cursor_[i].cell() << "'\n";
235         //lyxerr << "\ncursor.pos_: " << cursor().pos_;
236         //lyxerr << "\nanchor.pos_: " << anchor().pos_;
237         //lyxerr << "\n===================^^^^^^^^^^^^=====================\n\n\n";
238 }
239
240
241 bool MathCursor::isInside(MathInset * p) const
242 {
243         for (unsigned i = 0; i < Cursor_.size(); ++i) 
244                 if (parInset(i) == p) 
245                         return true;
246         return false;
247 }
248
249
250 bool MathCursor::openable(MathInset * p, bool sel, bool useupdown) const
251 {
252         if (!p)
253                 return false;
254         if (!(p->isActive() || (useupdown && p->isUpDownInset())))
255                 return false;
256
257         if (sel) {
258                 // we can't move into everything during selection
259                 if (Cursor_.size() == Anchor_.size())
260                         return false;
261                 if (p != Anchor_[Cursor_.size()].par_)
262                         return false;
263         }
264         return true;
265 }
266
267
268 bool MathCursor::plainLeft()
269 {
270         return array().prev(cursor().pos_);
271 }
272
273
274 bool MathCursor::Left(bool sel)
275 {
276         dump("Left 1");
277         if (macro_mode) {
278                 // was MacroModeBack()
279                 if (!imacro->name().empty()) {
280                         imacro->SetName(imacro->name().substr(0, imacro->name().length()-1));
281                         imacro->Metrics(imacro->size());
282                 } else
283                         MacroModeClose();
284                 return true;
285         }
286         SelHandle(sel);
287         clearLastCode();
288
289         MathInset * p = prevInset();
290         if (openable(p, sel, false)) {
291                 plainLeft();
292                 push(p, false);
293                 return true;
294         } 
295         if (plainLeft())
296                 return true;
297         if (cursor().par_->idxLeft(cursor().idx_, cursor().pos_))
298                 return true;
299         if (pop())
300                 return true;
301         return false;
302 }
303
304
305 bool MathCursor::plainRight()
306 {
307         return array().next(cursor().pos_);
308 }
309
310
311 bool MathCursor::Right(bool sel)
312 {
313         dump("Right 1");
314         if (macro_mode) {
315                 MacroModeClose();
316                 return true;
317         }
318         SelHandle(sel);
319         clearLastCode();
320
321         MathInset * p = nextInset();
322         if (openable(p, sel, false)) {
323                 push(p, true);
324                 return true;
325         }
326         if (array().next(cursor().pos_))
327                 return true;
328         if (cursor().par_->idxRight(cursor().idx_, cursor().pos_))
329                 return true;
330         if (!pop())
331                 return false;
332         array().next(cursor().pos_);
333         return true;
334 }
335
336
337 void MathCursor::first()
338 {
339         Cursor_.clear();
340         push(formula_->par(), true);
341 }
342
343
344 void MathCursor::last()
345 {
346         Cursor_.clear();
347         push(formula_->par(), false);
348 }
349
350
351 void MathCursor::SetPos(int x, int y)
352 {
353         dump("SetPos 1");
354         //lyxerr << "MathCursor::SetPos x: " << x << " y: " << y << "\n";
355
356         MacroModeClose();
357         lastcode = LM_TC_MIN;
358         first();
359
360         cursor().par_  = formula()->par();
361
362         while (1) {
363                 cursor().idx_ = -1;
364                 cursor().pos_ = -1;
365                 //lyxerr << "found idx: " << idx_ << " cursor: " << cursor().pos_  << "\n";
366                 int distmin = 1 << 30; // large enough
367                 for (int i = 0; i < cursor().par_->nargs(); ++i) {
368                         MathXArray const & ar = cursor().par_->xcell(i);
369                         int x1 = x - ar.xo();
370                         int y1 = y - ar.yo();
371                         int c  = ar.x2pos(x1);
372                         int xx = abs(x1 - ar.pos2x(c));
373                         int yy = abs(y1);
374                         //lyxerr << "idx: " << i << " xx: " << xx << " yy: " << yy
375                         //      << " c: " << c  << " xo: " << ar.xo() << "\n";
376                         if (yy + xx <= distmin) {
377                                 distmin        = yy + xx;
378                                 cursor().idx_  = i;
379                                 cursor().pos_  = c;
380                         }
381                 }
382                 //lyxerr << "found idx: " << cursor().idx_ << " cursor: "
383                 //      << cursor().pos_  << "\n";
384                 MathInset * n = nextInset();
385                 MathInset * p = prevInset();
386                 if (openable(n, selection, true) && n->covers(x, y))
387                         push(n, true);
388                 else if (openable(p, selection, true) && p->covers(x, y)) {
389                         plainLeft();
390                         push(p, false);
391                 } else 
392                         break;
393         }
394         dump("SetPos 2");
395 }
396
397
398 void MathCursor::Home()
399 {
400         dump("Home 1");
401         if (macro_mode)
402                 MacroModeClose();
403         clearLastCode();
404         if (!cursor().par_->idxHome(cursor().idx_, cursor().pos_)) 
405                 pop();
406         dump("Home 2");
407 }
408
409
410 void MathCursor::End()
411 {
412         dump("End 1");
413         if (macro_mode)
414                 MacroModeClose();
415         clearLastCode();
416         if (!cursor().par_->idxEnd(cursor().idx_, cursor().pos_)) {
417                 pop();
418                 array().next(cursor().pos_);
419         }
420         dump("End 2");
421 }
422
423
424 void MathCursor::insert(char c, MathTextCodes t)
425 {
426         //lyxerr << "inserting '" << c << "'\n";
427         if (selection)
428                 SelDel();
429
430         if (t == LM_TC_MIN)
431                 t = lastcode;
432
433         if (macro_mode && !(MathIsAlphaFont(t) || t == LM_TC_MIN))
434                 MacroModeClose();
435
436         if (macro_mode) {
437                 if (MathIsAlphaFont(t) || t == LM_TC_MIN) {
438                         // was MacroModeinsert(c);
439                         imacro->SetName(imacro->name() + static_cast<char>(c));
440                         return;
441                 }
442         }
443
444         array().insert(cursor().pos_, c, t);
445         array().next(cursor().pos_);
446
447         lastcode = t;
448 }
449
450
451 void MathCursor::insert(MathInset * p)
452 {
453         MacroModeClose();
454
455         if (selection) {
456                 if (p->nargs())
457                         SelCut();
458                 else
459                         SelDel();
460         }
461
462         array().insert(cursor().pos_, p);
463         array().next(cursor().pos_);
464 }
465
466
467 void MathCursor::insert(MathArray const & ar)
468 {
469         MacroModeClose();
470         if (selection)
471                 SelCut();
472
473         array().insert(cursor().pos_, ar);
474         cursor().pos_ += ar.size();
475 }
476
477
478 void MathCursor::Delete()
479 {
480         dump("Delete 1");
481         if (macro_mode)
482                 return;
483
484         if (selection) {
485                 SelDel();
486                 return;
487         }
488
489         if (cursor().pos_ < array().size())
490                 array().erase(cursor().pos_);
491
492         // delete empty cells if necessary
493         if (cursor().pos_ == 0 && array().size() == 0) {
494                 bool popit;
495                 bool removeit;
496                 cursor().par_->idxDelete(cursor().idx_, popit, removeit);
497                 if (popit && pop() && removeit)
498                         Delete();
499         }
500
501         dump("Delete 2");
502 }
503
504
505 void MathCursor::DelLine()
506 {
507         MacroModeClose();
508
509         if (selection) {
510                 SelDel();
511                 return;
512         }
513
514         if (cursor().par_->nrows() > 1)
515                 cursor().par_->delRow(row());
516 }
517
518
519 bool MathCursor::Up(bool sel)
520 {
521         dump("Up 1");
522         MacroModeClose();
523         SelHandle(sel);
524
525         if (selection) {
526                 int x = xarray().pos2x(cursor().pos_);
527                 if (cursor().par_->idxUp(cursor().idx_, cursor().pos_)) {
528                         cursor().pos_ = xarray().x2pos(x);
529                         return true;
530                 }
531                 if (pop()) 
532                         return true;
533                 return false;
534         }
535
536         // check whether we could move into an inset on the right or on the left
537         MathInset * p = nextInset();
538         if (p) {
539                 int idx, pos;
540                 if (p->idxFirstUp(idx, pos)) {
541                         push(p, true);
542                         cursor().par_ = p;
543                         cursor().idx_ = idx;
544                         cursor().pos_ = pos;
545                         dump("Up 3");
546                         return true;
547                 }
548         }
549
550         p = prevInset();
551         if (p) {
552                 int idx, pos;
553                 if (p->idxLastUp(idx, pos)) {
554                         plainLeft();
555                         push(p, false);
556                         cursor().par_ = p;
557                         cursor().idx_ = idx;
558                         cursor().pos_ = pos;
559                         dump("Up 4");
560                         return true;
561                 }
562         }
563
564         int x = xarray().pos2x(cursor().pos_);
565         if (cursor().idxUp()) {
566                 cursor().pos_ = xarray().x2pos(x);
567                 return true;
568         }
569         if (pop())
570                 return true;
571         return false;
572 }
573
574
575 bool MathCursor::Down(bool sel)
576 {
577         dump("Down 1");
578         MacroModeClose();
579         SelHandle(sel);
580
581         if (selection) {
582                 int x = xarray().pos2x(cursor().pos_);
583                 if (cursor().idxDown()) {
584                         cursor().pos_ = xarray().x2pos(x);
585                         return true;
586                 }
587                 if (pop()) 
588                         return true;
589                 return false;
590         }
591
592         // check whether we could move into an inset on the right or on the left
593         MathInset * p = nextInset();
594         if (p) {
595                 int idx, pos;
596                 if (p->idxFirstDown(idx, pos)) {
597                         push(p, true);
598                         cursor().idx_ = idx;
599                         cursor().pos_ = pos;
600                         dump("Down 3");
601                         return true;
602                 }
603         }
604
605         p = prevInset();
606         if (p) {
607                 int idx, pos;
608                 if (p->idxLastDown(idx, pos)) {
609                         plainLeft();
610                         push(p, false);
611                         cursor().idx_ = idx;
612                         cursor().pos_ = pos;
613                         dump("Down 4");
614                         return true;
615                 }
616         }
617
618         int x = xarray().pos2x(cursor().pos_);
619         if (cursor().par_->idxDown(cursor().idx_, cursor().pos_)) {
620                 cursor().pos_ = xarray().x2pos(x);
621                 return true;
622         }
623         if (pop())
624                 return true;
625         return false;
626 }
627
628
629 bool MathCursor::toggleLimits()
630 {
631         if (!prevIsInset())
632                 return false;
633         MathInset * p = prevInset();
634         int old = p->limits();
635         p->limits(old < 0 ? 1 : -1);
636         return old != p->limits();
637 }
638
639
640 void MathCursor::SetSize(MathStyles size)
641 {
642         cursor().par_->UserSetSize(size);
643 }
644
645
646
647 void MathCursor::Interpret(string const & s)
648 {
649         lyxerr << "Interpret: '" << s << "'  ('" << s.substr(0, 7)  << "' " <<
650 in_word_set(s) << " \n";
651
652         if (s[0] == '^') {
653                 MathUpDownInset * p = nearbyUpDownInset();
654                 if (!p) {
655                         p = new MathScriptInset(true, false);
656                         insert(p);
657                         plainLeft();
658                 }
659                 push(p, true);
660                 p->up(true);
661                 cursor().idx_ = 0;
662                 return;
663         }
664
665         if (s[0] == '_') {
666                 MathUpDownInset * p = nearbyUpDownInset();
667                 if (!p) {
668                         p = new MathScriptInset(false, true);
669                         insert(p);
670                         plainLeft();
671                 }
672                 push(p, true);
673                 p->down(true);
674                 cursor().idx_ = 1;
675                 return;
676         }
677
678         if (s[0] == '!' || s[0] == ','  || s[0] == ':' || s[0] == ';') {
679                 int sp = (s[0] == ',') ? 1:((s[0] == ':') ? 2:((s[0] == ';') ? 3: 0));
680                 insert(new MathSpaceInset(sp));
681                 return;
682         }
683
684         MathInset * p = 0;
685         latexkeys const * l = in_word_set(s);
686
687         if (l == 0) {
688                 if (s == "root") 
689                         p = new MathRootInset;
690                 else if (MathMacroTable::hasTemplate(s))
691                         p = new MathMacro(MathMacroTable::provideTemplate(s));
692                 else if (s.size() > 7 && s.substr(0, 7) == "matrix ") {
693                         int m = 1;
694                         int n = 1;
695                         string v_align;
696                         string h_align;
697                         istringstream is(s.substr(7).c_str());
698                         is >> m >> n >> v_align >> h_align;
699                         m = std::max(1, m);
700                         n = std::max(1, n);
701                         v_align += 'c';
702                         MathArrayInset * pp = new MathArrayInset(m, n);
703                         pp->valign(v_align[0]);
704                         pp->halign(h_align);
705                         p = pp;
706                 }
707                 else
708                         p = new MathFuncInset(s, LM_OT_UNDEF);
709         } else {
710                 switch (l->token) {
711                         case LM_TK_BIGSYM: 
712                                         p = new MathBigopInset(l->name, l->id);
713                                         break;
714                                 
715                         case LM_TK_SYM: {
716                                 MathTextCodes code = static_cast<MathTextCodes>(l->id);
717                                 if (code < 255)
718                                         insert(l->id, MathIsBOPS(code) ? LM_TC_BOPS : LM_TC_SYMB);
719                                 else
720                                         p = new MathFuncInset(l->name);
721                                 break;
722                         }
723
724                         case LM_TK_STACK:
725                                 p = new MathFracInset("stackrel");
726                                 break;
727
728                         case LM_TK_FRAC:
729                                 p = new MathFracInset("frac");
730                                 break;
731
732                         case LM_TK_SQRT:
733                                 p = new MathSqrtInset;
734                                 break;
735
736                         case LM_TK_DECORATION:
737                                 p = new MathDecorationInset(l->name, l->id);
738                                 break;
739
740                         case  LM_TK_FUNCLIM:
741                                 p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
742                                 break;
743
744                         case LM_TK_SPACE:
745                                 p = new MathSpaceInset(l->id);
746                                 break;
747
748                         case LM_TK_DOTS:
749                                 p = new MathDotsInset(l->name, l->id);
750                                 break;
751
752                         case LM_TK_MACRO:
753                                 p = new MathMacro(MathMacroTable::provideTemplate(s));
754                                 break;
755
756                         default:
757                                 p = new MathFuncInset(l->name);
758                                 break;
759                 }
760         }
761
762         if (p) {
763                 bool oldsel = selection;
764                 if (oldsel) 
765                         SelCut();
766                 insert(p);
767                 if (p->nargs()) {
768                         plainLeft();
769                         push(p, true);
770                         if (oldsel) 
771                                 SelPaste();
772                 }
773                 p->Metrics(p->size());
774         }
775 }
776
777
778 void MathCursor::MacroModeOpen()
779 {
780         if (!macro_mode) {
781                 imacro = new MathFuncInset("");
782                 insert(imacro);
783                 macro_mode = true;
784         } else
785                 lyxerr << "Math Warning: Already in macro mode" << endl;
786 }
787
788
789 void MathCursor::MacroModeClose()
790 {
791         if (macro_mode)  {
792                 macro_mode = false;
793                 latexkeys const * l = in_word_set(imacro->name());
794                 if (!imacro->name().empty()
795                                 && (!l || (l && IsMacro(l->token, l->id)))
796                                 && !MathMacroTable::hasTemplate(imacro->name()))
797                 {
798                         if (!l) {
799                                 //imacro->SetName(macrobf);
800                                 // This guarantees that the string will be removed by destructor
801                                 imacro->SetType(LM_OT_UNDEF);
802                         } else
803                                 imacro->SetName(l->name);
804                 } else {
805                         Left();
806                         array().erase(cursor().pos_);
807                         if (l || MathMacroTable::hasTemplate(imacro->name())) 
808                                 Interpret(imacro->name());
809                         imacro->SetName(string());
810                 }
811                 imacro = 0;
812         }
813 }
814
815
816 void MathCursor::SelCopy()
817 {
818         seldump("SelCopy");
819         if (selection) {
820                 theSelection.grab(*this);
821                 SelClear();
822         }
823 }
824
825
826 void MathCursor::SelCut()
827 {
828         seldump("SelCut");
829         if (selection) {
830                 theSelection.grab(*this);
831                 theSelection.erase(*this);
832                 SelClear();
833         }
834 }
835
836
837 void MathCursor::SelDel()
838 {
839         seldump("SelDel");
840         if (selection) {
841                 theSelection.erase(*this);
842                 SelClear();
843         }
844 }
845
846
847 void MathCursor::SelPaste()
848 {
849         seldump("SelPaste");
850         theSelection.paste(*this);
851         SelClear();
852 }
853
854
855 void MathCursor::SelHandle(bool sel)
856 {
857         if (sel && !selection)
858                 SelStart();
859         if (!sel && selection)
860                 SelClear();
861 }
862
863
864 void MathCursor::SelStart()
865 {
866         seldump("SelStart");
867         if (selection)
868                 return;
869
870         Anchor_ = Cursor_;
871         selection = true;
872 }
873
874
875 void MathCursor::SelClear()
876 {
877         selection = false;
878 }
879
880
881 void MathCursor::drawSelection(Painter & pain) const
882 {
883         if (!selection)
884                 return;
885
886         MathCursorPos i1;
887         MathCursorPos i2;
888         getSelection(i1, i2);
889
890         //lyxerr << "selection from: " << i1 << " to " << i2 << "\n";
891
892         if (i1.idx_ == i2.idx_) {
893                 MathXArray & c = i1.xcell();
894                 int x1 = c.xo() + c.pos2x(i1.pos_);
895                 int y1 = c.yo() - c.ascent();
896                 int x2 = c.xo() + c.pos2x(i2.pos_);
897                 int y2 = c.yo() + c.descent();
898                 pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
899         } else {
900
901 #if RECTANGULAR_SELECT
902                 std::vector<int> indices = i1.par_->idxBetween(i1.idx_, i2.idx_);
903                 for (unsigned i = 0; i < indices.size(); ++i) {
904                         MathXArray & c = i1.xcell(indices[i]);
905                         int x1 = c.xo();
906                         int y1 = c.yo() - c.ascent();
907                         int x2 = c.xo() + c.width();
908                         int y2 = c.yo() + c.descent();
909                         pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
910                 }
911 #else
912                 // leftmost cell
913                 MathXArray & c = i1.xcell();
914                 int x1 = c.xo() + c.pos2x(i1.pos_);
915                 int y1 = c.yo() - c.ascent();
916                 int x2 = c.xo() + c.width();
917                 int y2 = c.yo() + c.descent();
918                 pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
919                 // middle cells
920                 for (int idx = i1.idx_ + 1; idx < i2.idx_; ++idx) { 
921                         MathXArray & c = i1.xcell(idx);
922                         int x1 = c.xo();
923                         int y1 = c.yo() - c.ascent();
924                         int x2 = c.xo() + c.width();
925                         int y2 = c.yo() + c.descent();
926                         pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
927                 }
928                 // rightmost cell
929                 MathXArray & cr = i2.xcell();
930                 x1 = cr.xo();
931                 y1 = cr.yo() - cr.ascent();
932                 x2 = cr.xo() + cr.pos2x(i2.pos_);
933                 y2 = cr.yo() + cr.descent();
934                 pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
935 #endif
936         }
937 }
938
939
940 void MathCursor::handleFont(MathTextCodes t)
941 {
942         if (selection) {
943                 MathCursorPos i1;
944                 MathCursorPos i2;
945                 getSelection(i1, i2); 
946                 if (i1.idx_ == i2.idx_) {
947                         MathArray & ar = i1.cell();
948                         for (int pos = i1.pos_; pos != i2.pos_; ar.next(pos))
949                                 if (!ar.isInset(pos) && isalnum(ar.GetChar(pos))) { 
950                                         MathTextCodes c = ar.GetCode(pos) == t ? LM_TC_VAR : t;
951                                         ar.setCode(pos, c);
952                                 }
953                 }
954         } else {
955                 lastcode = (lastcode == t) ? LM_TC_VAR : t;
956         }
957 }
958
959
960 void MathCursor::handleAccent(string const & name, int code)
961 {
962         MathDecorationInset * p = new MathDecorationInset(name, code);
963         if (selection) {
964                 SelCut();
965                 p->cell(0) = theSelection.glue();
966         }
967         insert(p);
968         push(p, true);
969 }
970
971
972 void MathCursor::handleDelim(int l, int r)
973 {
974         MathDelimInset * p = new MathDelimInset(l, r);
975         if (selection) {
976                 SelCut();
977                 p->cell(0) = theSelection.glue();
978         }
979         insert(p);
980         plainLeft();
981         push(p, true);
982 }
983
984
985 void MathCursor::GetPos(int & x, int & y)
986 {
987         x = xarray().xo() + xarray().pos2x(cursor().pos_);
988         y = xarray().yo();
989 }
990
991
992 MathTextCodes MathCursor::nextCode() const
993 {
994         return array().GetCode(cursor().pos_); 
995 }
996
997
998 MathTextCodes MathCursor::prevCode() const
999 {
1000         return array().GetCode(cursor().pos_ - 1); 
1001 }
1002
1003
1004 MathInset * MathCursor::par() const
1005 {
1006         return cursor().par_;
1007 }
1008
1009
1010 InsetFormulaBase const * MathCursor::formula()
1011 {
1012         return formula_;
1013 }
1014
1015
1016 int MathCursor::pos() const
1017 {
1018         return cursor().pos_;
1019 }
1020
1021
1022 bool MathCursor::InMacroMode() const
1023 {
1024         return macro_mode;
1025 }
1026
1027
1028 bool MathCursor::Selection() const
1029 {
1030         return selection;
1031 }
1032
1033
1034 void MathCursor::clearLastCode()
1035 {
1036         lastcode = LM_TC_MIN;
1037 }
1038
1039
1040 void MathCursor::setLastCode(MathTextCodes t)
1041 {
1042         lastcode = t;
1043 }
1044
1045
1046 MathTextCodes MathCursor::getLastCode() const
1047 {
1048         return lastcode;
1049 }
1050
1051
1052 MathInset * MathCursor::enclosing(MathInsetTypes t, int & idx) const
1053 {
1054         for (int i = Cursor_.size() - 1; i >= 0; --i) {
1055                 //lyxerr << "checking level " << i << "\n";
1056                 if (Cursor_[i].par_->GetType() == t) {
1057                         idx = Cursor_[i].idx_;
1058                         return Cursor_[i].par_;
1059                 }
1060         }
1061         return 0;
1062 }
1063
1064
1065 void MathCursor::pullArg()
1066 {
1067         // pullArg
1068         dump("pullarg");
1069         MathArray a = array();
1070         if (pop()) {
1071                 array().erase(cursor().pos_);
1072                 array().insert(cursor().pos_, a);
1073         }
1074 }
1075
1076
1077 MathStyles MathCursor::style() const
1078 {
1079         return xarray().style();
1080 }
1081
1082
1083 void MathCursor::normalize() const
1084 {
1085 #ifdef WITH_WARNINGS
1086 #warning This is evil!
1087 #endif
1088         MathCursor * it = const_cast<MathCursor *>(this);
1089
1090         if (cursor().idx_ < 0 || cursor().idx_ > cursor().par_->nargs())
1091                 lyxerr << "this should not really happen - 1\n";
1092         it->cursor().idx_    = max(cursor().idx_, 0);
1093         it->cursor().idx_    = min(cursor().idx_, cursor().par_->nargs());
1094
1095         if (cursor().pos_ < 0 || cursor().pos_ > array().size())
1096                 lyxerr << "this should not really happen - 2\n";
1097         it->cursor().pos_ = max(cursor().pos_, 0);
1098         it->cursor().pos_ = min(cursor().pos_, array().size());
1099 }
1100
1101
1102 int MathCursor::col() const
1103 {
1104         return par()->col(cursor().idx_);
1105 }
1106
1107
1108 int MathCursor::row() const
1109 {
1110         return par()->row(cursor().idx_);
1111 }
1112
1113
1114 /*
1115 char MathCursorPos::GetChar() const
1116 {
1117         return array().GetChar(cursor().pos_);
1118 }
1119
1120
1121 string MathCursorPos::readString()
1122 {
1123         string s;
1124         int code = nextCode();
1125         for ( ; OK() && nextCode() == code; Next()) 
1126                 s += GetChar();
1127
1128         return s;
1129 }
1130 */
1131
1132
1133 MathInset * MathCursor::prevInset() const
1134 {
1135         normalize();
1136         int c = cursor().pos_;
1137         if (!array().prev(c))
1138                 return 0;
1139         return array().nextInset(c);
1140 }
1141
1142
1143 MathInset * MathCursor::nextInset() const
1144 {
1145         normalize();
1146         return array().nextInset(cursor().pos_);
1147 }
1148
1149
1150 MathUpDownInset * MathCursor::nearbyUpDownInset() const
1151 {
1152         normalize();
1153         MathInset * p = array().prevInset(cursor().pos_);
1154         if (p && p->isUpDownInset())
1155                 return static_cast<MathUpDownInset *>(p);
1156         p = array().nextInset(cursor().pos_);
1157         if (p && p->isUpDownInset())
1158                 return static_cast<MathUpDownInset *>(p);
1159         return 0;
1160 }
1161
1162
1163 MathArray & MathCursor::array() const
1164 {
1165         static MathArray dummy;
1166         if (!cursor().par_) {
1167                 lyxerr << "############  par_ not valid\n";
1168                 return dummy;
1169         }
1170
1171         if (cursor().idx_ < 0 || cursor().idx_ >= cursor().par_->nargs()) {
1172                 lyxerr << "############  idx_ " << cursor().idx_ << " not valid\n";
1173                 return dummy;
1174         }
1175
1176         return cursor().cell();
1177 }
1178
1179
1180 MathXArray & MathCursor::xarray() const
1181 {
1182         return cursor().xcell();
1183 }
1184
1185
1186 bool MathCursor::nextIsInset() const
1187 {
1188         return cursor().pos_ < array().size() && MathIsInset(nextCode());
1189 }
1190
1191
1192 bool MathCursor::prevIsInset() const
1193 {
1194         return cursor().pos_ > 0 && MathIsInset(prevCode());
1195 }
1196
1197
1198 int MathCursor::xpos() const 
1199 {
1200         normalize();
1201         return xarray().pos2x(cursor().pos_);
1202 }
1203
1204
1205 void MathCursor::gotoX(int x)
1206 {
1207         cursor().pos_ = xarray().x2pos(x);      
1208 }
1209
1210
1211 void MathCursor::idxNext()
1212 {
1213         cursor().par_->idxNext(cursor().idx_, cursor().pos_);
1214 }
1215
1216
1217 void MathCursor::idxPrev()
1218 {
1219         cursor().par_->idxPrev(cursor().idx_, cursor().pos_);
1220 }
1221
1222
1223 void MathCursor::splitCell()
1224 {
1225         if (cursor().idx_ == cursor().par_->nargs() - 1) 
1226                 return;
1227         MathArray ar = array();
1228         ar.erase(0, cursor().pos_);
1229         array().erase(cursor().pos_, array().size());
1230         ++cursor().idx_;
1231         cursor().pos_ = 0;
1232         array().insert(0, ar);
1233 }
1234
1235
1236 void MathCursor::breakLine()
1237 {
1238         MathMatrixInset * p = static_cast<MathMatrixInset *>(formula()->par());
1239         if (p->GetType() == LM_OT_SIMPLE || p->GetType() == LM_OT_EQUATION) {
1240                 p->mutate(LM_OT_EQNARRAY);
1241                 p->addRow(0);
1242                 cursor().idx_ = p->nrows();
1243                 cursor().pos_ = 0;
1244         } else {
1245                 p->addRow(row());
1246
1247                 // split line
1248                 const int r = row();
1249                 for (int c = col() + 1; c < p->ncols(); ++c) {
1250                         const int i1 = p->index(r, c);
1251                         const int i2 = p->index(r + 1, c);      
1252                         lyxerr << "swapping cells " << i1 << " and " << i2 << "\n";
1253                         p->cell(i1).swap(p->cell(i2));
1254                 }
1255
1256                 // split cell
1257                 splitCell();
1258                 p->cell(cursor().idx_).swap(p->cell(cursor().idx_ + p->ncols() - 1));
1259         }
1260 }
1261
1262
1263 char MathCursor::valign() const
1264 {
1265         int idx;
1266         MathGridInset * p =
1267                 static_cast<MathGridInset *>(enclosing(LM_OT_MATRIX, idx));
1268         return p ? p->valign() : 0;
1269 }
1270
1271
1272 char MathCursor::halign() const
1273 {
1274         int idx;
1275         MathGridInset * p =
1276                 static_cast<MathGridInset *>(enclosing(LM_OT_MATRIX, idx));
1277         return p ? p->halign(idx % p->ncols()) : 0;
1278 }
1279
1280
1281 MathCursorPos MathCursor::firstSelectionPos() const
1282 {
1283         MathCursorPos anc = normalAnchor();
1284         return anc < cursor() ? anc : cursor(); 
1285 }
1286
1287
1288 MathCursorPos MathCursor::lastSelectionPos() const
1289 {
1290         MathCursorPos anc = normalAnchor();
1291         return anc < cursor() ? cursor() : anc; 
1292 }
1293
1294
1295 void MathCursor::getSelection(MathCursorPos & i1, MathCursorPos & i2) const
1296 {
1297         MathCursorPos anc = normalAnchor();
1298         if (anc < cursor()) {
1299                 i1 = anc;
1300                 i2 = cursor();
1301         } else {
1302                 i1 = cursor();
1303                 i2 = anc;
1304         }
1305 }
1306
1307
1308 MathCursorPos & MathCursor::cursor()
1309 {
1310         return Cursor_.back();
1311 }
1312
1313
1314 MathCursorPos const & MathCursor::cursor() const
1315 {
1316         return Cursor_.back();
1317 }
1318
1319
1320
1321 ////////////////////////////////////////////////////////////////////////
1322
1323
1324 bool operator==(MathCursorPos const & ti, MathCursorPos const & it)
1325 {
1326         return ti.par_ == it.par_ && ti.idx_ == it.idx_ && ti.pos_ == it.pos_;
1327 }
1328
1329
1330 bool operator<(MathCursorPos const & ti, MathCursorPos const & it)
1331 {
1332         if (ti.par_ != it.par_) {
1333                 lyxerr << "can't compare cursor and anchor in different insets\n";
1334                 return true;
1335         }
1336         if (ti.idx_ != it.idx_)
1337                 return ti.idx_ < it.idx_;
1338         return ti.pos_ < it.pos_;
1339 }
1340
1341
1342 MathArray & MathCursorPos::cell(int idx) const
1343 {
1344         return par_->cell(idx);
1345 }
1346
1347 MathArray & MathCursorPos::cell() const
1348 {
1349         return par_->cell(idx_);
1350 }
1351
1352
1353 MathXArray & MathCursorPos::xcell(int idx) const
1354 {
1355         return par_->xcell(idx);
1356 }
1357
1358
1359 MathXArray & MathCursorPos::xcell() const
1360 {
1361         return par_->xcell(idx_);
1362 }
1363
1364
1365 MathCursorPos MathCursor::normalAnchor() const
1366 {
1367         // use Anchor on the same level as Cursor
1368         MathCursorPos normal = Anchor_[Cursor_.size() - 1];
1369         if (Cursor_.size() < Anchor_.size() && !(normal < cursor())) {
1370                 // anchor is behind cursor -> move anchor behind the inset
1371                 normal.cell().next(normal.pos_);
1372         }
1373         //lyxerr << "normalizing: from " << Anchor_[Anchor_.size() - 1] << " to "
1374         //      << normal << "\n";
1375         return normal;
1376 }
1377
1378
1379 bool MathCursorPos::idxUp()
1380 {
1381         return par_->idxUp(idx_, pos_);
1382 }
1383
1384
1385 bool MathCursorPos::idxDown()
1386 {
1387         return par_->idxDown(idx_, pos_);
1388 }
1389
1390
1391 bool MathCursorPos::idxLeft()
1392 {
1393         return par_->idxLeft(idx_, pos_);
1394 }
1395
1396
1397 bool MathCursorPos::idxRight()
1398 {
1399         return par_->idxRight(idx_, pos_);
1400 }