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