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