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