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