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