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