1 /**
2 *  Copyright Mikael H�gdahl - triyana@users.sourceforge.net
3 *
4 *  This source is distributed under the terms of the Q Public License version 1.0,
5 *  created by Trolltech (www.trolltech.com).
6 */
7 
8 #include "Fl_Table.h"
9 #include "Fl_Table_Data.h"
10 #include "Fl_Select.h"
11 #include "Fl_Find.h"
12 #include "Fl_Defines.h"
13 #include <FL/Fl.H>
14 #include <FL/fl_ask.H>
15 #include <FL/fl_draw.H>
16 #include <FL/Fl_Choice.H>
17 #include <FL/Fl_Check_Button.H>
18 #include <FL/Fl_File_Chooser.H>
19 #include <FL/Fl_Input.H>
20 #include <FL/Fl_Scrollbar.H>
21 #include <FL/fl_show_colormap.H>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <ctype.h>
26 
27 
28 
29 /**
30 *  Callback for vertical scrollbar
31 */
cb_ver(Fl_Widget * w,void * o)32 void Fl_Table::cb_ver (Fl_Widget* w, void* o) {
33     ((Fl_Table*)o)->cb_ver();
34 }
35 
36 
37 
38 /**
39 *  Callback for horizontal scrollbar
40 */
cb_hor(Fl_Widget * w,void * o)41 void Fl_Table::cb_hor (Fl_Widget* w, void* o) {
42    ((Fl_Table*)o)->cb_hor();
43 }
44 
45 
46 
47 /**
48 *  Create table widget
49 *  @param Fl_Table_Data - Data object
50 *  @param int           - X pos
51 *  @param int           - Y pos
52 *  @param int           - Width
53 *  @param int           - Height
54 */
Fl_Table(Fl_Window * dlgParent,Fl_Table_Data * data,int x,int y,int w,int h)55 Fl_Table::Fl_Table (Fl_Window* dlgParent, Fl_Table_Data* data, int x, int y, int w, int h) :
56 Fl_Group (x, y, w, h, 0) {
57     aData            = data;
58     aDlgParent       = dlgParent;
59     aCurrCol         = 0;
60     aCurrRow         = 0;
61     aStartCol        = 0;
62     aStartRow        = 0;
63     aWidth           = 0;
64     aVisibleRows     = 0;
65     aMaxVisibleRows  = 0;
66     aVisibleCols     = 0;
67     aWidth           = 0;
68     aClipped         = 0;
69     *aSearchWord     = '\0';
70     aCase            = false;
71     aEditWidget      = 0;
72     aDragState       = DRAG_NONE;
73     aEventHandler    = 0;
74     aFind            = new Fl_Find ();
75 
76     begin();
77         aDummy        = new Fl_Input (0, 0, 0, 0);
78         aScrollbarVer = new Fl_Scrollbar (x + w - SCROLL_SIZE, y, SCROLL_SIZE, h - SCROLL_SIZE);
79         aScrollbarHor = new Fl_Scrollbar (x, y + h - SCROLL_SIZE, w - SCROLL_SIZE, SCROLL_SIZE);
80     end();
81 
82     box (FL_DOWN_BOX);
83     color (FL_WHITE);
84     aScrollbarVer->callback (cb_ver, this);
85     aScrollbarHor->callback (cb_hor, this);
86     aScrollbarHor->type (FL_HORIZONTAL);
87     aScrollbarVer->slider_size (0.08);
88     aScrollbarHor->slider_size (0.08);
89 
90     set_data_size ();
91     Fl::focus (this);
92     remove (aDummy);
93 }
94 
95 
96 
97 /**
98 *  Delete table object
99 */
~Fl_Table()100 Fl_Table::~Fl_Table () {
101     quit_edit_mode (false);
102     delete aData;
103     delete []aWidth;
104     delete aDummy;
105     delete aFind;
106 }
107 
108 
109 
110 /**
111 *  Add new row at the end of table
112 */
add()113 void Fl_Table::add () {
114     aData->add ();
115     aCurrRow = aData->rows() - 1;
116     if (aData->rows() >= aMaxVisibleRows) {
117         aStartRow = aData->rows() - aMaxVisibleRows + 1;
118         if (aStartRow < 0) aStartRow = 0;
119     }
120     set_data_size();
121     if (aEventHandler) aEventHandler->handle (FL_DATA_CHANGE);
122     redraw();
123 }
124 
125 
126 
127 /**
128 *  Scrollbar has changed it position
129 */
cb_hor()130 void Fl_Table::cb_hor () {
131     aStartCol = aScrollbarHor->value ();
132     quit_edit_mode (true);
133     redraw ();
134 }
135 
136 
137 
138 /**
139 *  Scrollbar has changed it position
140 */
cb_ver()141 void Fl_Table::cb_ver () {
142     aStartRow = aScrollbarVer->value ();
143     quit_edit_mode (true);
144     redraw ();
145 }
146 
147 
148 
149 /**
150 *  Create a clip rectangle.
151 *  You must call fl_pop_clip after
152 *  @return int - CLIP_COL | CLIP_ROW if width or height has shrinked/changed
153 */
clip(int xpos,int ypos,int width,int height,int row)154 int Fl_Table::clip (int xpos, int ypos, int width, int height, int row) {
155     int retVal     = 0;
156     int win_width  = x() + w() - ((row == FL_LABEL_ROW) ? 0 : (SCROLL_SIZE));
157     int win_height = y() + h() - ((row == FL_LABEL_ROW) ? 0 : (SCROLL_SIZE));
158 
159     if ((xpos + width) >= win_width) {
160         width  = win_width - xpos;
161         retVal |= CLIP_COL;
162     }
163 
164     if ((ypos + height) > win_height) {
165         height = win_height - ypos;
166         retVal |= CLIP_ROW;
167     }
168 
169     fl_clip (xpos, ypos, width, height);
170     return retVal;
171 }
172 
173 
174 
175 /**
176 *  Set new data source
177 */
data(Fl_Table_Data * data)178 void Fl_Table::data (Fl_Table_Data* data) {
179     delete aData;
180     aData = data;
181 
182     set_data_size();
183 
184     aCurrRow  = aData->rows () - 1;
185     aCurrCol  = 0;
186     aStartCol = 0;
187     aStartRow = aCurrRow - (h() / aData->height(aCurrRow)) + 4;
188     if (aStartRow < 0) aStartRow = 0;
189     if (aStartCol < 0) aStartCol = 0;
190     ((Fl_Valuator*)aScrollbarVer)->value (aStartRow);
191     aScrollbarHor->slider_size (0.25);
192     aScrollbarVer->slider_size (0.25);
193 }
194 
195 
196 
197 /**
198 *  Draw lable, header and all cells.
199 */
draw()200 void Fl_Table::draw () {
201     int   xpos = x() + 1;
202     int   ypos = y() + 1;
203     int   foo  = 1;
204 
205     Fl_Group::draw ();
206 
207     if (!aData) return;
208 
209     if (aData->show_label()) {
210         draw_label (xpos + 1, ypos + foo);
211         ypos += aData->height(FL_LABEL_ROW);
212         foo = 0;
213     }
214 
215     aVisibleCols = 0;
216     if (aData->show_header()) {
217         draw_header (xpos + 1, ypos + foo);
218         ypos += aData->height (FL_HEADER_ROW);
219         foo = 0;
220     }
221 
222     aVisibleRows = aMaxVisibleRows = 0;
223     aClipped = 1;
224     aActiveX = aActiveY = aActiveW = aActiveH = aActiveR = aActiveC = -1;
225 
226     int clipped = 0;
227     int lastHeight = 20;
228     for (int f = aStartRow; f < aData->rows(); f++) {
229         clipped = draw_row (f, xpos + 1, ypos + foo);
230 
231         if (clipped == CLIP_COL || clipped == CLIP_NONE) {
232             aVisibleRows++;
233             aMaxVisibleRows++;
234             aClipped    = 0;
235             lastHeight  = aData->height (f);
236             ypos       += lastHeight;
237         }
238         else
239             break;
240     }
241     if (clipped != CLIP_ROW)
242         aMaxVisibleRows += (y() + h() - ypos) / lastHeight;
243 
244     if (aEditWidget) {
245         aEditWidget->redraw ();
246         aEditWidget->draw ();
247         Fl::focus (aEditWidget);
248     }
249     else if (aActiveH > -1) {
250         clip (aActiveX, aActiveY, aActiveW, aActiveH);
251         draw_cell (aActiveR, aActiveC, aActiveX, aActiveY, aActiveW, aActiveH);
252         fl_pop_clip();
253     }
254 }
255 
256 
257 
258 /**
259 * Draw cell
260 */
draw_cell(int row,int col,int xpos,int ypos,int width,int height)261 void Fl_Table::draw_cell (int row, int col, int xpos, int ypos, int width, int height) {
262     int arow = row;
263 
264     if (row == aActiveR && col == aActiveC) arow = FL_ACTIVE_ROW;
265 
266     if (aData->editor_type(row, col, false) == FL_BOOL_EDITOR) {
267         int addx  = 2;
268         int addy  = (height / 2) - (aData->textsize (row, col) / 2) - 2;
269         int size  = aData->textsize (row, col) + 4;
270 
271         if (aData->align (row, col) == FL_ALIGN_RIGHT)
272             addx = width - 4 - size;
273         else if (aData->align (row, col) == FL_ALIGN_CENTER)
274             addx = (width / 2) - (aData->textsize (row, col) / 2);
275 
276         draw_cell (xpos, ypos, width, height, aData->box_color (arow, col), aData->border_color (arow, col));
277         fl_draw_box (FL_DOWN_BOX, xpos + addx, ypos + addy, size, size, FL_WHITE);
278 
279         if (*aData->value (row, col) != '\0' && *aData->value (row, col) != '0') {
280             fl_color (FL_BLACK);
281             fl_line (xpos + addx + 3, ypos + addy + 3, xpos + addx + size - 4, ypos + addy + size - 4);
282             fl_line (xpos + addx + size - 4, ypos + addy + 3, xpos + addx + 3, ypos + addy + size - 4);
283         }
284     }
285     else if (aData->editor_type(row, col, false) == FL_SECRET_EDITOR) {
286         draw_cell (xpos, ypos, width, height, aData->box_color (arow, col), aData->border_color (arow, col));
287         fl_font (aData->textfont (arow, col), aData->textsize (arow, col));
288         fl_color (aData->textcolor (arow, col));
289         fl_draw ("******", xpos + 2, ypos, width - 4, height, aData->align (arow, col));
290     }
291     else {
292         draw_cell (xpos, ypos, width, height, aData->box_color (arow, col), aData->border_color (arow, col));
293         fl_font (aData->textfont (arow, col), aData->textsize (arow, col));
294         fl_color (aData->textcolor (arow, col));
295         fl_draw (aData->value (row, col), xpos + 2, ypos, width - 4, height, aData->align (arow, col));
296     }
297 }
298 
299 
300 
301 /**
302 * Draw cell background area
303 */
draw_cell(int x,int y,int w,int h,Fl_Color bg,Fl_Color fg)304 void Fl_Table::draw_cell (int x, int y, int w, int h, Fl_Color bg, Fl_Color fg) {
305     fl_color (bg);
306     fl_rectf (x, y, w, h);
307     fl_color (fg);
308     fl_rect (x, y, w, h);
309 }
310 
311 
312 
313 /**
314 *  Draw table title
315 */
draw_label(int xpos,int ypos)316 void Fl_Table::draw_label (int xpos, int ypos) {
317     int height = aData->height(FL_LABEL_ROW);
318     int width  = w() - 1;
319 
320     height--;
321     clip (xpos, ypos, width, height, FL_LABEL_ROW);
322     fl_draw_box (FL_UP_BOX, xpos, ypos, width - 1, height, aData->box_color (FL_LABEL_ROW, 0));
323     fl_font (aData->textfont (FL_LABEL_ROW, 0), aData->textsize (FL_LABEL_ROW, 0));
324     fl_color (aData->textcolor (FL_LABEL_ROW, 0));
325     fl_draw (aData->value(FL_LABEL_ROW, 0), xpos + 2, ypos, width - 4, height, FL_ALIGN_CENTER);
326     fl_pop_clip ();
327 }
328 
329 
330 
331 /**
332 *  Draw table header
333 */
draw_header(int xpos,int ypos)334 void Fl_Table::draw_header (int xpos, int ypos) {
335     int height = aData->height(FL_HEADER_ROW);
336     int width;
337     int clipped;
338 
339     for (int f = aStartCol; f < aData->cols(); f++) {
340         width   = aWidth[f];
341         clipped = clip (xpos, ypos, width, height, FL_HEADER_ROW);
342 
343         fl_draw_box (FL_UP_BOX, xpos, ypos, width, height, aData->box_color (FL_HEADER_ROW, 0));
344         fl_font (aData->textfont (FL_HEADER_ROW, f), aData->textsize (FL_HEADER_ROW, f));
345         fl_color (aData->textcolor (FL_HEADER_ROW, f));
346         fl_draw (aData->value (FL_HEADER_ROW, f), xpos + 2, ypos, width - 4, height, aData->align (FL_HEADER_ROW, f));
347         fl_pop_clip ();
348 
349         if (clipped == CLIP_COL || clipped == CLIP_BOTH)
350             return;
351         else
352             aVisibleCols++;
353 
354         xpos += aWidth[f];
355     }
356 }
357 
358 
359 
360 /**
361 *  Draw table row, return true if row is clipped
362 */
draw_row(int row,int xpos,int ypos)363 int Fl_Table::draw_row (int row, int xpos, int ypos) {
364     int height;
365     int width;
366     int clipped = 0;
367 
368     for (int f = aStartCol; f < aData->cols(); f++) {
369         height  = aData->height(f);
370         width   = aWidth[f];
371         clipped = clip (xpos, ypos, width, height);
372 
373         height++;
374         if (f < (aData->cols() - 1)) width++;
375         if (row == (aData->rows() - 1)) height--;
376 
377         if (aEditWidget && row == aCurrRow && f == aCurrCol)
378             aEditWidget->resize (xpos, ypos, width, height);
379         else if (row == aCurrRow && f == aCurrCol) {
380             //  Save size and pos for drawing active box last, after all rows gave been drawn
381             aActiveX = xpos; aActiveY = ypos; aActiveW = width; aActiveH = height;
382             aActiveC = f; aActiveR = row;
383         }
384         else
385             draw_cell (row, f, xpos, ypos, width, height);
386 
387         xpos += aWidth[f];
388         fl_pop_clip ();
389 
390         if (clipped == CLIP_COL) break;
391     }
392     return clipped;
393 }
394 
395 
396 
397 /**
398 *  Start editing current row
399 *  @param bool - true to force use of the custom editor (FL_DLG_CUSTOM_EDITOR) flag
400 */
edit(bool force_custom)401 void Fl_Table::edit (bool force_custom) {
402     if (aEditWidget) {
403         if (aData && aData->rows() > 0 && aCurrRow >= 0 && quit_edit_mode (true) == true)
404             start_edit_mode (force_custom);
405     }
406     else
407         start_edit_mode (force_custom);
408 }
409 
410 
411 
412 /**
413 *  Erase current row
414 */
erase()415 void Fl_Table::erase () {
416     aData->erase (aCurrRow);
417     set_data_size();
418     set_cursor(aCurrRow, aCurrCol);
419     if (aEventHandler) aEventHandler->handle (FL_DATA_CHANGE);
420     redraw();
421 }
422 
423 
424 
425 /**
426 *  Mouse is dragged, change column size if right position
427 */
ev_drag()428 void Fl_Table::ev_drag () {
429     int xpos = Fl::event_x();
430 
431     if (aDragState == DRAG_HOR) {
432         int currx = x();
433         for (int f = aStartCol; f < aDragCol; f++)
434             currx  += aWidth[f];
435         if (xpos - currx > 10)
436             aWidth[aDragCol] = xpos - currx;
437         redraw ();
438     }
439 }
440 
441 
442 
443 /**
444 *  Keys are pressed down
445 */
ev_keyboard_down()446 bool Fl_Table::ev_keyboard_down () {
447     switch (Fl::event_key()) {
448         case FL_Down:
449             move_cursor (1, 0);
450             break;
451 
452         case FL_Left:
453             move_cursor (0, -1);
454             break;
455 
456         case FL_Page_Down:
457             if (Fl::event_state() == FL_CTRL)
458                 move_cursor (1999999999, 0);
459             else
460                 move_cursor (SCROLL_JUMP, 0);
461             break;
462 
463         case FL_Page_Up:
464             if (Fl::event_state() == FL_CTRL)
465                 move_cursor (-1999999999, 0);
466             else
467                 move_cursor (-SCROLL_JUMP, 0);
468             break;
469 
470         case FL_Right:
471             move_cursor (0, 1);
472             break;
473 
474         case FL_Up:
475             move_cursor (-1, 0);
476             break;
477 
478         case FL_Escape:
479             if (aEditWidget) quit_edit_mode (false);
480             break;
481 
482         case FL_Enter:
483             if (aEditWidget == 0)
484                 start_edit_mode ();
485             else
486                 quit_edit_mode (true);
487             break;
488 
489         default:
490             if (Fl::event_state() == FL_ALT)
491                 return false;
492             else if (Fl::event_state() == FL_CTRL) {
493                 //printf ("2 - %d\n", Fl::event_key());
494 
495                 switch (Fl::event_key()) {
496                     case 'a':
497                     case 'n':
498                         add ();
499                         break;
500 
501                     case 'c':
502                         Fl::copy (aData->value (aCurrRow, aCurrCol), strlen (aData->value (aCurrRow, aCurrCol)), 1);
503                         break;
504 
505                     case 'd':
506                         erase ();
507                         break;
508 
509                     case 'f':
510                         find (false);
511                         break;
512 
513                     case 'g':
514                         find (true);
515                         break;
516 
517                     case 'i':
518                         insert ();
519                         break;
520 
521                     case 's':
522                         if (dirty())
523                             save ();
524                         break;
525 
526                     case 'x':
527                         Fl::copy (aData->value (aCurrRow, aCurrCol), strlen (aData->value (aCurrRow, aCurrCol)), 1);
528                         aData->value ("", aCurrRow, aCurrCol);
529                         break;
530 
531                     case 'v':
532                         aDummy->value ("");
533                         Fl::paste (*aDummy, 1);
534                         if (aData->value (aDummy->value(), aCurrRow, aCurrCol) == false && aEventHandler) aEventHandler->handle (FL_DATA_ERROR);
535                         break;
536 
537                     case FL_Escape:
538                         break;
539                 }
540                 Fl::focus (this);
541                 redraw ();
542             }
543             break;
544     }
545 
546     if (aCurrRow < 0) aCurrRow = 0;
547     if (aCurrCol < 0) aCurrCol = 0;
548     if (aCurrRow < aStartRow) aStartRow = aCurrRow;
549     if (aCurrCol < aStartCol) aStartCol = aCurrCol;
550     return true;
551 }
552 
553 
554 
555 /**
556 *  Mouse is moved, check if user are above header. If so change mouse cursor if between cells.
557 */
ev_move()558 void Fl_Table::ev_move () {
559     if (!aData) return;
560     int xpos   = Fl::event_x();
561     int ypos   = Fl::event_y();
562     int currx  = x();
563     int curry  = y();
564 
565     if (aData->show_label())
566         curry += (aData->textsize (FL_LABEL_ROW, 0) + ( aData->textsize (FL_LABEL_ROW, 0) / 2));
567     if (ypos > curry) {
568         if (aData->show_header())
569             curry += aData->height (FL_HEADER_ROW);
570 
571         if (ypos < curry) {
572             //  Cursor is inside header
573             for (int f = aStartCol; f < aData->cols(); f++) {
574                 currx  += aWidth[f];
575                 if (xpos > (currx - 3) && xpos < (currx + 4)) {
576                     aDragState = DRAG_HOR;
577                     aDragCol   = f;
578                     fl_cursor (FL_CURSOR_WE);
579                     return;
580                 }
581             }
582         }
583     }
584 
585     //  Set cursor to standar one only if it was set to another cursor before
586     if (aDragState != DRAG_NONE) {
587         fl_cursor (FL_CURSOR_DEFAULT);
588         aDragState = DRAG_NONE;
589     }
590 }
591 
592 
593 
594 /**
595 *  Mouse is pushed.
596 *  Select new current cell
597 */
ev_push()598 void Fl_Table::ev_push () {
599     int xpos   = Fl::event_x();
600     int ypos   = Fl::event_y();
601     int row;
602     int col;
603     int c, d;
604 
605     get_cell_pos (row, col, xpos, ypos, c, d);
606 
607     if (aEditWidget) {
608         if (row != aCurrRow || col != aCurrCol) {
609             quit_edit_mode (true);
610             Fl::focus (this);
611         }
612         else {
613             aEditWidget->handle (FL_PUSH);
614             aEditWidget->draw();
615             Fl::focus (aEditWidget);
616             return;
617         }
618     }
619     else
620         Fl::focus (this);
621 
622     if (row <=  FL_NO_ROW || col < 0)
623         return;
624     else if (row ==  FL_HEADER_ROW && col >= 0) {
625         if (Fl::event_button() == FL_LEFT_MOUSE)
626             aData->sort (col, true);
627         else if (Fl::event_button() == FL_RIGHT_MOUSE)
628             aData->sort (col, false);
629         redraw ();
630     }
631     else if (row >= 0 && col >= 0 && (row != aCurrRow || col != aCurrCol)) {
632         if (quit_edit_mode (true)) {
633             aCurrRow = row;
634             aCurrCol = col;
635             redraw ();
636         }
637     }
638     else {
639         if (Fl::event_clicks() == 1) {
640             if (aEditWidget == 0)
641                 start_edit_mode ();
642             else
643                 quit_edit_mode (true);
644         }
645     }
646 }
647 
648 
649 
650 /**
651 *  Search for text in cell.
652 *  Display a search dialog or continue to serach for next word
653 */
find(bool nextword)654 void Fl_Table::find (bool nextword) {
655     if (!aData) return;
656 
657     const char* searchWord  = 0;
658     const char* replaceWord = 0;
659     bool        found       = false;
660     char        tmp[200];
661     int         ret;
662     int         case_sense;
663     int         begin = 0;
664 
665     if (nextword == false || *aSearchWord == '\0') {
666         ret         = aFind->show_modal (aDlgParent);
667         searchWord  = aFind->text ();
668         replaceWord = aFind->text ();
669         case_sense  = aFind->case_sensitive ();
670         begin       = aFind->beginning ();
671 
672         if (ret == 0) return;
673 
674         aCase = (case_sense == 1) ? true : false;
675 
676         if (searchWord && *searchWord) {
677             strncpy (aSearchWord, searchWord, 199);
678 
679             if (aCase == false) {
680                 #ifdef WIN32
681                         _strlwr (aSearchWord);
682                 #else
683                     char* f = aSearchWord;
684                     while (*f) {
685                         *f = tolower (*f);
686                         f++;
687                     }
688                 #endif
689             }
690         }
691         else
692             return;
693     }
694 
695 
696     for (int row = (begin == 0 ? aCurrRow : 0); row < aData->rows(); row++) {
697         for (int col = (begin == 0 ? (row == aCurrRow ? aCurrCol + 1: 0) : 0); col < aData->cols(); col++) {
698             if (aCase == false) {
699                 strncpy (tmp, aData->value (row, col), 199);
700                 #ifdef WIN32
701                     _strlwr (tmp);
702                 #else
703                     char* f = tmp;
704                     while (*f) {
705                         *f = tolower (*f);
706                         f++;
707                     }
708                 #endif
709 
710                 if (strstr (tmp, aSearchWord))
711                     found = true;
712             }
713             else if (strstr (aData->value (row, col), aSearchWord))
714                 found = true;
715 
716             if (found) {
717                 set_cursor (row, col);
718                 return;
719             }
720         }
721     }
722     fl_beep (FL_BEEP_ERROR);
723 }
724 
725 
726 
727 /**
728 *  Get cell size and position
729 */
get_cell_pos(int & row,int & col,int & xpos,int & ypos,int & width,int & height)730 void Fl_Table::get_cell_pos (int& row, int& col, int& xpos, int& ypos, int& width, int& height) {
731     row = col = FL_NO_ROW;
732     if (!aData) return;
733 
734     int currx  = x();
735     int curry  = y();
736 
737     if (aData->show_label()) curry += (aData->height (FL_LABEL_ROW));
738     if (ypos < curry) {
739         row = FL_LABEL_ROW;
740         return;
741     }
742     if (aData->show_header()) curry  += aData->height (FL_HEADER_ROW);
743     if (ypos < curry) row = FL_HEADER_ROW;
744 
745     for (int c = aStartCol; c < aData->cols(); c++) {
746         if (xpos > currx && xpos <= (currx + aWidth[c])) {
747             col   = c;
748             xpos  = currx;
749             width = aWidth[c];
750             break;
751         }
752         else if (xpos > (x() + w() - aScrollbarVer->w()))
753             break;
754 
755         currx  += aWidth[c];
756     }
757 
758     if (row == FL_HEADER_ROW) return;
759 
760     for (int r = aStartRow; r < aData->rows(); r++) {
761         if (ypos >= curry && ypos < (curry + aData->height(r))) {
762             row    = r;
763             ypos   = curry;
764             height = aData->height(r);
765             break;
766         }
767         else if (ypos > (y() + h() - aScrollbarHor->h()))
768             break;
769 
770         curry  += aData->height(r);
771     }
772 }
773 
774 
775 
776 /**
777 *  All events goes here
778 *  @param int - Event
779 */
handle(int event)780 int Fl_Table::handle (int event) {
781     if (!aData) return Fl_Group::handle (event);
782 
783     switch (event) {
784         case FL_DRAG:
785             ev_drag ();
786             return 1;
787 
788         case FL_KEYDOWN:
789             if (ev_keyboard_down ())
790                 return 1;
791             else
792                 break;
793 
794         case FL_KEYUP:
795             if (aEditWidget) aEditWidget->redraw();
796             return 0;
797 
798         case FL_LEAVE:
799             if (aDragState != DRAG_NONE) {
800                 fl_cursor (FL_CURSOR_DEFAULT);
801                 aDragState = DRAG_NONE;
802             }
803             return 0;
804 
805         case FL_MOVE:
806             ev_move ();
807             return 0;
808 
809         case FL_PUSH:
810             ev_push ();
811             Fl_Group::handle (event);
812             return 1;
813 
814         default:
815             break;
816     }
817 
818     return Fl_Group::handle (event);
819 }
820 
821 
822 
823 /**
824 *  Insert row at current position
825 */
insert()826 void Fl_Table::insert () {
827     aData->insert (aCurrRow);
828     set_data_size();
829     set_cursor(aCurrRow, aCurrCol);
830     if (aEventHandler) aEventHandler->handle (FL_DATA_CHANGE);
831     redraw();
832 }
833 
834 
835 
836 /**
837 * Move cursor from current position
838 * @param int - Number of rows to move cursor
839 * @param int - Number of columns to move cursor
840 */
move_cursor(int rows,int cols)841 void Fl_Table::move_cursor (int rows, int cols) {
842     if (aEditWidget) return;
843     if (!aData || aData->rows() == 0 || aData->cols() == 0) return;
844 
845     aCurrRow += rows;
846     if (aVisibleRows > 1) {
847         if (aCurrRow < aStartRow) aStartRow = aCurrRow;
848         if (aCurrRow > (aStartRow + aVisibleRows - 1)) aStartRow = aCurrRow - aVisibleRows + 1;
849     }
850     else
851         aStartRow = aCurrRow;
852 
853     if (aCurrRow >= aData->rows()) aCurrRow = aData->rows() - 1;
854     if (aCurrRow < 0) aCurrRow = 0;
855     if (aStartRow >= aData->rows() || aStartRow < 0) aStartRow = aCurrRow;
856 
857     aCurrCol += cols;
858     if (aVisibleCols > 1) {
859         if (aCurrCol < aStartCol) aStartCol = aCurrCol;
860         if (aCurrCol > (aStartCol + aVisibleCols - 1)) aStartCol = aCurrCol - aVisibleCols + 1;
861     }
862     else
863         aStartCol = aCurrCol;
864 
865     if (aCurrCol >= aData->cols()) aCurrCol = aData->cols() - 1;
866     if (aCurrCol < 0) aCurrCol = 0;
867     if (aStartCol >= aData->cols() || aStartCol < 0) aStartCol = aCurrCol;
868 
869     ((Fl_Valuator*)aScrollbarVer)->value ((double)aStartRow);
870     ((Fl_Valuator*)aScrollbarHor)->value ((double)aStartCol);
871     redraw ();
872 }
873 
874 
875 
quit_edit_mode(bool save)876 bool Fl_Table::quit_edit_mode (bool save) {
877     if (aEditWidget) {
878         const char** list;
879         int          row;
880         int          a, b;
881         bool         stop = true;
882         char         label[100];
883 
884         if (save) {
885             switch (aData->editor_type(aCurrRow, aCurrCol, false)) {
886                 case FL_BOOL_EDITOR:
887                     if (((Fl_Check_Button*)aEditWidget)->value() == 0)
888                         stop = aData->value ("\0", aCurrRow, aCurrCol);
889                     else
890                         stop = aData->value ("\1", aCurrRow, aCurrCol);
891                     break;
892 
893                 case FL_LIST_EDITOR:
894                     row  = ((Fl_Choice*)aEditWidget)->value ();
895                     list = aData->choice (aCurrRow, aCurrCol, a, b, label);
896                     stop = aData->value (list[row], aCurrRow, aCurrCol);
897                     break;
898 
899                 default:
900                     stop = aData->value (((Fl_Input*)aEditWidget)->value(), aCurrRow, aCurrCol);
901                     break;
902             }
903         }
904 
905         if (stop == true) {
906             remove (aEditWidget);
907             delete aEditWidget;
908             aEditWidget = 0;
909             if (aEventHandler) aEventHandler->handle (FL_DATA_CHANGE);
910         }
911         else {
912             fl_beep (FL_BEEP_ERROR);
913             if (aEventHandler) aEventHandler->handle (FL_DATA_ERROR);
914             aEditWidget->redraw ();
915             Fl::focus (aEditWidget);
916             return false;
917         }
918     }
919     Fl::focus (this);
920     redraw ();
921     return true;
922 }
923 
924 
925 
926 /**
927 *  Resize table and scrollbars.
928 *  @param int  - X pos
929 *  @param int  - Y pos
930 *  @param int  - Width
931 *  @param int  - Height
932 */
resize(int x,int y,int w,int h)933 void Fl_Table::resize (int x, int y, int w, int h) {
934     Fl_Group::resize (x, y, w, h);
935 
936     int hfix = (aData && aData->show_label()) ? aData->height(FL_LABEL_ROW) : 0;
937     int yfix = (aData && aData->show_label()) ? 0 : 2;
938 
939     aScrollbarVer->resize (x + w - SCROLL_SIZE, y + hfix + yfix, SCROLL_SIZE, h - hfix - yfix);
940     aScrollbarHor->resize (x, y + h - SCROLL_SIZE, w - SCROLL_SIZE, SCROLL_SIZE);
941 }
942 
943 
944 
945 /**
946 *  Let data save itself
947 */
save()948 bool Fl_Table::save () {
949     int row = aData->save ();
950 
951     if (row < 0) {
952         if (aEventHandler) aEventHandler->handle (FL_DATA_SAVE);
953         set_data_size();
954         redraw ();
955         return true;
956     }
957     else {
958         if (aEventHandler) aEventHandler->handle (FL_DATA_SAVE_ERROR);
959         set_cursor (row, 0);
960         redraw ();
961         return false;
962     }
963 }
964 
965 /**
966  * Callback for file chooser
967  */
tab_fc_callback(Fl_File_Chooser * fc,void * data)968 void tab_fc_callback(Fl_File_Chooser *fc, void *data)
969 {
970 	Fl_Table *t = static_cast<Fl_Table *>(data);
971 	t->aData->value (fc->value(), t->aCurrRow, t->aCurrCol);
972 }
973 
974 /**
975 *  Start to edit a cell
976 */
start_edit_mode(bool force_custom)977 void Fl_Table::start_edit_mode (bool force_custom) {
978     if (aData->editor_type (aCurrRow, aCurrCol, false) == FL_DLG_COLOR_EDITOR) {
979         int col = atoi (aData->value(aCurrRow, aCurrCol));
980         if (col < 0 || col > 255) col = 0;
981         int newcol = fl_show_colormap ((Fl_Color) col);
982         char buff [31];
983         SNPRINTF (buff, 30, "%d", newcol);
984         aData->value (buff, aCurrRow, aCurrCol);
985 
986     }
987     else if (aData->editor_type (aCurrRow, aCurrCol, false) == FL_DLG_DIR_EDITOR) {
988         char* dir = fl_dir_chooser("Select Directory", aData->value(aCurrRow, aCurrCol));
989         aData->value (dir, aCurrRow, aCurrCol);
990 
991     }
992     else if (aData->editor_type (aCurrRow, aCurrCol, false) == FL_DLG_FILE_EDITOR) {
993 
994         Fl_File_Chooser *fc = new Fl_File_Chooser("Select File", "*", Fl_File_Chooser::SINGLE, aData->value(aCurrRow, aCurrCol));
995         fc->callback(tab_fc_callback, this);
996         delete fc;
997     }
998     else {
999         aEditWidget = aData->editor (aCurrRow, aCurrCol, force_custom);
1000         if (!aEditWidget) return;
1001 
1002         switch (aData->editor_type(aCurrRow, aCurrCol, force_custom)) {
1003             case FL_DLG_LIST_EDITOR: {
1004                 if (aEditWidget) {
1005                     //  This is a dialog popup mode and when dialog close the editing also stops
1006 
1007                     Fl_Select* dlg = (Fl_Select*) aEditWidget;
1008                     bool ok = false;
1009 
1010                     Fl::event_clicks (0);
1011                     if (dlg && dlg->show_modal (aDlgParent) == 1 && dlg->row () > 0) {
1012                         int a, b;
1013                         char label[100];
1014                         const char** list = aData->choice (aCurrRow, aCurrCol, a, b, label);
1015                         ok = aData->value (list[dlg->row () - 1], aCurrRow, aCurrCol);
1016                     }
1017                     delete dlg;
1018                     aEditWidget = 0;
1019                     if (aEventHandler && ok)
1020                         aEventHandler->handle (FL_DATA_CHANGE);
1021                 }
1022                 Fl::focus (this);
1023                 break;
1024             }
1025 
1026             case FL_DLG_CUSTOM_EDITOR: {
1027                 Fl_Dialog*  dlg = (Fl_Dialog*) aEditWidget;
1028                 bool        ok  = false;
1029 
1030                 Fl::event_clicks (0);
1031                 if (dlg) {
1032                     if (dlg->show_modal (aDlgParent) == 1) {
1033                         ok = true;
1034                         //  Dialog has changed data, make sure current data object nows that
1035                         aData->dirty (true);
1036                     }
1037                 }
1038                 delete dlg;
1039                 aEditWidget = 0;
1040                 if (aEventHandler && ok)
1041                     aEventHandler->handle (FL_DATA_CHANGE);
1042                 Fl::focus (this);
1043                 break;
1044             }
1045 
1046             default:
1047                 Fl_Group::add (aEditWidget);
1048                 Fl::focus (aEditWidget);
1049                 aEditWidget->draw ();
1050                 break;
1051         }
1052     }
1053     redraw ();
1054 }
1055 
1056 
1057 
1058 /**
1059 *  Set size of columns and scrollbar values
1060 */
set_data_size()1061 void Fl_Table::set_data_size () {
1062     if (!aData) return;
1063 
1064     aScrollbarVer->range (0, aData->rows());
1065     aScrollbarHor->range (0, aData->cols());
1066     delete []aWidth;
1067     aWidth = new int[aData->cols() + 1];
1068     for (int f = 0; f < aData->cols(); f++)
1069         aWidth[f] = aData->width (f);
1070 
1071     redraw();
1072 }
1073