]> git.lyx.org Git - lyx.git/blob - src/insets/insettabular.C
InsetTabular should now be feature-equal to table-support, small fixes.
[lyx.git] / src / insets / insettabular.C
1 /* This file is part of
2  * ======================================================
3  * 
4  *           LyX, The Document Processor
5  *
6  *           Copyright 2000 The LyX Team.
7  *
8  *======================================================
9  */
10
11 #include <config.h>
12
13 #include <fstream>
14 #include <algorithm>
15
16 #include <cstdlib>
17
18 #ifdef __GNUG__
19 #pragma implementation
20 #endif
21
22 #include "insettabular.h"
23
24 #include "buffer.h"
25 #include "commandtags.h"
26 #include "LaTeXFeatures.h"
27 #include "Painter.h"
28 #include "font.h"
29 #include "lyxtext.h"
30 #include "lyx_gui_misc.h"
31 #include "LyXView.h"
32 #include "lyxfunc.h"
33 #include "insets/insettext.h"
34
35 extern void MenuLayoutTabular(bool, InsetTabular *);
36 extern bool UpdateLayoutTabular(bool, InsetTabular *);
37 extern void TabularOptClose();
38
39 const int ADD_TO_HEIGHT = 2;
40 const int ADD_TO_TABULAR_WIDTH = 2;
41
42 using std::ostream;
43 using std::ifstream;
44 using std::max;
45 using std::endl;
46
47 #define cellstart(p) ((p % 2) == 0)
48
49 InsetTabular::InsetTabular(Buffer * buf, int rows, int columns)
50 {
51     if (rows <= 0)
52         rows = 1;
53     if (columns <= 0)
54         columns = 1;
55     buffer = buf; // set this first!!!
56     tabular = new LyXTabular(this, rows,columns);
57     // for now make it always display as display() inset
58     // just for test!!!
59     the_locking_inset = 0;
60     locked = no_selection = cursor_visible = false;
61     cursor.x_fix = -1;
62     oldcell = -1;
63     actcell = cursor.pos = 0;
64     sel_pos_start = sel_pos_end = sel_cell_start = sel_cell_end = 0;
65     init_inset = true;
66 }
67
68
69 InsetTabular::InsetTabular(InsetTabular const & tab, Buffer * buf)
70 {
71     buffer = buf; // set this first
72     tabular = new LyXTabular(this, *(tab.tabular));
73     the_locking_inset = 0;
74     locked = no_selection = cursor_visible = false;
75     cursor.x_fix = -1;
76     oldcell = -1;
77     actcell = cursor.pos = 0;
78     sel_pos_start = sel_pos_end = sel_cell_start = sel_cell_end = 0;
79     init_inset = true;
80 }
81
82
83 InsetTabular::~InsetTabular()
84 {
85     delete tabular;
86 }
87
88
89 Inset * InsetTabular::Clone() const
90 {
91     InsetTabular * t = new InsetTabular(*this, buffer);
92     delete t->tabular;
93     t->tabular = tabular->Clone(t);
94     return t;
95 }
96
97
98 void InsetTabular::Write(ostream & os) const
99 {
100     os << " Tabular" << endl;
101     tabular->Write(os);
102 }
103
104
105 void InsetTabular::Read(LyXLex & lex)
106 {
107     bool old_format = (lex.GetString() == "\\LyXTable");
108     string token;
109
110     if (tabular)
111         delete tabular;
112     tabular = new LyXTabular(this, lex);
113
114     init_inset = true;
115
116     if (old_format)
117         return;
118
119     lex.nextToken();
120     token = lex.GetString();
121     while (lex.IsOK() && (token != "\\end_inset")) {
122         lex.nextToken();
123         token = lex.GetString();
124     }
125     if (token != "\\end_inset") {
126         lex.printError("Missing \\end_inset at this point. "
127                        "Read: `$$Token'");
128     }
129 }
130
131
132 int InsetTabular::ascent(Painter & pain, LyXFont const & font) const
133 {
134     if (init_inset) {
135         calculate_width_of_cells(pain, font);
136         init_inset = false;
137     }
138     return tabular->GetAscentOfRow(0);
139 }
140
141
142 int InsetTabular::descent(Painter & pain, LyXFont const & font) const
143 {
144     if (init_inset) {
145         calculate_width_of_cells(pain, font);
146         init_inset = false;
147     }
148     return tabular->GetHeightOfTabular() - tabular->GetAscentOfRow(0);
149 }
150
151
152 int InsetTabular::width(Painter & pain, LyXFont const & font) const
153 {
154     if (init_inset) {
155         calculate_width_of_cells(pain, font);
156         init_inset = false;
157     }
158     return tabular->GetWidthOfTabular() + (2 * ADD_TO_TABULAR_WIDTH);
159 }
160
161
162 void InsetTabular::draw(Painter & pain, const LyXFont & font, int baseline,
163                         float & x) const
164 {
165     int i, j, cell=0;
166     int nx;
167     float cx;
168     bool reinit = false;
169
170     UpdatableInset::draw(pain,font,baseline,x);
171     if (init_inset || (top_x != int(x)) || (top_baseline != baseline)) {
172         int ox = top_x;
173         init_inset = false;
174         top_x = int(x);
175         top_baseline = baseline;
176         if (ox != top_x)
177             recomputeTextInsets(pain, font);
178         calculate_width_of_cells(pain, font);
179         resetPos(pain);
180         reinit = true;
181     }
182     x += ADD_TO_TABULAR_WIDTH;
183     for(i=0;i<tabular->rows();++i) {
184         nx = int(x);
185         for(j=0;j<tabular->columns();++j) {
186             if (tabular->IsPartOfMultiColumn(i,j))
187                 continue;
188             cx = nx + tabular->GetBeginningOfTextInCell(cell);
189             if (hasSelection())
190                 DrawCellSelection(pain, nx, baseline, i, j, cell);
191             tabular->GetCellInset(cell)->draw(pain, font, baseline, cx);
192             DrawCellLines(pain, nx, baseline, i, cell);
193             nx += tabular->GetWidthOfColumn(cell);
194             ++cell;
195         }
196         baseline += tabular->GetDescentOfRow(i) + tabular->GetAscentOfRow(i+1)
197             + tabular->GetAdditionalHeight(cell+1);
198     }
199     x += width(pain, font);
200 }
201
202
203 void InsetTabular::DrawCellLines(Painter & pain, int x, int baseline,
204                                  int row, int cell) const
205 {
206     int  x2 = x + tabular->GetWidthOfColumn(cell);
207     bool on_off;
208
209     if (!tabular->TopAlreadyDrawed(cell)) {
210         on_off = !tabular->TopLine(cell);
211         pain.line(x, baseline - tabular->GetAscentOfRow(row),
212                   x2, baseline -  tabular->GetAscentOfRow(row),
213                   on_off ? LColor::tabularonoffline:LColor::tabularline,
214                   on_off ? Painter::line_onoffdash:Painter::line_solid);
215     }
216     on_off = !tabular->BottomLine(cell);
217     pain.line(x,baseline +  tabular->GetDescentOfRow(row),
218               x2, baseline +  tabular->GetDescentOfRow(row),
219               on_off ? LColor::tabularonoffline:LColor::tabularline,
220               on_off ? Painter::line_onoffdash:Painter::line_solid);
221     if (!tabular->LeftAlreadyDrawed(cell)) {
222         on_off = !tabular->LeftLine(cell);
223         pain.line(x, baseline -  tabular->GetAscentOfRow(row),
224                   x, baseline +  tabular->GetDescentOfRow(row),
225                   on_off ? LColor::tabularonoffline:LColor::tabularline,
226                   on_off ? Painter::line_onoffdash:Painter::line_solid);
227     }
228     on_off = !tabular->RightLine(cell);
229     pain.line(x2 - tabular->GetAdditionalWidth(cell),
230               baseline -  tabular->GetAscentOfRow(row),
231               x2 - tabular->GetAdditionalWidth(cell),
232               baseline +  tabular->GetDescentOfRow(row),
233               on_off ? LColor::tabularonoffline:LColor::tabularline,
234               on_off ? Painter::line_onoffdash:Painter::line_solid);
235 }
236
237
238 void InsetTabular::DrawCellSelection(Painter & pain, int x, int baseline,
239                                      int row, int column, int cell) const
240 {
241     int tmp;
242
243     int cs = tabular->column_of_cell(sel_cell_start);
244     int ce = tabular->column_of_cell(sel_cell_end);
245     if (cs > ce) {
246         ce = cs;
247         cs = tabular->column_of_cell(sel_cell_end);
248     } else {
249         ce = tabular->right_column_of_cell(sel_cell_end);
250     }
251
252     int rs = tabular->row_of_cell(sel_cell_start);
253     int re = tabular->row_of_cell(sel_cell_end);
254     if (rs > re) {
255         tmp = rs;
256         rs = re;
257         re = tmp;
258     }
259
260     if ((column >= cs) && (column <= ce) && (row >= rs) && (row <= re)) {
261         int w = tabular->GetWidthOfColumn(cell);
262         int h = tabular->GetAscentOfRow(row) + tabular->GetDescentOfRow(row);
263         pain.fillRectangle(x, baseline - tabular->GetAscentOfRow(row),
264                            w, h, LColor::selection);
265     }
266 }
267
268
269 char const * InsetTabular::EditMessage() const
270 {
271     return _("Opened Tabular Inset");
272 }
273
274
275 void InsetTabular::Edit(BufferView * bv, int x, int y, unsigned int button)
276 {
277     UpdatableInset::Edit(bv, x, y, button);
278
279     if (!bv->lockInset(this)) {
280         lyxerr[Debug::INSETS] << "InsetTabular::Cannot lock inset" << endl;
281         return;
282     }
283     locked = true;
284     the_locking_inset = 0;
285     inset_pos = inset_x = inset_y = 0;
286     setPos(bv->painter(), x, y);
287     sel_pos_start = sel_pos_end = cursor.pos;
288     sel_cell_start = sel_cell_end = actcell;
289     bv->text->FinishUndo();
290     if (InsetHit(bv, x, y)) {
291         ActivateCellInset(bv, x, y, button);
292     }
293     UpdateLocal(bv, true);
294 //    bv->getOwner()->getPopups().updateFormTabular();
295 }
296
297
298 void InsetTabular::InsetUnlock(BufferView * bv)
299 {
300     TabularOptClose();
301     if (the_locking_inset) {
302         the_locking_inset->InsetUnlock(bv);
303         the_locking_inset = 0;
304     }
305     HideInsetCursor(bv);
306     if (hasSelection()) {
307         sel_pos_start = sel_pos_end = cursor.pos;
308         sel_cell_start = sel_cell_end = actcell;
309         UpdateLocal(bv, false);
310     }
311     no_selection = false;
312     oldcell = -1;
313     locked = false;
314 }
315
316 void InsetTabular::UpdateLocal(BufferView * bv, bool flag)
317 {
318     if (flag)
319         calculate_width_of_cells(bv->painter(), LyXFont(LyXFont::ALL_SANE));
320     bv->updateInset(this, flag);
321     if (flag)
322         resetPos(bv->painter());
323 }
324
325 bool InsetTabular::LockInsetInInset(BufferView * bv, UpdatableInset * inset)
326 {
327     lyxerr[Debug::INSETS] << "InsetTabular::LockInsetInInset(" <<inset<< "): ";
328     if (!inset)
329         return false;
330     oldcell = -1;
331     if (inset == tabular->GetCellInset(actcell)) {
332         lyxerr[Debug::INSETS] << "OK" << endl;
333         the_locking_inset = tabular->GetCellInset(actcell);
334         resetPos(bv->painter());
335         inset_x = cursor.x - top_x + tabular->GetBeginningOfTextInCell(actcell);
336         inset_y = cursor.y;
337         inset_pos = cursor.pos;
338         return true;
339     } else if (the_locking_inset && (the_locking_inset == inset)) {
340         if (cursor.pos == inset_pos) {
341             lyxerr[Debug::INSETS] << "OK" << endl;
342             resetPos(bv->painter());
343             inset_x = cursor.x - top_x + tabular->GetBeginningOfTextInCell(actcell);
344             inset_y = cursor.y;
345         } else {
346             lyxerr[Debug::INSETS] << "cursor.pos != inset_pos" << endl;
347         }
348     } else if (the_locking_inset) {
349         lyxerr[Debug::INSETS] << "MAYBE" << endl;
350         return the_locking_inset->LockInsetInInset(bv, inset);
351     }
352     lyxerr[Debug::INSETS] << "NOT OK" << endl;
353     return false;
354 }
355
356 bool InsetTabular::UnlockInsetInInset(BufferView * bv, UpdatableInset * inset,
357                                    bool lr)
358 {
359     if (!the_locking_inset)
360         return false;
361     if (the_locking_inset == inset) {
362         the_locking_inset->InsetUnlock(bv);
363         the_locking_inset = 0;
364         if (lr)
365             moveRight(bv, false);
366         return true;
367     }
368     if (the_locking_inset->UnlockInsetInInset(bv, inset, lr)) {
369         if ((inset->LyxCode() == TABULAR_CODE) &&
370             !the_locking_inset->GetFirstLockingInsetOfType(TABULAR_CODE))
371         {
372             UpdateLayoutTabular(true, const_cast<InsetTabular *>(this));
373             oldcell = actcell;
374         }
375         return true;
376     }
377     return false;
378 }
379
380 bool InsetTabular::UpdateInsetInInset(BufferView * bv, Inset * inset)
381 {
382     if (!the_locking_inset)
383         return false;
384     if (the_locking_inset != inset)
385         return the_locking_inset->UpdateInsetInInset(bv, inset);
386     UpdateLocal(bv);
387     return true;
388 }
389
390
391 int InsetTabular::InsetInInsetY()
392 {
393     if (!the_locking_inset)
394         return 0;
395
396     return (inset_y + the_locking_inset->InsetInInsetY());
397 }
398
399
400 UpdatableInset * InsetTabular::GetLockingInset()
401 {
402     return the_locking_inset ? the_locking_inset->GetLockingInset() : this;
403 }
404
405
406 UpdatableInset * InsetTabular::GetFirstLockingInsetOfType(Inset::Code c)
407 {
408     if (c == LyxCode())
409         return this;
410     if (the_locking_inset)
411         return the_locking_inset->GetFirstLockingInsetOfType(c);
412     return 0;
413 }
414
415
416 bool InsetTabular::InsertInset(BufferView * bv, Inset * inset)
417 {
418     if (the_locking_inset)
419         return the_locking_inset->InsertInset(bv, inset);
420     return false;
421 }
422
423
424 void InsetTabular::InsetButtonRelease(BufferView * bv,
425                                       int x, int y, int button)
426 {
427     if (button == 3) {
428         if (the_locking_inset) {
429             UpdatableInset * i;
430             if ((i=the_locking_inset->GetFirstLockingInsetOfType(TABULAR_CODE))) {
431                 i->InsetButtonRelease(bv, x, y, button);
432                 return;
433             }
434         }
435         MenuLayoutTabular(true, this);
436         return;
437     }
438     if (the_locking_inset) {
439         the_locking_inset->InsetButtonRelease(bv, x-inset_x, y-inset_y,button);
440         return;
441     }
442     no_selection = false;
443 }
444
445
446 void InsetTabular::InsetButtonPress(BufferView * bv, int x, int y, int button)
447 {
448     if (hasSelection()) {
449         sel_pos_start = sel_pos_end = sel_cell_start = sel_cell_end = 0;
450         UpdateLocal(bv, false);
451     }
452     no_selection = false;
453
454     int ocell = actcell;
455
456     setPos(bv->painter(), x, y);
457     sel_pos_start = sel_pos_end = cursor.pos;
458     sel_cell_start = sel_cell_end = actcell;
459
460     bool inset_hit = InsetHit(bv, x, y);
461
462     if ((ocell == actcell) && the_locking_inset && inset_hit) {
463         the_locking_inset->InsetButtonPress(bv, x-inset_x, y-inset_y, button);
464         return;
465     } else if (the_locking_inset) {
466         the_locking_inset->InsetUnlock(bv);
467     }
468     the_locking_inset = 0;
469     if (inset_hit && bv->the_locking_inset) {
470         ActivateCellInset(bv, x, y, button);
471         the_locking_inset->InsetButtonPress(bv, x-inset_x, y-inset_y, button);
472     }
473     
474 #if 0
475     if (button == 3)
476         bview->getOwner()->getPopups().showFormTabular();
477     else if (ocell != actcell)
478         bview->getOwner()->getPopups().updateFormTabular();
479 #endif
480 }
481
482
483 void InsetTabular::InsetMotionNotify(BufferView * bv, int x, int y, int button)
484 {
485     if (the_locking_inset) {
486         the_locking_inset->InsetMotionNotify(bv, x - inset_x,
487                                              y - inset_y, button);
488         return;
489     }
490     if (!no_selection) {
491             // int ocell = actcell,
492             int old = sel_pos_end;
493
494         setPos(bv->painter(), x, y);
495         sel_pos_end = cursor.pos;
496         sel_cell_end = actcell;
497         if (old != sel_pos_end)
498             UpdateLocal(bv, false);
499 #if 0
500         if (ocell != actcell)
501             bview->getOwner()->getPopups().updateFormTabular();
502 #endif
503     }
504     no_selection = false;
505 }
506
507
508 void InsetTabular::InsetKeyPress(XKeyEvent * xke)
509 {
510     if (the_locking_inset) {
511         the_locking_inset->InsetKeyPress(xke);
512         return;
513     }
514 }
515
516
517 UpdatableInset::RESULT InsetTabular::LocalDispatch(BufferView * bv, int action,
518                                                    string const & arg)
519 {
520     UpdatableInset::RESULT 
521         result;
522
523     no_selection = false;
524     if (((result=UpdatableInset::LocalDispatch(bv, action, arg)) == DISPATCHED)
525         || (result == DISPATCHED_NOUPDATE)) {
526
527         resetPos(bv->painter());
528         return result;
529     }
530     result=DISPATCHED;
531
532     if ((action < 0) && arg.empty())
533         return FINISHED;
534
535     if ((action != LFUN_DOWN) && (action != LFUN_UP) &&
536         (action != LFUN_DOWNSEL) && (action != LFUN_UPSEL))
537         cursor.x_fix = -1;
538     if (the_locking_inset) {
539         result=the_locking_inset->LocalDispatch(bv, action, arg);
540         if (result == DISPATCHED_NOUPDATE)
541             return result;
542         else if (result == DISPATCHED) {
543             bool upd = SetCellDimensions(bv->painter(), actcell, actrow);
544             the_locking_inset->ToggleInsetCursor(bv);
545             UpdateLocal(bv, upd);
546             the_locking_inset->ToggleInsetCursor(bv);
547             return result;
548         } else if (result == FINISHED) {
549             if ((action == LFUN_RIGHT) || (action == -1)) {
550                 cursor.pos = inset_pos + 1;
551                 resetPos(bv->painter());
552             }
553             sel_pos_start = sel_pos_end = cursor.pos;
554             sel_cell_start = sel_cell_end = actcell;
555             the_locking_inset=0;
556             result = DISPATCHED;
557             return result;
558         }
559     }
560
561     bool hs = hasSelection();
562     HideInsetCursor(bv);
563     switch (action) {
564         // Normal chars not handled here
565     case -1:
566         break;
567         // --- Cursor Movements ---------------------------------------------
568     case LFUN_RIGHTSEL:
569         if (tabular->IsLastCellInRow(actcell) && !cellstart(cursor.pos))
570             break;
571         moveRight(bv, false);
572         sel_pos_end = cursor.pos;
573         if (!cellstart(cursor.pos)) {
574             if (tabular->right_column_of_cell(sel_cell_start) >
575                 tabular->right_column_of_cell(actcell))
576                 sel_cell_end = actcell+1;
577             else
578                 sel_cell_end = actcell;
579         }
580         UpdateLocal(bv, false);
581         break;
582     case LFUN_RIGHT:
583         result = moveRight(bv);
584         sel_pos_start = sel_pos_end = cursor.pos;
585         sel_cell_start = sel_cell_end = actcell;
586         if (hs)
587             UpdateLocal(bv, false);
588         break;
589     case LFUN_LEFTSEL:
590         if (tabular->IsFirstCellInRow(actcell) && cellstart(cursor.pos))
591             break;
592         moveLeft(bv, false);
593         sel_pos_end = cursor.pos;
594         if (cellstart(cursor.pos)) {
595             if (tabular->column_of_cell(sel_cell_start) >=
596                 tabular->column_of_cell(actcell))
597                 sel_cell_end = actcell;
598             else
599                 sel_cell_end = actcell-1;
600         }
601         UpdateLocal(bv, false);
602         break;
603     case LFUN_LEFT:
604         result = moveLeft(bv);
605         sel_pos_start = sel_pos_end = cursor.pos;
606         sel_cell_start = sel_cell_end = actcell;
607         if (hs)
608             UpdateLocal(bv, false);
609         break;
610     case LFUN_DOWNSEL:
611     {
612         int ocell = actcell;
613         moveDown(bv);
614         sel_pos_end = cursor.pos;
615         if ((ocell == sel_cell_end) ||
616             (tabular->column_of_cell(ocell)>tabular->column_of_cell(actcell)))
617             sel_cell_end = tabular->GetCellBelow(sel_cell_end);
618         else
619             sel_cell_end = tabular->GetLastCellBelow(sel_cell_end);
620         UpdateLocal(bv, false);
621     }
622     break;
623     case LFUN_DOWN:
624         result= moveDown(bv);
625         sel_pos_start = sel_pos_end = cursor.pos;
626         sel_cell_start = sel_cell_end = actcell;
627         if (hs)
628             UpdateLocal(bv, false);
629         break;
630     case LFUN_UPSEL:
631     {
632         int ocell = actcell;
633         moveUp(bv);
634         sel_pos_end = cursor.pos;
635         if ((ocell == sel_cell_end) ||
636             (tabular->column_of_cell(ocell)>tabular->column_of_cell(actcell)))
637             sel_cell_end = tabular->GetCellAbove(sel_cell_end);
638         else
639             sel_cell_end = tabular->GetLastCellAbove(sel_cell_end);
640         UpdateLocal(bv, false);
641     }
642     break;
643     case LFUN_UP:
644         result= moveUp(bv);
645         sel_pos_start = sel_pos_end = cursor.pos;
646         sel_cell_start = sel_cell_end = actcell;
647         if (hs)
648             UpdateLocal(bv, false);
649         break;
650     case LFUN_BACKSPACE:
651         break;
652     case LFUN_DELETE:
653         break;
654     case LFUN_HOME:
655         break;
656     case LFUN_END:
657         break;
658     case LFUN_SHIFT_TAB:
659     case LFUN_TAB:
660         if (the_locking_inset) {
661             the_locking_inset->InsetUnlock(bv);
662         }
663         the_locking_inset = 0;
664         if (action == LFUN_TAB)
665             moveNextCell(bv);
666         else
667             movePrevCell(bv);
668         sel_pos_start = sel_pos_end = cursor.pos;
669         sel_cell_start = sel_cell_end = actcell;
670         if (hs)
671             UpdateLocal(bv, false);
672         break;
673     case LFUN_LAYOUT_TABLE:
674     {
675         int flag = (arg == "true");
676         MenuLayoutTabular(flag, this);
677     }
678     break;
679     default:
680         result = UNDISPATCHED;
681         break;
682     }
683     if (result!=FINISHED) {
684         if (!the_locking_inset) {
685 #if 0       
686             if (ocell != actcell)
687                 bview->getOwner()->getPopups().updateFormTabular();
688 #endif
689             ShowInsetCursor(bv);
690         }
691     } else
692         bv->unlockInset(this);
693     return result;
694 }
695
696
697 int InsetTabular::Latex(ostream & os, bool fragile, bool fp) const
698 {
699     return tabular->Latex(os, fragile, fp);
700 }
701
702
703 int InsetTabular::Ascii(ostream &) const
704 {
705     return 0;
706 }
707
708 int InsetTabular::Linuxdoc(ostream &) const
709 {
710     return 0;
711 }
712
713
714 int InsetTabular::DocBook(ostream &) const
715 {
716     return 0;
717 }
718
719
720 void InsetTabular::Validate(LaTeXFeatures & features) const
721 {
722     tabular->Validate(features);
723 }
724
725
726 void InsetTabular::calculate_width_of_cells(Painter & pain,
727                                             LyXFont const & font) const
728 {
729     int cell = -1;
730     int maxAsc, maxDesc;
731     InsetText * inset;
732     
733     for(int i = 0; i < tabular->rows(); ++i) {
734         maxAsc = maxDesc = 0;
735         for(int j= 0; j < tabular->columns(); ++j) {
736             if (tabular->IsPartOfMultiColumn(i,j))
737                 continue;
738             ++cell;
739             inset = tabular->GetCellInset(cell);
740             maxAsc = max(maxAsc, inset->ascent(pain, font));
741             maxDesc = max(maxDesc, inset->descent(pain, font));
742             tabular->SetWidthOfCell(cell, inset->width(pain, font));
743         }
744         tabular->SetAscentOfRow(i, maxAsc + ADD_TO_HEIGHT);
745         tabular->SetDescentOfRow(i, maxDesc + ADD_TO_HEIGHT);
746     }
747 }
748
749
750 void InsetTabular::GetCursorPos(int & x, int & y) const
751 {
752     x = cursor.x-top_x;
753     y = cursor.y;
754 }
755
756
757 void InsetTabular::ToggleInsetCursor(BufferView * bv)
758 {
759     if (the_locking_inset) {
760         the_locking_inset->ToggleInsetCursor(bv);
761         return;
762     }
763
764     LyXFont font; // = the_locking_inset->GetFont(par, cursor.pos);
765
766     int asc = lyxfont::maxAscent(font);
767     int desc = lyxfont::maxDescent(font);
768   
769     if (cursor_visible)
770         bv->hideLockedInsetCursor();
771     else
772         bv->showLockedInsetCursor(cursor.x, cursor.y, asc, desc);
773     cursor_visible = !cursor_visible;
774 }
775
776
777 void InsetTabular::ShowInsetCursor(BufferView * bv)
778 {
779     if (!cursor_visible) {
780         LyXFont font; // = GetFont(par, cursor.pos);
781     
782         int asc = lyxfont::maxAscent(font);
783         int desc = lyxfont::maxDescent(font);
784         bv->fitLockedInsetCursor(cursor.x, cursor.y, asc, desc);
785         bv->showLockedInsetCursor(cursor.x, cursor.y, asc, desc);
786         cursor_visible = true;
787     }
788 }
789
790
791 void InsetTabular::HideInsetCursor(BufferView * bv)
792 {
793     if (cursor_visible)
794         ToggleInsetCursor(bv);
795 }
796
797
798 void InsetTabular::setPos(Painter & pain, int x, int y) const
799 {
800     cursor.y = cursor.pos = actcell = actrow = actcol = 0;
801     int ly = tabular->GetDescentOfRow(actrow);
802
803     // first search the right row
804     while((ly < y) && (actrow < tabular->rows())) {
805         cursor.y += tabular->GetDescentOfRow(actrow) +
806             tabular->GetAscentOfRow(actrow+1) +
807             tabular->GetAdditionalHeight(tabular->GetCellNumber(actrow+1,
808                                                                 actcol));
809         ++actrow;
810         ly = cursor.y + tabular->GetDescentOfRow(actrow);
811     }
812     actcell = tabular->GetCellNumber(actrow, actcol);
813
814     // now search the right column
815     int lx = tabular->GetWidthOfColumn(actcell) -
816         tabular->GetAdditionalWidth(actcell);
817     for(; !tabular->IsLastCellInRow(actcell) && (lx < x);
818         ++actcell,lx += tabular->GetWidthOfColumn(actcell) +
819             tabular->GetAdditionalWidth(actcell-1));
820     cursor.pos = ((actcell+1) * 2) - 1;
821     resetPos(pain);
822     if ((lx - (tabular->GetWidthOfColumn(actcell)/2)) < x) {
823         cursor.x = lx + top_x - 2;
824     } else {
825         --cursor.pos;
826         cursor.x = lx - tabular->GetWidthOfColumn(actcell) + top_x + 2;
827     }
828     resetPos(pain);
829 }
830
831 int InsetTabular::getCellXPos(int cell) const
832 {
833     int c;
834
835     for(c=cell;!tabular->IsFirstCellInRow(c);--c)
836         ;
837     int lx = tabular->GetWidthOfColumn(cell);
838     for(; (c < cell); ++c) {
839         lx += tabular->GetWidthOfColumn(c);
840     }
841     return (lx - tabular->GetWidthOfColumn(cell) + top_x +
842             ADD_TO_TABULAR_WIDTH);
843 }
844
845 void InsetTabular::resetPos(Painter & pain) const
846 {
847     if (!locked)
848         return;
849     actcol = tabular->column_of_cell(actcell);
850
851     int cell = 0;
852     actrow = cursor.y = 0;
853     for(; (cell<actcell) && !tabular->IsLastRow(cell); ++cell) {
854         if (tabular->IsLastCellInRow(cell)) {
855             cursor.y += tabular->GetDescentOfRow(actrow) +
856                 tabular->GetAscentOfRow(actrow+1) +
857                 tabular->GetAdditionalHeight(cell+1);
858             ++actrow;
859         }
860     }
861     cursor.x = getCellXPos(actcell) + 2;
862     if (cursor.pos % 2) {
863         LyXFont font(LyXFont::ALL_SANE);
864         cursor.x += tabular->GetCellInset(actcell)->width(pain,font) +
865                 tabular->GetBeginningOfTextInCell(actcell);
866     }
867     if ((!the_locking_inset ||
868          !the_locking_inset->GetFirstLockingInsetOfType(TABULAR_CODE)) &&
869         (actcell != oldcell)) {
870         UpdateLayoutTabular(true, const_cast<InsetTabular *>(this));
871         oldcell = actcell;
872     }
873 }
874
875
876 bool InsetTabular::SetCellDimensions(Painter & pain, int cell, int row)
877 {
878     InsetText * inset = tabular->GetCellInset(cell);
879     LyXFont font(LyXFont::ALL_SANE);
880     int asc = inset->ascent(pain, font) + ADD_TO_HEIGHT;
881     int desc = inset->descent(pain, font) + ADD_TO_HEIGHT;
882     int maxAsc = tabular->GetAscentOfRow(row);
883     int maxDesc = tabular->GetDescentOfRow(row);
884     bool ret = tabular->SetWidthOfCell(cell, inset->width(pain, font));
885
886     if (maxAsc < asc) {
887         ret = true;
888         tabular->SetAscentOfRow(row, asc);
889     }
890     if (maxDesc < desc) {
891         ret = true;
892         tabular->SetDescentOfRow(row, desc);
893     }
894     return ret;
895 }
896
897
898 UpdatableInset::RESULT InsetTabular::moveRight(BufferView * bv, bool lock)
899 {
900     if (!cellstart(cursor.pos)) {
901         if (tabular->IsLastCell(actcell))
902             return FINISHED;
903         ++actcell;
904         ++cursor.pos;
905     } else if (lock) {
906         if (ActivateCellInset(bv))
907             return DISPATCHED;
908     } else {              // before the inset
909         ++cursor.pos;
910     }
911     resetPos(bv->painter());
912     return DISPATCHED_NOUPDATE;
913 }
914
915
916 UpdatableInset::RESULT InsetTabular::moveLeft(BufferView * bv, bool lock)
917 {
918     if (!cursor.pos) {
919         if (!actcell)
920             return FINISHED;
921         cursor.pos = 2;
922     }
923     --cursor.pos;
924     if (!cellstart(cursor.pos)) {
925         --actcell;
926     } else if (lock) {       // behind the inset
927         if (ActivateCellInset(bv, 0, 0, 0, true))
928             return DISPATCHED;
929     }
930     resetPos(bv->painter());
931     return DISPATCHED_NOUPDATE;
932 }
933
934
935 UpdatableInset::RESULT InsetTabular::moveUp(BufferView * bv)
936 {
937     int ocell = actcell;
938     actcell = tabular->GetCellAbove(actcell);
939     if (actcell == ocell) // we moved out of the inset
940         return FINISHED;
941     resetPos(bv->painter());
942     return DISPATCHED_NOUPDATE;
943 }
944
945
946 UpdatableInset::RESULT InsetTabular::moveDown(BufferView * bv)
947 {
948     int ocell = actcell;
949     actcell = tabular->GetCellBelow(actcell);
950     if (actcell == ocell) // we moved out of the inset
951         return FINISHED;
952     resetPos(bv->painter());
953     return DISPATCHED_NOUPDATE;
954 }
955
956
957 bool InsetTabular::moveNextCell(BufferView * bv)
958 {
959     if (tabular->IsLastCell(actcell))
960         return false;
961     ++actcell;
962     ++cursor.pos;
963     if (!cellstart(cursor.pos))
964         ++cursor.pos;
965     resetPos(bv->painter());
966     return true;
967 }
968
969
970 bool InsetTabular::movePrevCell(BufferView * bv)
971 {
972     if (!actcell) // first cell
973         return false;
974     --actcell;
975     --cursor.pos;
976     if (cellstart(cursor.pos))
977         --cursor.pos;
978     resetPos(bv->painter());
979     return true;
980 }
981
982
983 bool InsetTabular::Delete()
984 {
985     return true;
986 }
987
988
989 void InsetTabular::SetFont(BufferView *, LyXFont const &, bool)
990 {
991 }
992
993
994 void InsetTabular::TabularFeatures(BufferView * bv, int feature, string val)
995 {
996     int
997         i, j,
998         sel_col_start,
999         sel_col_end,
1000         sel_row_start,
1001         sel_row_end,
1002         setLines = 0,
1003         setAlign = LYX_ALIGN_LEFT,
1004         lineSet;
1005     bool
1006         what;
1007
1008     switch (feature) {
1009       case LyXTabular::ALIGN_LEFT:
1010           setAlign=LYX_ALIGN_LEFT;
1011           break;
1012       case LyXTabular::ALIGN_RIGHT:
1013           setAlign=LYX_ALIGN_RIGHT;
1014           break;
1015       case LyXTabular::ALIGN_CENTER:
1016           setAlign=LYX_ALIGN_CENTER;
1017           break;
1018       default:
1019           break;
1020     }
1021     if (hasSelection()) {
1022         int tmp;
1023         sel_col_start = tabular->column_of_cell(sel_cell_start);
1024         sel_col_end = tabular->column_of_cell(sel_cell_end);
1025         if (sel_col_start > sel_col_end) {
1026             sel_col_end = sel_col_start;
1027             sel_col_start = tabular->column_of_cell(sel_cell_end);
1028         } else {
1029             sel_col_end = tabular->right_column_of_cell(sel_cell_end);
1030         }
1031         
1032         sel_row_start = tabular->row_of_cell(sel_cell_start);
1033         sel_row_end = tabular->row_of_cell(sel_cell_end);
1034         if (sel_row_start > sel_row_end) {
1035             tmp = sel_row_start;
1036             sel_row_start = sel_row_end;
1037             sel_row_end = tmp;
1038         }
1039     } else {
1040         sel_col_start = sel_col_end = tabular->column_of_cell(actcell);
1041         sel_row_start = sel_row_end = tabular->row_of_cell(actcell);
1042     }
1043     bv->text->SetUndo(Undo::FINISH, 
1044               bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->previous,
1045               bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->next);
1046
1047     int row = tabular->row_of_cell(actcell);
1048     int column = tabular->column_of_cell(actcell);
1049
1050     switch (feature) {
1051     case LyXTabular::SET_PWIDTH:
1052     {
1053         bool update = (tabular->GetPWidth(actcell) != val);
1054         tabular->SetPWidth(actcell,val);
1055         if (update)
1056             UpdateLocal(bv, true);
1057     }
1058     break;
1059     case LyXTabular::SET_SPECIAL_COLUMN:
1060     case LyXTabular::SET_SPECIAL_MULTI:
1061         tabular->SetAlignSpecial(actcell,val,feature);
1062         break;
1063     case LyXTabular::APPEND_ROW:
1064         // append the row into the tabular
1065         tabular->AppendRow(actcell);
1066         UpdateLocal(bv, true);
1067         break;
1068     case LyXTabular::APPEND_COLUMN:
1069         // append the column into the tabular
1070         tabular->AppendColumn(actcell);
1071         actcell = tabular->GetCellNumber(row, column);
1072         UpdateLocal(bv, true);
1073         break;
1074     case LyXTabular::DELETE_ROW:
1075         tabular->DeleteRow(tabular->row_of_cell(actcell));
1076         if ((row+1) > tabular->rows())
1077             --row;
1078         actcell = tabular->GetCellNumber(row, column);
1079         UpdateLocal(bv, true);
1080         break;
1081     case LyXTabular::DELETE_COLUMN:
1082         tabular->DeleteColumn(tabular->column_of_cell(actcell));
1083         if ((column+1) > tabular->columns())
1084             --column;
1085         actcell = tabular->GetCellNumber(row, column);
1086         UpdateLocal(bv, true);
1087         break;
1088     case LyXTabular::TOGGLE_LINE_TOP:
1089         lineSet = !tabular->TopLine(actcell);
1090         for(i=sel_row_start; i<=sel_row_end; ++i)
1091             for(j=sel_col_start; j<=sel_col_end; ++j)
1092                 tabular->SetTopLine(tabular->GetCellNumber(i,j),lineSet);
1093         UpdateLocal(bv, true);
1094         break;
1095     
1096     case LyXTabular::TOGGLE_LINE_BOTTOM:
1097         lineSet = !tabular->BottomLine(actcell); 
1098         for(i=sel_row_start; i<=sel_row_end; ++i)
1099             for(j=sel_col_start; j<=sel_col_end; ++j)
1100                 tabular->SetBottomLine(tabular->GetCellNumber(i,j),lineSet);
1101         UpdateLocal(bv, true);
1102         break;
1103                 
1104     case LyXTabular::TOGGLE_LINE_LEFT:
1105         lineSet = !tabular->LeftLine(actcell);
1106         for(i=sel_row_start; i<=sel_row_end; ++i)
1107             for(j=sel_col_start; j<=sel_col_end; ++j)
1108                 tabular->SetLeftLine(tabular->GetCellNumber(i,j),lineSet);
1109         UpdateLocal(bv, true);
1110         break;
1111
1112     case LyXTabular::TOGGLE_LINE_RIGHT:
1113         lineSet = !tabular->RightLine(actcell);
1114         for(i=sel_row_start; i<=sel_row_end; ++i)
1115             for(j=sel_col_start; j<=sel_col_end; ++j)
1116                 tabular->SetRightLine(tabular->GetCellNumber(i,j),lineSet);
1117         UpdateLocal(bv, true);
1118         break;
1119     case LyXTabular::ALIGN_LEFT:
1120     case LyXTabular::ALIGN_RIGHT:
1121     case LyXTabular::ALIGN_CENTER:
1122         for(i=sel_row_start; i<=sel_row_end; ++i)
1123             for(j=sel_col_start; j<=sel_col_end; ++j)
1124                 tabular->SetAlignment(tabular->GetCellNumber(i,j),setAlign);
1125         UpdateLocal(bv, true);
1126         break;
1127     case LyXTabular::MULTICOLUMN:
1128     {
1129         if (sel_row_start != sel_row_end) {
1130             WriteAlert(_("Impossible Operation!"), 
1131                        _("Multicolumns can only be horizontally."), 
1132                        _("Sorry."));
1133             return;
1134         }
1135         // just multicol for one Single Cell
1136         if (!hasSelection()) {
1137             // check wether we are completly in a multicol
1138             if (tabular->IsMultiColumn(actcell)) {
1139                 tabular->UnsetMultiColumn(actcell);
1140                 UpdateLocal(bv, true);
1141             } else {
1142                 tabular->SetMultiColumn(actcell, 1);
1143                 UpdateLocal(bv, false);
1144             }
1145             return;
1146         }
1147         // we have a selection so this means we just add all this
1148         // cells to form a multicolumn cell
1149         int
1150             s_start, s_end;
1151
1152         if (sel_cell_start > sel_cell_end) {
1153             s_start = sel_cell_end;
1154             s_end = sel_cell_start;
1155         } else {
1156             s_start = sel_cell_start;
1157             s_end = sel_cell_end;
1158         }
1159         tabular->SetMultiColumn(s_start, s_end-s_start+1);
1160         actcell = s_start;
1161         cursor.pos = 0;
1162         sel_cell_end = sel_cell_start;
1163         sel_pos_end = sel_pos_start;
1164         UpdateLocal(bv, true);
1165         break;
1166     }
1167     case LyXTabular::SET_ALL_LINES:
1168         setLines = 1;
1169     case LyXTabular::UNSET_ALL_LINES:
1170         for(i=sel_row_start; i<=sel_row_end; ++i)
1171             for(j=sel_col_start; j<=sel_col_end; ++j)
1172                 tabular->SetAllLines(tabular->GetCellNumber(i,j), setLines);
1173         UpdateLocal(bv, true);
1174         break;
1175     case LyXTabular::SET_LONGTABULAR:
1176         tabular->SetLongTabular(true);
1177         UpdateLocal(bv, true); // because this toggles displayed
1178         break;
1179     case LyXTabular::UNSET_LONGTABULAR:
1180         tabular->SetLongTabular(false);
1181         UpdateLocal(bv, true); // because this toggles displayed
1182         break;
1183     case LyXTabular::SET_ROTATE_TABULAR:
1184         tabular->SetRotateTabular(true);
1185         break;
1186     case LyXTabular::UNSET_ROTATE_TABULAR:
1187         tabular->SetRotateTabular(false);
1188         break;
1189     case LyXTabular::SET_ROTATE_CELL:
1190         for(i=sel_row_start; i<=sel_row_end; ++i)
1191             for(j=sel_col_start; j<=sel_col_end; ++j)
1192                 tabular->SetRotateCell(tabular->GetCellNumber(i,j),true);
1193         break;
1194     case LyXTabular::UNSET_ROTATE_CELL:
1195         for(i=sel_row_start; i<=sel_row_end; ++i)
1196             for(j=sel_col_start; j<=sel_col_end; ++j)
1197                 tabular->SetRotateCell(tabular->GetCellNumber(i,j),false);
1198         break;
1199     case LyXTabular::SET_LINEBREAKS:
1200         what = !tabular->GetLinebreaks(actcell);
1201         for(i=sel_row_start; i<=sel_row_end; ++i)
1202             for(j=sel_col_start; j<=sel_col_end; ++j)
1203                 tabular->SetLinebreaks(tabular->GetCellNumber(i,j),what);
1204         break;
1205     case LyXTabular::SET_LTFIRSTHEAD:
1206         tabular->SetLTHead(actcell,true);
1207         break;
1208     case LyXTabular::SET_LTHEAD:
1209         tabular->SetLTHead(actcell,false);
1210         break;
1211     case LyXTabular::SET_LTFOOT:
1212         tabular->SetLTFoot(actcell,false);
1213         break;
1214     case LyXTabular::SET_LTLASTFOOT:
1215         tabular->SetLTFoot(actcell,true);
1216         break;
1217     case LyXTabular::SET_LTNEWPAGE:
1218         what = !tabular->GetLTNewPage(actcell);
1219         tabular->SetLTNewPage(actcell,what);
1220         break;
1221     }
1222 }
1223
1224 bool InsetTabular::ActivateCellInset(BufferView * bv, int x, int y, int button,
1225                                      bool behind)
1226 {
1227     // the cursor.pos has to be before the inset so if it isn't now just
1228     // reset the curor pos first!
1229     if (cursor.pos % 2) { // behind the inset
1230         --cursor.pos;
1231         resetPos(bv->painter());
1232     }
1233     UpdatableInset * inset =
1234         static_cast<UpdatableInset*>(tabular->GetCellInset(actcell));
1235     LyXFont font(LyXFont::ALL_SANE);
1236     if (behind) {
1237         x = inset->x() + inset->width(bv->painter(), font);
1238         y = inset->descent(bv->painter(), font);
1239     }
1240     inset_x = cursor.x - top_x + tabular->GetBeginningOfTextInCell(actcell);
1241     inset_y = cursor.y;
1242     inset->Edit(bv, x-inset_x, y-inset_y, button);
1243     if (!the_locking_inset)
1244         return false;
1245     UpdateLocal(bv, true);
1246     return true;
1247 }
1248
1249 bool InsetTabular::InsetHit(BufferView * bv, int x, int ) const
1250 {
1251     InsetText * inset = tabular->GetCellInset(actcell);
1252     int x1 = x + top_x;
1253
1254     if (cursor.pos % 2) { // behind the inset
1255         return (((x+top_x) < cursor.x) &&
1256                 ((x+top_x) > (cursor.x - inset->width(bv->painter(),
1257                                                       LyXFont(LyXFont::ALL_SANE)))));
1258     } else {
1259         int x2 = cursor.x + tabular->GetBeginningOfTextInCell(actcell);
1260         return ((x1 > x2) &&
1261                 (x1 < (x2 + inset->width(bv->painter(),
1262                                          LyXFont(LyXFont::ALL_SANE)))));
1263     }
1264 }
1265
1266 // This returns paperWidth() if the cell-width is unlimited or the width
1267 // in pixels if we have a pwidth for this cell.
1268 int InsetTabular::GetMaxWidthOfCell(Painter & pain, int cell) const
1269 {
1270     string w;
1271         
1272     if ((w=tabular->GetPWidth(cell)).empty())
1273         return pain.paperWidth();
1274     return VSpace(w).inPixels( 0, 0);
1275 }
1276
1277 int InsetTabular::getMaxWidth(Painter & pain,
1278                               UpdatableInset const * inset) const
1279 {
1280     int cell;
1281     int n = tabular->GetNumberOfCells();
1282     for(cell=0; cell < n; ++cell) {
1283         if (tabular->GetCellInset(cell) == inset)
1284             break;
1285     }
1286     if (cell >= n)
1287         return pain.paperWidth();
1288     int w = GetMaxWidthOfCell(pain, cell);
1289     // this because text insets remove the xpos from the maxwidth because
1290     // otherwise the would not break good!!!
1291 //    w += getCellXPos(cell) + tabular->GetBeginningOfTextInCell(cell);
1292 //    w += inset->x();
1293     return w;
1294 }
1295
1296 void InsetTabular::recomputeTextInsets(Painter & pain, const LyXFont & font) const
1297 {
1298     InsetText * inset;
1299     int cx, cell;
1300
1301     cx = top_x;
1302     for(int j= 0; j < tabular->columns(); ++j) {
1303         for(int i = 0; i < tabular->rows(); ++i) {
1304             if (tabular->IsPartOfMultiColumn(i,j))
1305                 continue;
1306             cell = tabular->GetCellNumber(i,j);
1307             inset = tabular->GetCellInset(cell);
1308             inset->computeTextRows(pain);
1309             tabular->SetWidthOfCell(cell, inset->width(pain, font));
1310         }
1311         cell = tabular->GetCellNumber(0, j);
1312         cx += tabular->GetWidthOfColumn(cell);
1313     }
1314 }