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