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