1 /*
2  Copyright (c) 2004 Markus Niemistö
3 
4  Permission is hereby granted, free of charge, to any person
5  obtaining a copy of this software and associated documentation
6  files (the "Software"), to deal in the Software without
7  restriction, including without limitation the rights to use,
8  copy, modify, merge, publish, distribute, sublicense, and/or
9  sell copies of the Software, and to permit persons to whom
10  the Software is furnished to do so, subject to the following
11  conditions:
12 
13  The above copyright notice and this permission notice shall
14  be included in all copies or substantial portions of the Software.
15 
16  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23  DEALINGS IN THE SOFTWARE.
24 */
25 
26 #include <config.h>
27 
28 #include <FL/Fl.H>
29 #include <FL/fl_draw.H>
30 
31 #include <stdarg.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <string>
35 #include "table.h"
36 
37 //#define DAMAGE_HEADER  FL_DAMAGE_USER1
38 //#define DAMAGE_ROWS  FL_DAMAGE_USER2
39 
40 #define DAMAGE_HEADER  FL_DAMAGE_ALL
41 #define DAMAGE_ROWS  FL_DAMAGE_ALL
42 
43 using namespace std;
44 
45 /*
46  * nullptr is not available in gcc < 4.6
47  * Redefine to NULL for CentOS 6 and OSX Darwin
48  */
49 #ifndef __FreeBSD__
50  #ifndef nullptr
51  #define nullptr NULL
52  #endif
53 #endif
54 
55 
56 /*
57  * ======================================
58  *  void Table.drawHeader(int x, int y);
59  * ======================================
60  *
61  * Draws header buttons starting at (x, y).
62  */
drawHeader(int x,int y)63 void Table::drawHeader(int x, int y) {
64   int w;
65   struct ColumnInfo col;
66 
67   fl_font(tbl_font, tbl_fontsize);
68 
69   /*
70    * Draw all header cells that aren't clipped.
71    */
72   for (int i = leftCol; i <= rightCol; i++) {
73     col = header[i];
74 
75     if (col.hidden)
76       continue;
77 
78     // Draw box
79     if (pushed != i)
80       fl_draw_box(FL_THIN_UP_BOX, x, y, w = col.width, headerHeight, FL_GRAY);
81     else
82       fl_draw_box(FL_THIN_DOWN_BOX, x, y, w = col.width, headerHeight, FL_GRAY);
83 
84     fl_color(FL_FOREGROUND_COLOR);
85 
86     // Draw labels
87     if (col.title != NULL)
88       fl_draw(col.title, x + 2, y - 1, w - 2, headerHeight, col.hdr_align);
89 
90     x += w;
91 
92     // Draw "the sort arrow", if any.
93     if (sortColumn == i) {
94       int mod = headerHeight - 10;
95 
96       if (ascent)
97         fl_polygon(x - mod - 6, y + 5, x - mod / 2 - 6,
98                    y + mod + 5, x - 6, y + 5);
99       else
100         fl_polygon(x - mod - 6, y + mod + 5,
101                    x - mod / 2 - 6, y + 5, x - 6, y + mod + 5);
102     }
103   }
104 }
105 
106 
107 /*
108  * =============================================================
109  *  void Table.drawRow(int row, char *rowData[], int x, int y);
110  * =============================================================
111  *
112  * Draws all items in the row. Starts drawing from (x, y).
113  */
drawRow(int row,char * rowData[],int x,int y)114 void Table::drawRow(int row, char *rowData[], int x, int y) {
115   int w;
116   ColumnInfo col;
117 
118   fl_font(tbl_font, tbl_fontsize);
119 
120   // Draw background box.
121   if (row != selected) {
122     Fl_Color bg;
123     if (!withGrid && row % 2 == 0) // different bg for consecutive rows
124       bg = color();
125     else {
126       bg = fl_color_average(color(), FL_BLACK, .9);
127       if (fl_contrast(bg, FL_BLACK) == FL_WHITE) // widget has very dark text bg
128         bg = fl_color_average(color(), FL_WHITE, .9);
129     }
130 
131     fl_rectf(iX, y, tableWidth - hScroll->value(), rowHeight, bg);
132 
133     fl_color(FL_FOREGROUND_COLOR);
134   }
135   else {
136     if (Fl::focus() == this) {
137       fl_rectf(iX, y, tableWidth - hScroll->value(), rowHeight, selection_color());
138       fl_color(FL_FOREGROUND_COLOR);
139 
140       // Draw focus
141       fl_line_style(FL_DOT);
142       fl_rect(iX, y, tableWidth - hScroll->value(), rowHeight);
143       fl_line_style(FL_SOLID);
144     }
145     else
146       fl_rectf(iX, y, tableWidth - hScroll->value(), rowHeight, selection_color());
147 
148     fl_color(fl_contrast(FL_FOREGROUND_COLOR, selection_color()));
149   }
150 
151 
152   // Get color from highlighter.
153   Fl_Color color;
154   if ((highlighter != NULL) && (highlighter(row, rowData, &color)))
155     fl_color(color);
156 
157   const char *str;
158 
159   // Draw the data.
160   for (int i = leftCol; i <= rightCol; i++) {
161     w = (col = header[i]).width;
162     if (col.hidden)
163       continue;
164 
165     if (withGrid == true) {
166       fl_color(FL_FOREGROUND_COLOR);
167       fl_line_style(FL_SOLID);
168       fl_rect(x,y,w,rowHeight);
169 
170       fl_color(FL_FOREGROUND_COLOR);
171     }
172 
173     if ((str = rowData[i]) != NULL)
174       fl_draw(str, x + 1, y - 1, w - 2, rowHeight + 1, col.align);
175 
176     x += w;
177   }
178 }
179 
180 
181 /*
182  * =======================================================
183  *  Table.Table(int x, int y, int w, int h, char *label);
184  * =======================================================
185  *
186  * This is standard FLTK constructor. See FLTK documentation for
187  * more information.
188  */
Table(int x,int y,int w,int h,char * label)189 Table::Table(int x, int y, int w, int h, char *label) :
190        Fl_Group(x, y, w, h, label) {
191 
192   // Setup variables.
193   align((Fl_Align)(FL_ALIGN_LEFT | FL_ALIGN_CLIP));
194 
195   box(FL_THIN_DOWN_FRAME);
196   color(FL_BACKGROUND2_COLOR, FL_SELECTION_COLOR);
197 
198   tbl_fontsize = 14;
199   headerHeight = tbl_fontsize + 4;
200   rowHeight = tbl_fontsize + 4;
201   scrollbarSize = 16;
202 
203   // Create scrollbars.
204   vScroll = new Fl_Scrollbar(x + w - scrollbarSize, y, scrollbarSize, h + scrollbarSize);
205   vScroll->type(FL_VERTICAL);
206   vScroll->linesize(3 * rowHeight);
207   vScroll->callback(scrollCallback, (void*)this);
208   vScroll->hide();
209 
210   hScroll = new Fl_Scrollbar(x, y + h - scrollbarSize, w, scrollbarSize);
211   hScroll->type(FL_HORIZONTAL);
212   hScroll->callback(scrollCallback, (void*)this);
213   hScroll->hide();
214 
215   Fl_Group::end();
216 
217   // Setup the rest of the variables to reasonable defaults.
218   nCols = nRows = 0;
219   cPos = 0;
220 
221   dragX = 0;
222   resizing = -1;
223   pushed = -1;
224 
225   curRow = NULL;
226   highlighter = NULL;
227 
228   sortColumn = -1;
229   selected = -1;
230   canSort = true;
231   canResize = true;
232   ascent = false;
233   noMoreColumns = false;
234   dimensionsChanged = false;
235   toBeSorted = false;
236   headerEnabled = true;
237   withGrid = false;
238 
239   popupMenu = NULL;
240   menuAlloc = false;
241 
242   Vscroll = var;
243   Hscroll = var;
244 
245   tbl_font = FL_HELVETICA;
246 }
247 
248 
249 /*
250  * =================
251  *  Table.~Table();
252  * =================
253  *
254  * Destructor.
255  */
~Table()256 Table::~Table() {
257   delete vScroll;
258   delete hScroll;
259 
260   menuClear();
261   clear();
262 }
263 
264 
265 /*
266  * ====================================
267  *  bool Table.headerOn();
268  *  void Table.headerOn(bool enabled);
269  * ====================================
270  *
271  * These methods get or set the value of variable controlling
272  * whether header buttons are displayed.
273  */
headerOn() const274 bool Table::headerOn() const {
275   return headerEnabled;
276 }
277 
headerOn(bool enabled)278 void Table::headerOn(bool enabled) {
279   headerEnabled = enabled;
280   dimensionsChanged = true;
281   redraw();
282 }
283 
284 
285 /*
286  * ====================================
287  *  bool Table.gridEnabled();
288  *  void Table.gridEnabled(bool enabled);
289  * ====================================
290  *
291  * These methods get or set the value of variable controlling
292  * whether the table grid is displayed.
293  */
gridEnabled() const294 bool Table::gridEnabled() const {
295   return withGrid;
296 }
297 
gridEnabled(bool enabled)298 void Table::gridEnabled(bool enabled) {
299   withGrid = enabled;
300   dimensionsChanged = true;
301   redraw();
302 }
303 
304 
305 /*
306  * =====================================
307  *  bool Table.allowResize();
308  *  void Table.allowResize(bool allow);
309  * =====================================
310  *
311  * These methods get or set the value of variable controlling
312  * whether user may resize columns by dragging the column border.
313  */
allowResize() const314 bool Table::allowResize() const {
315   return canResize;
316 }
317 
allowResize(bool allow)318 void Table::allowResize(bool allow) {
319   canResize = allow;
320 }
321 
322 
323 /*
324  * ===================================
325  *  bool Table.allowSort();
326  *  void Table.allowSort(bool allow);
327  * ===================================
328  *
329  * These methods get or set the value of variable controlling
330  * whether user can determine how data on table is sorted by
331  * clicking on the header buttons.
332  */
allowSort() const333 bool Table::allowSort() const {
334   return canSort;
335 }
336 
allowSort(bool allow)337 void Table::allowSort(bool allow) {
338   canSort = allow;
339 }
340 
341 
342 /*
343  * ==================================
344  *  int Table.headerSize();
345  *  void Table.headerSize(int size);
346  * ==================================
347  *
348  * These methods get or set the value of variable controlling
349  * the height of header buttons.
350  */
headerSize() const351 int Table::headerSize() const {
352   return headerHeight;
353 }
354 
headerSize(int height)355 void Table::headerSize(int height) {
356   headerHeight = height + 4;
357   dimensionsChanged = true;
358   redraw();
359 }
360 
361 
362 /*
363  * ===============================
364  *  int Table.rowSize();
365  *  void Table.rowSize(int size);
366  * ===============================
367  *
368  * These methods get or set the value of variable controlling
369  * the height of rows.
370  */
rowSize() const371 int Table::rowSize() const {
372   return rowHeight;
373 }
374 
rowSize(int height)375 void Table::rowSize(int height) {
376   height += 4;
377   rowHeight = height;
378   vScroll->linesize(3 * height);
379   dimensionsChanged = true;
380   redraw();
381 }
382 
383 
384 /*
385  * ===================================
386  *  int Table.scrollbSize();
387  *  void Table.scrollbSize(int size);
388  * ===================================
389  *
390  * These methods get or set the value of variable controlling
391  * the size (width) of the scrollbars.
392  */
scrollbSize() const393 int Table::scrollbSize() const {
394   return scrollbarSize;
395 }
396 
scrollbSize(int size)397 void Table::scrollbSize(int size) {
398   scrollbarSize = size;
399   dimensionsChanged = true;
400   redraw();
401 }
402 
403 
404 /*
405  * =====================================================
406  *  Fl_Align Table.columnAlign(int column);
407  *  void Table.columnAlign(int column, Fl_Align align);
408  * =====================================================
409  *
410  * These methods get or set the value of variable controlling
411  * the alignment of the specified column.
412  */
columnAlign(int column) const413 Fl_Align Table::columnAlign(int column) const {
414   if ((column < 0) && (column >= nCols))
415     return (Fl_Align)(FL_ALIGN_LEFT | FL_ALIGN_CLIP);
416     /* NOT REACHED */
417 
418   return header[column].align;
419 }
420 
columnAlign(int column,Fl_Align align)421 void Table::columnAlign(int column, Fl_Align align) {
422   if ((column < 0) || (column >= nCols))
423     return;
424     /* NOT REACHED */
425 
426   header[column].align = (Fl_Align)(align | FL_ALIGN_CLIP);
427   redraw();
428 }
429 
headerAlign(int column) const430 Fl_Align Table::headerAlign(int column) const {
431   if ((column < 0) && (column >= nCols))
432     return (Fl_Align)(FL_ALIGN_LEFT | FL_ALIGN_CLIP);
433     /* NOT REACHED */
434 
435   return header[column].hdr_align;
436 }
437 
headerAlign(int column,Fl_Align align)438 void Table::headerAlign(int column, Fl_Align align) {
439   if ((column < 0) && (column >= nCols))
440     return;
441     /* NOT REACHED */
442 
443   header[column].hdr_align = (Fl_Align)(align | FL_ALIGN_CLIP);
444   redraw();
445 }
446 
447 
448 /*
449  * =====================================================
450  *  int Table.columnWidth(int column);
451  *  void Table.columnWidth(int column, int width);
452  * =====================================================
453  *
454  * These methods get or set the value of variable controlling
455  * the width of the specified column.
456  */
columnWidth(int column) const457 int Table::columnWidth(int column) const {
458   if ((column < 0) && (column >= nCols))
459     return 0;
460     /* NOT REACHED */
461 
462   return header[column].width;
463 }
464 
columnWidth(int column,int width)465 void Table::columnWidth(int column, int width) {
466   if ((column < 0) && (column >= nCols))
467     return;
468     /* NOT REACHED */
469 
470   header[column].width = width;
471   dimensionsChanged = true;
472   redraw();
473 }
474 
475 
476 /*
477  * ========================================================
478  *  const char *Table.columnTitle(int column);
479  *  void Table.columnTitle(int column, const char *title);
480  * ========================================================
481  *
482  * These methods get or set the value of variable controlling
483  * the width of the specified column.
484  */
columnTitle(int column)485 const char *Table::columnTitle(int column) {
486   if ((column < 0) && (column >= nCols))
487     return NULL;
488     /* NOT REACHED */
489 
490   return header[column].title;
491 }
492 
columnTitle(int column,const char * title)493 void Table::columnTitle(int column, const char *title) {
494   if ((column < 0) && (column >= nCols))
495     return;
496     /* NOT REACHED */
497 
498   free((void*)header[column].title);
499   header[column].title = strdup(title);
500 
501   damage(DAMAGE_HEADER);
502 }
503 
504 
505 /*
506  * ===================================================
507  *  bool Table.columnHidden(int column);
508  *  void Table.columnHidden(int column, bool hidden);
509  * ===================================================
510  *
511  * These methods get or set the value of variable controlling
512  * whether column is visible or not.
513  */
columnHidden(int column)514 bool Table::columnHidden(int column) {
515   if ((column < 0) && (column >= nCols))
516     return false;
517     /* NOT REACHED */
518 
519   return header[column].hidden;
520 }
521 
columnHidden(int column,bool hidden)522 void Table::columnHidden(int column, bool hidden) {
523   if ((column < 0) && (column >= nCols))
524     return;
525     /* NOT REACHED */
526 
527   header[column].hidden = hidden;
528   dimensionsChanged = true;
529   damage(DAMAGE_HEADER | DAMAGE_ROWS);
530 }
531 
532 
533 /*
534  * ===================
535  *  int Table.rows();
536  * ===================
537  *
538  * Returns the number of rows in table.
539  */
rows()540 int Table::rows() {
541   return nRows;
542 }
543 
544 
545 /*
546  * ======================
547  *  int Table.columns();
548  * ======================
549  *
550  * Returns the number of columns in table.
551  */
columns()552 int Table::columns() {
553   return nCols;
554 }
555 
556 
557 /*
558  * ==================================
559  *  void Table.value(int selection);
560  * ==================================
561  *
562  * Sets the currently selected row.
563  */
value(int selection)564 void Table::value(int selection) {
565   if ((selection >= 0) && (selection < nRows))
566     selected = selection;
567   damage(DAMAGE_ROWS);
568 }
569 
570 
571 /*
572  * ====================
573  *  int Table.value();
574  * ====================
575  *
576  * Returns the number of the currently selected row.
577  */
value()578 int Table::value() {
579   return selected;
580 }
581 
582 
583 /*
584  * ====================================================================
585  *  void Table.addColumn(const char *label, int width, Fl_Align align,
586  *      int (*comparator)(const char*, const char*));
587  * ====================================================================
588  *
589  * Adds column with label as title, width, align and comparator as
590  * sort function.
591  */
addColumn(const char * label,int width,Fl_Align align,int (* comparator)(const char *,const char *))592 void Table::addColumn(const char *label, int width, Fl_Align align,
593     int (*comparator)(const char*, const char*)) {
594   if (!noMoreColumns) {
595     struct ColumnInfo col;
596 
597     dimensionsChanged = true;
598 
599     col.title = strdup(label);
600     col.width = width;
601     col.hidden = false;
602     col.align = (Fl_Align)(align | FL_ALIGN_CLIP);
603                 col.hdr_align = (Fl_Align)(FL_ALIGN_CENTER | FL_ALIGN_CLIP);
604     col.comparator = comparator;
605     col.callback = NULL;
606     header.push_back(col);
607     nCols++;
608   }
609 }
610 
611 
colcomparator(int col,int (* comparator)(const char *,const char *))612 void Table::colcomparator(int col,
613       int (*comparator)(const char*, const char*))
614 {
615   header[col].comparator = comparator;
616 }
617 
colcallback(int col,void (* callback)())618 void Table::colcallback (int col, void (*callback)())
619 {
620    header[col].callback = callback;
621 }
622 
623 /*
624  * ================================================
625  *  void Table.addHiddenColumn(const char *label);
626  * ================================================
627  *
628  * Adds a nonvisible column with label as title.
629  */
addHiddenColumn(const char * label)630 void Table::addHiddenColumn(const char *label) {
631   if (!noMoreColumns) {
632     struct ColumnInfo col;
633 
634     col.title = strdup(label);
635     col.width = 0;
636     col.hidden = true;
637     col.align = FL_ALIGN_LEFT;
638     col.comparator = NULL;
639     header.push_back(col);
640     nCols++;
641   }
642 }
643 
644 
645 /*
646  * =================================
647  *  void Table.addCell(char *data);
648  * =================================
649  *
650  * Adds a cell with data to the table.
651  */
addCell(char * data)652 void Table::addCell(char *data) {
653   if (!noMoreColumns)
654     noMoreColumns = true;
655 
656   if ((cPos >= nCols) || (curRow == NULL)) {
657     this->data.push_back(curRow = new char*[nCols]);
658     dimensionsChanged = true;
659     nRows++;
660     cPos = 0;
661   }
662 
663   if (data != NULL)
664     curRow[cPos] = strdup(data);
665   else
666     curRow[cPos] = strdup("");
667 
668   if (cPos == sortColumn)
669     toBeSorted = true;
670   cPos++;
671 }
672 
673 
674 /*
675  * ===================================
676  *  void Table.addRow(int cols, ...);
677  * ===================================
678  *
679  * Adds cols number of cells to table.
680  */
addRow(int cols,...)681 void Table::addRow(int cols, ...) {
682   char *temp;
683 
684   if (!noMoreColumns)
685     noMoreColumns = true;
686 
687   if ((cPos != 0) || (curRow == NULL)) {
688     this->data.push_back(curRow = new char*[nCols]);
689     dimensionsChanged = true;
690     nRows++;
691     cPos = 0;
692   }
693 
694   if (cols > nCols)
695     cols = nCols;
696 
697   va_list ap;
698   va_start(ap, cols);
699   for (int c = 0; c < cols; c++, cPos++) {
700     if (cPos >= nCols) {
701       this->data.push_back(curRow = new char*[nCols]);
702       dimensionsChanged = true;
703       nRows++;
704       cPos = 0;
705     }
706 
707     if ((temp = va_arg(ap, char *)) != NULL)
708       curRow[cPos] = strdup(temp);
709     else
710       curRow[cPos] = strdup("");
711   }
712   va_end(ap);
713 
714   toBeSorted = true;
715   dimensionsChanged = true;
716 }
717 
718 
719 /*
720  * ================================
721  *  void Table.removeRow(int row);
722  * ================================
723  *
724  * Removes row referenced by row.
725  */
removeRow(int row)726 void Table::removeRow(int row) {
727   if ((row == -1) && (selected >= 0))
728     row = selected;
729   if ((row >= 0) && (row < nRows)) {
730     char **rowData = data[row];
731     if (rowData == curRow)
732       curRow = NULL;
733     for (int i = 0; i < nCols; i++)
734       free(rowData[i]);
735     data.erase(row + data.begin());
736     nRows--;
737     dimensionsChanged = true;
738     toBeSorted = true;
739     selected = -1;
740   }
741   redraw ();
742 }
743 
744 
745 /*
746  * =======================================
747  *  void Table.clear(bool removeColumns);
748  * =======================================
749  *
750  * Frees all data in table. If removeColumns is true, frees also header
751  * structures.
752  */
clear(bool removeColumns)753 void Table::clear(bool removeColumns) {
754   nRows = 0;
755   curRow = NULL;
756   cPos = 0;
757 
758   // Delete row data.
759   vector<char**>::iterator end = data.end();
760   char **row;
761   for (vector<char**>::iterator i = data.begin(); i < end; ++i) {
762     row = *i;
763     for (int i = 0; i < nCols; i++)
764       free(row[i]);
765     delete [] row;
766   }
767   data.clear();
768 
769   if (removeColumns) {
770     // Delete header data.
771     vector<struct ColumnInfo>::iterator end = header.end();
772     for (vector<struct ColumnInfo>::iterator i = header.begin();
773         i < end; ++i)
774       free((void*)(*i).title);
775 
776     header.clear();
777     nCols = 0;
778   }
779 
780   selected = -1;
781   dimensionsChanged = true;
782   redraw ();
783 }
784 
785 
786 /*
787  * ============================================
788  *  char *Table.valueAt(int row, int column);
789  *  int Table.intValueAt(int row, int column);
790  * ============================================
791  *
792  * Returns value in cell referenced by row and column.
793  */
valueAt(int row,int column)794 char *Table::valueAt(int row, int column) {
795   if ((row >= 0) && (row < nRows) && (column >= 0) && (column < nCols))
796     return data[row][column];
797   else if ((row == -1) && (selected >= 0) && (column >= 0) && (column < nCols))
798     return data[selected][column];
799   else
800     return NULL;
801 }
802 
803 
intValueAt(int row,int column)804 int Table::intValueAt(int row, int column) {
805   if ((row == -1) && (selected >= 0))
806     row = selected;
807 
808   if ((row >= 0) && (row < nRows) && (column >= 0) && (column < nCols))
809     return strtol(data[row][column], NULL, 10);
810   else
811     return 0;
812 }
813 
814 
815 /*
816  * ======================================================
817  *  void Table.valueAt(int row, int column, char *data);
818  *  void Table.valueAt(int row, int column, int data);
819  * ======================================================
820  *
821  * Sets alue in cell referenced by row and column.
822  */
valueAt(int row,int column,char * data)823 void Table::valueAt(int row, int column, char *data) {
824   if ((row == -1) && (selected >= 0))
825     row = selected;
826 
827   if ((row >= 0) && (row < nRows) && (column >= 0) && (column < nCols)) {
828     if (column == sortColumn)
829       toBeSorted = true;
830     if (this->data[row][column] != NULL)
831       free(this->data[row][column]);
832     this->data[row][column] = strdup(data);
833   }
834 }
835 
836 
valueAt(int row,int column,int data)837 void Table::valueAt(int row, int column, int data) {
838   if ((row == -1) && (selected >= 0))
839     row = selected;
840 
841   if ((row >= 0) && (row < nRows) && (column >= 0) && (column < nCols)) {
842     if (column == sortColumn)
843       toBeSorted = true;
844     if (this->data[row][column] != NULL)
845       free(this->data[row][column]);
846     string temp = "";
847     temp += data;
848     strcpy(this->data[row][column] = (char*)malloc(temp.length()),
849         temp.c_str());
850   }
851 }
852 
853 
854 /*
855  * =====================================
856  *  const char **Table.getRow(int row);
857  * =====================================
858  *
859  * Returns pointer to the data of the row number row.
860  */
getRow(int row)861 const char **Table::getRow(int row) {
862   if ((row == -1) && (selected >= 0))
863     row = selected;
864 
865   if ((row >= 0) && (row < nRows))
866     return (const char**)data[row];
867   else
868     return NULL;
869 }
870 
871 
872 /*
873  * ==============
874  *  Menu methods
875  * ==============
876  *
877  * These work in the same way as in class Fl_Menu_ (methods menu,
878  * copy and clear). These are used for handling the popup menu.
879  */
menu()880 const Fl_Menu_Item *Table::menu() {
881   return popupMenu;
882 }
883 
menu(const Fl_Menu_Item * m)884 void Table::menu(const Fl_Menu_Item *m) {
885   menuClear();
886   popupMenu = m;
887 }
888 
menuCopy(const Fl_Menu_Item * m)889 void Table::menuCopy(const Fl_Menu_Item *m) {
890   int n = m->size();
891 
892   Fl_Menu_Item* newMenu = new Fl_Menu_Item[n];
893   memcpy(newMenu, m, n * sizeof(Fl_Menu_Item));
894   menu(newMenu);
895   menuAlloc = true;
896 }
897 
menuClear()898 void Table::menuClear() {
899   if (menuAlloc)
900     delete[] popupMenu;
901   popupMenu = NULL;
902 }
903 
904 
905 /*
906  * ================================================================
907  *  Table.where(int x, int y, int &row, int &column, int &resize);
908  * ================================================================
909  *
910  * Finds corresponding row and column for x and y coordinates. This function
911  * uses Fl::event_inside() method.
912  *
913  * row = -1 means header and row = -2 means that coordinates don't
914  * correspond any cell.
915  */
where(int x,int y,int & row,int & column,int & resize)916 void Table::where(int x, int y, int &row, int &column, int &resize) {
917   int temp, temp2;
918 
919   // Inside the header
920   if ((nCols > 0) && headerEnabled &&
921        Fl::event_inside(oX, oY, iW, headerHeight)) {
922     row = -1;
923     temp = leftColX + iX - hScroll->value();
924 
925     // Scan visible columns until found one that matches.
926     for (column = leftCol; column <= rightCol; column++ ) {
927       if (header[column].hidden)
928         continue;
929 
930       temp2 = temp;
931 
932       // Near the left border of the column header
933       if ((x >= temp) && (x <= temp + 3)) {
934         resize = 1;
935         return;
936         /* NOT REACHED */
937       }
938 
939       // Near the right border of the column header
940       else if ((x >= (temp += header[column].width) - 3) &&
941           (x < temp)) {
942         resize = 2;
943         return;
944         /* NOT REACHED */
945       }
946 
947       // Somewhere else
948       else if ((x >= temp2) && (x < temp)) {
949         resize = 0;
950         return;
951         /* NOT REACHED */
952       }
953     }
954   } // Header
955 
956 
957   /*
958    * Now the harder one. X and Y lie somewhere in the table.
959    * Find correct row and column.
960    */
961   else if ((nRows > 0) && Fl::event_inside(iX, iY, iW, iH)) {
962     temp = topRowY;
963     int yMod = iY - vScroll->value();
964     int leftX = leftColX + iX - hScroll->value();
965 
966     // Scan rows
967     for (row = topRow; row <= bottomRow; row++) {
968       int temp2 = leftX;
969       for (column = leftCol; column <= rightCol; column++) {
970         if (header[column].hidden)
971           continue;
972 
973         if (Fl::event_inside(temp2, temp + yMod,
974             header[column].width, rowHeight))
975           return;
976           /* NOT REACHED */
977         temp2 += header[column].width;
978       }
979       temp += rowHeight;
980     }
981   }
982   row = column = -2;
983 }
984 
985 
FirstRow()986 void Table::FirstRow()
987 {
988   if (nRows == 0 || selected == 0) return;
989   scrollTo (selected = 0);
990 }
991 
PrevPage()992 void Table::PrevPage ()
993 {
994 // Does it make sense to move up?
995   if (selected > 0) {
996 // Number of rows on the 'page'
997     int step = iH / rowHeight;
998 // Change selection
999     if (selected >= step)
1000       selected -= step;
1001     else
1002       selected = 0;
1003     scrollTo(selected * rowHeight);
1004   }
1005 }
1006 
PrevRow()1007 void Table::PrevRow()
1008 {
1009   int newpos, oldpos;
1010   if (nRows == 0) return;
1011   selected = (selected > 0) ? selected - 1 : 0;
1012   oldpos = vScroll->value();
1013   newpos = rowHeight * selected;
1014   if (newpos - oldpos > 0)
1015     scrollTo (oldpos);
1016   else
1017     scrollTo (newpos);
1018 }
1019 
NextRow()1020 void Table::NextRow()
1021 {
1022   int newpos, oldpos, lastrow;
1023   if (nRows == 0 || selected == (nRows - 1)) return;
1024   lastrow = nRows -1;
1025   selected = (selected < lastrow) ? selected + 1 : lastrow;
1026   oldpos = vScroll->value();
1027   newpos = rowHeight *(selected + 1) - iH;
1028   if (newpos - oldpos < 0)
1029     scrollTo (oldpos);
1030   else
1031     scrollTo (newpos);
1032 }
1033 
NextPage()1034 void Table::NextPage ()
1035 {
1036   if ((selected >= 0) && (selected < (nRows - 1))) {
1037     int step = iH / rowHeight;
1038     if ((selected += step) >= nRows)
1039       selected = nRows - 1;
1040     scrollTo(rowHeight * (selected + 1) - iH);
1041   }
1042 }
1043 
LastRow()1044 void Table::LastRow()
1045 {
1046   if (nRows == 0) return;
1047   selected = nRows - 1;
1048   scrollTo (rowHeight * (selected + 1) - iH);
1049 }
1050 
GotoRow(int n)1051 void Table::GotoRow(int n)
1052 {
1053   if (n >= 0 && (n < nRows)) {
1054     selected = n;
1055     scrollTo(rowHeight * (selected + 1) - iH);
1056   }
1057 }
1058 
1059 /*
1060  * ==============================
1061  *  int Table.handle(int event);
1062  * ==============================
1063  *
1064  * FLTK internal. Handles incoming events.
1065  */
handle(int event)1066 int Table::handle(int event) {
1067   int ret = 0;
1068   static int row, prev_row;
1069   int column, resize;
1070 
1071   if (event != FL_KEYDOWN)
1072     ret = Fl_Group::handle(event);
1073 
1074   /*
1075    * MAIN SWITCH
1076    */
1077   switch (event) {
1078 
1079   /*
1080    * PUSH event
1081    */
1082   case FL_PUSH:
1083     // Which row/column are we over?
1084     where(Fl::event_x(), Fl::event_y(), row, column, resize);
1085 
1086     switch (row) {
1087     // Push on nothing... Not interested
1088     case -2:
1089       if (selected != -1) {
1090 //        selected = -1;
1091         damage(DAMAGE_ROWS);
1092       }
1093       break;
1094 
1095     // Push on header.
1096     case -1:
1097       if ((canResize) && (Fl::event_button() == 1) &&
1098           (resize != 0)) {
1099         resizing = (resize == 1) ? column - 1 : column;
1100         dragX = Fl::event_x();
1101         ret = 1;
1102       }
1103       else  if ((canSort) && (Fl::event_button() == 1)) {
1104         pushed = column;
1105         damage(DAMAGE_HEADER);
1106         ret = 1;
1107       }
1108       break;
1109 
1110       // Push on cell.
1111       default:
1112       bool changed = selected != row;
1113       selected = row;
1114 
1115       // Create new selection
1116       int len = 0;
1117       char **tableRow = data[selected];
1118       char *buffer = nullptr;
1119 
1120       for (int col = 0; col < nCols; col++)
1121         len += strlen(tableRow[col]) + 1;
1122 
1123       // Create a tab separated list from data.
1124       buffer = (char*)malloc(len);
1125       if (buffer != nullptr) {
1126           strcpy(buffer, tableRow[0]);
1127           for (int col = 1; col < nCols; col++) {
1128             strcat(buffer, "\t");
1129             strcat(buffer, tableRow[col]);
1130           }
1131           Fl::selection(*this, buffer, len);
1132           free(buffer);
1133       }
1134 
1135       // Update view.
1136       damage(DAMAGE_ROWS);
1137       take_focus();
1138 
1139       // Show popup menu
1140       if ((Fl::event_button() == 3) && (popupMenu != NULL)) {
1141         const Fl_Menu_Item *m;
1142         m = popupMenu->popup(Fl::event_x(),
1143             Fl::event_y());
1144         if (m != NULL)
1145           m->do_callback(this, m->user_data());
1146 	ret = 1;
1147 	break;
1148       }
1149 
1150 
1151       // Callback
1152       if ((Fl::event_clicks() != 0) && !changed &&
1153           (when() & TABLE_WHEN_DCLICK)) {
1154             Fl::event_is_click(0);
1155         do_callback();
1156       }
1157       else if (changed && (when() & FL_WHEN_CHANGED))
1158         do_callback();
1159       else if (!changed && (when() & FL_WHEN_NOT_CHANGED))
1160 	do_callback();
1161       ret = 1;
1162       break;
1163     } // switch(row)
1164     break;
1165 
1166 
1167   /*
1168    * DRAG event
1169    */
1170   case FL_DRAG:
1171     // Resizing...
1172     if (resizing > -1 ) {
1173       int offset = dragX - Fl::event_x();
1174       int newWidth = header[resizing].width - offset;
1175 
1176       // Width must be at least 1.
1177       if (newWidth < 1)
1178         newWidth = 1;
1179 
1180       // Test if column really is resized.
1181       if (header[resizing].width != newWidth) {
1182         header[resizing].width = newWidth;
1183         dragX = Fl::event_x();
1184         resized();
1185         redraw();
1186       }
1187       ret = 1;
1188     }
1189     else {
1190       prev_row = row;
1191       where(Fl::event_x(), Fl::event_y(), row, column, resize);
1192       if (row < 0 || pushed != -1) {
1193         ret = 1;
1194       	break;
1195       }
1196       if (prev_row != row) {
1197         selected = row;
1198         damage(DAMAGE_ROWS);
1199         take_focus();
1200         if (when() & FL_WHEN_CHANGED)
1201           do_callback();
1202       }
1203     }
1204     break;
1205 
1206 
1207   /*
1208    * RELEASE event
1209    */
1210   case FL_RELEASE:
1211     // Which row/column are we over?
1212     where(Fl::event_x(), Fl::event_y(), row, column, resize);
1213 
1214     // Restore cursor and end resizing.
1215     if (Fl::event_button() == 1) {
1216       fl_cursor(FL_CURSOR_DEFAULT, FL_BLACK, FL_WHITE);
1217       if ((pushed == column) && canSort) {
1218 
1219 if (this->header[pushed].callback != NULL)
1220   (this->header[pushed].callback)();
1221 
1222 if (this->header[pushed].comparator != NULL) {
1223         if (sortColumn == pushed) {
1224           if (ascent)
1225             ascent = false;
1226           else
1227             sortColumn = -1;
1228         }
1229         else {
1230           ascent = true;
1231           sortColumn = pushed;
1232         }
1233         sort();
1234 }
1235         redraw();
1236       }
1237       pushed = -1;
1238       resizing = -1;
1239       ret = 1;
1240     }
1241 
1242     // Callback.
1243     if ((row >= 0) && (when() & FL_WHEN_RELEASE))
1244         do_callback();
1245     break;
1246 
1247 
1248   /*
1249    * MOVE event
1250    */
1251   case FL_MOVE:
1252     // Which row/column are we over?
1253     where(Fl::event_x(), Fl::event_y(), row, column, resize);
1254 
1255     // If near header boundary.
1256     if ((row == -1) && canResize && resize)
1257       fl_cursor(FL_CURSOR_WE, FL_BLACK, FL_WHITE);
1258     else
1259       fl_cursor(FL_CURSOR_DEFAULT, FL_BLACK, FL_WHITE);
1260     ret = 1;
1261     break;
1262 
1263   case FL_ENTER:
1264   case FL_LEAVE:
1265     if (event == FL_LEAVE)
1266       fl_cursor(FL_CURSOR_DEFAULT, FL_BLACK, FL_WHITE);
1267     ret = 1;
1268     break;
1269 
1270   case FL_FOCUS:
1271   case FL_UNFOCUS:
1272     if (Fl::visible_focus()) {
1273       damage(DAMAGE_ROWS);
1274       ret = 1;
1275     }
1276     break;
1277 
1278   /*
1279    * KEYDOWN event
1280    */
1281   case FL_KEYDOWN:
1282     switch(Fl::event_key())  {
1283     case FL_Enter:
1284       if ((selected > -1) && ((when() & TABLE_WHEN_DCLICK) ||
1285           (when() & FL_WHEN_ENTER_KEY)))
1286         do_callback();
1287       ret = 1;
1288       break;
1289 
1290     case FL_Home:
1291       FirstRow();
1292       ret = 1;
1293       break;
1294 
1295     case FL_Up:
1296       PrevRow();
1297       ret = 1;
1298       break;
1299 
1300     case FL_Down:
1301       NextRow();
1302       ret = 1;
1303       break;
1304 
1305     case FL_End:
1306       LastRow();
1307       ret = 1;
1308       break;
1309 
1310     case FL_Page_Up:
1311       PrevPage ();
1312       ret = 1;
1313       break;
1314 
1315     case FL_Page_Down:
1316       NextPage ();
1317       ret = 1;
1318       break;
1319 
1320     }
1321     break;
1322 
1323   }
1324   return ret;
1325 }
1326 
1327 
1328 /*
1329  * ===============================
1330  *  void Table.scrollTo(int pos);
1331  * ===============================
1332  *
1333  * Scrolls table to given position.
1334  */
scrollTo(int pos)1335 void Table::scrollTo(int pos) {
1336 
1337   if (vScroll->visible() || nRows > (iH / rowHeight)) {
1338     int max = rowHeight * nRows - iH;
1339 //printf ("pos %d, max %d\n", pos, max); fflush (stdout);
1340     if (pos < 0 || max < 0) pos = 0;
1341     if (pos > max) pos = max;
1342 
1343     vScroll->Fl_Valuator::value(1.0*pos);
1344     vScroll->damage (FL_DAMAGE_ALL);
1345     vScroll->redraw ();
1346     scrolled();
1347   }
1348   damage(DAMAGE_ROWS);
1349   if (when() & FL_WHEN_CHANGED)
1350     do_callback();
1351 
1352 }
1353 
scrollPos() const1354 int Table::scrollPos() const
1355 {
1356   return (int)vScroll->value();
1357 }
1358 
1359 /*
1360  * ===========================================
1361  *  void Table.sort(int column, bool ascent);
1362  * ===========================================
1363  *
1364  * Sets sortColumn and ascent and sorts table. Does not redraw.
1365  */
sort(int column,bool ascent)1366 void Table::sort(int column, bool ascent) {
1367   if ((column < -1) || (column >= nCols))
1368     return;
1369 
1370   sortColumn = column;
1371   this->ascent = ascent;
1372   sort();
1373 }
1374 
1375 
aSort(int start,int end,int (* compare)(const char *,const char *))1376 void Table::aSort(int start, int end,
1377     int (*compare)(const char *, const char*)) {
1378   int i, j;
1379   const char *x;
1380   char **temp;
1381 
1382   x = data[(start + end) / 2][sortColumn];
1383   i = start;
1384   j = end;
1385 
1386   for (;;) {
1387     while ((i < end) && (compare(data[i][sortColumn], x) < 0))
1388       i++;
1389     while ((j > 0) && (compare(data[j][sortColumn], x) > 0))
1390       j--;
1391 
1392     while ((i < end) && (i != j) &&
1393        (compare(data[i][sortColumn], data[j][sortColumn]) == 0))
1394       i++;
1395 
1396     if (i == j)
1397       break;
1398 
1399     temp = data[i];
1400     data[i] = data[j];
1401     data[j] = temp;
1402   }
1403 
1404   if (start < --i)
1405     aSort(start, i, compare);
1406 
1407   if (end > ++j)
1408     aSort(j, end, compare);
1409 }
1410 
1411 
dSort(int start,int end,int (* compare)(const char *,const char *))1412 void Table::dSort(int start, int end,
1413     int (*compare)(const char *, const char*)) {
1414   int i, j;
1415   const char *x;
1416   char **temp;
1417 
1418   x = data[(start + end) / 2][sortColumn];
1419   i = start;
1420   j = end;
1421 
1422   for (;;) {
1423     while ((i < end) && (compare(data[i][sortColumn], x) > 0))
1424       i++;
1425     while ((j > 0) && (compare(data[j][sortColumn], x) < 0))
1426       j--;
1427 
1428     while ((i < end) && (i != j) &&
1429        (compare(data[i][sortColumn], data[j][sortColumn]) == 0))
1430       i++;
1431 
1432     if (i == j)
1433       break;
1434 
1435     temp = data[i];
1436     data[i] = data[j];
1437     data[j] = temp;
1438   }
1439 
1440   if (start < --i)
1441     dSort(start, i, compare);
1442 
1443   if (end > ++j)
1444     dSort(j, end, compare);
1445 }
1446 
1447 
1448 /*
1449  * ====================
1450  *  void Table.sort();
1451  * ====================
1452  *
1453  * Sorts table according sortColumn and ascent. Does not redraw.
1454  */
sort()1455 void Table::sort() {
1456   if ((sortColumn == -1) || !canSort)
1457     return;
1458     /* NOT REACHED */
1459 
1460   toBeSorted = false;
1461 
1462   int (*compare)(const char *, const char*);
1463 
1464   // Get comparator function or set it to the default.
1465   if (this->header[sortColumn].comparator == NULL)
1466 //    compare = strcasecmp;
1467           return;
1468   else
1469     compare = header[sortColumn].comparator;
1470 
1471   // Sort in descending order.
1472   if ((nRows > 1) && ascent)
1473     aSort(0, nRows - 1, compare);
1474 
1475   // Sort in ascending order.
1476   else if (nRows > 1)
1477     dSort(0, nRows - 1, compare);
1478 }
1479 
1480 
1481 /*
1482  * ====================================================
1483  *  void Table.getSort(int &sortColumn, bool &ascent);
1484  * ====================================================
1485  *
1486  * Set sortColumn and ascent according to current sort policy.
1487  */
getSort(int & sortColumn,bool & ascent)1488 void Table::getSort(int &sortColumn, bool &ascent) {
1489   sortColumn = this->sortColumn;
1490   ascent = this->ascent;
1491 }
1492 
1493 
1494 /*
1495  * =====================================================
1496  *  int compareInt(const char *val1, const char *val2);
1497  * =====================================================
1498  *
1499  * This function compares values as numbers instead of strings. Solves
1500  * problem with string sorting (eg. 1 - 10 - 11 - 12 - 2 - 3 ...).
1501  */
compareInt(const char * val1,const char * val2)1502 int compareInt(const char *val1, const char *val2) {
1503   return strtol(val1, NULL, 0) - strtol(val2, NULL, 0);
1504 }
1505 
1506 
1507 /*
1508  * ==============================================================================
1509  *  void Table.setHighlighter(bool (*highliter)(int, char **, Fl_Color &color));
1510  * ==============================================================================
1511  *
1512  * Sets highlighter function to highlighter. Highlighter is used to determine
1513  * text color in Table.drawRow().
1514  */
setHighlighter(bool (* highlighter)(int,char **,Fl_Color *))1515 void Table::setHighlighter(bool (*highlighter)(int, char **, Fl_Color *)) {
1516   this->highlighter = highlighter;
1517 }
1518 
1519 
1520 /*
1521  * ====================
1522  *  void Table.draw();
1523  * ====================
1524  *
1525  * FLTK internal. Called when Table widget needs to be drawn.
1526  */
draw()1527 void Table::draw() {
1528   int damage;
1529 
1530   if (dimensionsChanged) {
1531     dimensionsChanged = false;
1532     resized();
1533   }
1534 
1535   if (toBeSorted)
1536     sort();
1537 
1538   damage = Fl_Widget::damage();
1539 
1540   // Draw children.
1541   if (damage & (FL_DAMAGE_ALL | FL_DAMAGE_CHILD)) {
1542     fl_push_clip(oX, oY, oW, oH);
1543     Fl_Group::draw();
1544     fl_pop_clip();
1545   }
1546 
1547   // Draw box.
1548   if (damage & FL_DAMAGE_ALL) {
1549     // Draw box.
1550     draw_box(box(), x(), y(), w(), h(), FL_GRAY);
1551 
1552     // Draw label.
1553     draw_label();
1554   }
1555 
1556   // Draw header.
1557   int xPos = leftColX + iX - hScroll->value();
1558   if (headerEnabled && (damage & (FL_DAMAGE_ALL | DAMAGE_HEADER)) &&
1559       (nCols > 0)) {
1560     fl_push_clip(iX, oY, iW, headerHeight);
1561     drawHeader(xPos, oY);
1562     fl_pop_clip();
1563   }
1564 
1565   // Draw all the cells.
1566   if ((damage & (FL_DAMAGE_ALL | DAMAGE_ROWS)) && (nRows > 0) &&
1567       (nCols > 0)) {
1568     fl_push_clip(iX, iY, iW, iH);
1569 
1570     int yMod = iY - vScroll->value();
1571     for (int row = topRow, rowY = topRowY; row <= bottomRow;
1572         row++, rowY += rowHeight)
1573       drawRow(row, data[row], xPos, rowY + yMod);
1574     fl_pop_clip();
1575   }
1576 
1577   fl_push_clip(oX, oY, oW, oH);
1578 
1579   if (tableWidth < iW)
1580     fl_rectf(iX + tableWidth, oY, iW - tableWidth, oH, FL_GRAY);
1581 
1582   // Table height smaller than window? Fill remainder with rectangle
1583   if (tableHeight < iH)
1584     fl_rectf(iX, iY + tableHeight, iW, iH - tableHeight, FL_GRAY);
1585 
1586   if (vScroll->visible()) {
1587                 vScroll->damage (FL_DAMAGE_ALL);
1588     vScroll->redraw();
1589   }
1590   if (hScroll->visible()) {
1591     hScroll->damage (FL_DAMAGE_ALL);
1592     hScroll->redraw();
1593   }
1594 
1595   // Both scrollbars? Draw little box in lower right
1596   if (vScroll->visible() && hScroll->visible())
1597     fl_rectf(vScroll->x(), hScroll->y(), vScroll->w(), hScroll->h(), FL_GRAY);
1598   fl_pop_clip();
1599 }
1600 
1601 
1602 /*
1603  * ================================================
1604  *  void Table.resize(int x, int y, int w, int h);
1605  * ================================================
1606  *
1607  * FLTK internal. Called when Table widget is resized.
1608  */
resize(int x,int y,int w2,int h)1609 void Table::resize(int x, int y, int w2, int h) {
1610 // resize the columns proportionally if the width changes
1611   if (w2 != w()) {
1612     int iw = w() - (vScroll->visible() ? vScroll->w() : 0) - 4;
1613     int iw2 = w2 - (vScroll->visible() ? vScroll->w() : 0) - 4;
1614     if (iw > 0 && iw2 > 0) {
1615       int lastcol = 0;
1616       int iw3 = 0;
1617       for (int i = 0; i < nCols - 1; i++) {
1618         if (!header[i].hidden) {
1619           header[i].width = (int)(1.0 * header[i].width * iw2 / iw + 0.5);
1620           iw3 += header[i].width;
1621           lastcol = i;
1622         }
1623       }
1624       // adjust last visible column
1625       if (iw3 < iw2) header[lastcol].width += (iw2 - iw3);
1626       if (iw3 > iw2) header[lastcol].width -= (iw3 - iw2);
1627     }
1628   }
1629   Fl_Widget::resize(x, y, w2, h);
1630   resized();
1631   damage(FL_DAMAGE_ALL);
1632 }
1633 
1634 
1635 /*
1636  * ==============================
1637  *  void Table.calcDimensions();
1638  * ==============================
1639  *
1640  * Calculates table dimensions.
1641  */
calcDimensions()1642 void Table::calcDimensions() {
1643   // Calculate width and height of the table (in pixels).
1644   tableWidth = 0;
1645   for (int i = 0; i < nCols; i++)
1646     if (!header[i].hidden)
1647       tableWidth +=header[i].width;
1648 
1649   tableHeight = nRows * rowHeight;
1650   Fl_Boxtype b;
1651 
1652   iX = oX = x() + Fl::box_dx(b = box());
1653   iY = oY = y() + Fl::box_dy(b);
1654   iW = oW = w() - Fl::box_dw(b);
1655   iH = oH = h() - Fl::box_dh(b);
1656 
1657   // Trim inner size if header enabled.
1658   if (headerEnabled) {
1659     iY += headerHeight;
1660     iH -= headerHeight;
1661   }
1662 
1663   // Hide scrollbars if window is large enough
1664   int hideV, hideH;
1665 
1666   hideV = (tableHeight <= iH),
1667   hideH = (tableWidth <= iW);
1668 
1669   if (!hideH && hideV)
1670     hideV = (tableHeight - iH - scrollbarSize) <= 0;
1671   if (!hideV && hideH)
1672     hideH = (tableWidth - iW + scrollbarSize) <= 0;
1673 
1674   if (Vscroll == always) {
1675     vScroll->show();
1676     iW -= scrollbarSize;
1677   } else if (Vscroll == never) {
1678     vScroll->hide();
1679     vScroll->Fl_Valuator::value(0);
1680   } else if (hideV) {
1681     vScroll->hide();
1682     vScroll->Fl_Valuator::value(0);
1683   }
1684   else {
1685     vScroll->show();
1686     iW -= scrollbarSize;
1687   }
1688 
1689   if (Hscroll == always) {
1690     hScroll->show();
1691     iH -= scrollbarSize;
1692   } else if (Hscroll == never) {
1693     hScroll->hide();
1694     hScroll->Fl_Valuator::value(0);
1695   } else if (hideH) {
1696     hScroll->hide();
1697     hScroll->Fl_Valuator::value(0);
1698   }
1699   else {
1700     hScroll->show();
1701     iH -= scrollbarSize;
1702   }
1703 }
1704 
1705 
1706 /*
1707  * ========================
1708  *  void Table.scrolled();
1709  * ========================
1710  *
1711  * Calculates visible are after scroll or adding data.
1712  */
scrolled()1713 void Table::scrolled()
1714 {
1715   int y, row, voff = vScroll->value();
1716 
1717   // First visible row
1718   row = voff / rowHeight;
1719   topRow = (row >= nRows) ? (nRows - 1) : row;
1720   topRow = (topRow < 0) ? 0 : topRow;
1721 
1722   y = topRow * rowHeight;
1723   if ((topRow > 0) && (y > voff)) {
1724     topRow--;
1725     y -= rowHeight;
1726   }
1727   topRowY = y;
1728 
1729   // Last visible row
1730   row = (voff + iH) / rowHeight;
1731   bottomRow = (row >= nRows) ? (nRows - 1) : row;
1732 
1733   // First visible column
1734   int x, col, h = hScroll->value();
1735   for (col = x = 0; col < nCols; col++) {
1736     if (header[col].hidden)
1737       continue;
1738 
1739     x += header[col].width;
1740     if (x >= h) {
1741       x -= header[col].width;
1742       break;
1743     }
1744   }
1745   leftCol = (col >= nCols) ? (nCols - 1) : col;
1746   leftColX = x;
1747 
1748   // Last visible column
1749   h += iW;
1750   for (; col < nCols; col++) {
1751     if (header[col].hidden)
1752       continue;
1753 
1754     x += header[col].width;
1755     if (x >= h)
1756       break;
1757   }
1758   rightCol = (col >= nCols) ? (nCols - 1) : col;
1759 }
1760 
1761 
1762 /*
1763  * =======================
1764  *  void Table.resized();
1765  * =======================
1766  *
1767  * Calculates scrollbar properties after resizing or adding data.
1768  */
resized()1769 void Table::resized() {
1770   calcDimensions();
1771 
1772   // Calculate properties for vertical scrollbar.
1773   if (vScroll->visible()) {
1774     vScroll->bounds(0, tableHeight - iH);
1775     vScroll->resize(oX + oW - scrollbarSize, oY, scrollbarSize,
1776         oH - (hScroll->visible() ? scrollbarSize : 0));
1777     vScroll->Fl_Valuator::value(vScroll->clamp(vScroll->value()));
1778     vScroll->slider_size(iH > tableHeight ? 1 : (float)iH / tableHeight);
1779   }
1780 
1781   // Calculate properties for horizontal scrollbar.
1782   if (hScroll->visible()) {
1783     hScroll->bounds(0, tableWidth - iW);
1784     hScroll->resize(oX, oY + oH - scrollbarSize,
1785         oW - (vScroll->visible() ? scrollbarSize : 0), scrollbarSize);
1786     hScroll->Fl_Valuator::value(hScroll->clamp(hScroll->value()));
1787     hScroll->slider_size(iW > tableWidth ? 1 : (float)iW / tableWidth);
1788   }
1789   scrolled();
1790   dimensionsChanged = false;
1791 }
1792 
1793 
1794 /*
1795  * ===========================================================
1796  *  void Table.scrollCallback(Fl_Widget *widget, void *data);
1797  * ===========================================================
1798  *
1799  * Internal callback for scrollbars. Scrolls view.
1800  */
scrollCallback(Fl_Widget * widget,void * data)1801 void Table::scrollCallback(Fl_Widget *widget, void *data) {
1802   Table *me = (Table*)data;
1803 
1804   me->scrolled();
1805 
1806   if (widget == me->vScroll)
1807     me->damage(DAMAGE_ROWS);
1808   else
1809     me->damage(DAMAGE_ROWS | DAMAGE_HEADER);
1810 }
1811 
1812 #include "re.h"
1813 
1814 inline static
search_row(const std::vector<char ** > & data,int row,int col,int ncols,fre_t & re,bool allcols)1815 bool search_row(const std::vector<char**>& data, int row, int col, int ncols, fre_t& re, bool allcols)
1816 {
1817   if (unlikely(allcols)) {
1818     for (col = 0; col < ncols; col++)
1819       if (re.match(data[row][col]))
1820 	return true;
1821   }
1822   else if (re.match(data[row][col]))
1823     return true;
1824   return false;
1825 }
1826 
1827 /*
1828  * ==================================================================
1829  *  void Table.search(int& row, int& col, bool rev, const char* re);
1830  * ==================================================================
1831  *
1832  * Searches Table data starting at `row', in direction indicated by `rev',
1833  * for column data matching regexp `re'.  Looks in all row columns if `col'
1834  * is equal to nCols, or just the specified column if 0 <= col < nCols.
1835  * Returns true if found, in which case the `row' and `col' arguments will
1836  * point to the matching data.  If false is returned, the contents of
1837  * `row' and `col' are undefined.
1838  */
1839 
search(int & row,int & col,bool rev,const char * re)1840 bool Table::search(int& row, int& col, bool rev, const char* re)
1841 {
1842   if (unlikely(col < 0 || col > nCols || row < 0 || row >= nRows))
1843     return false;
1844 
1845   bool allcols = col == nCols;
1846   fre_t sre(re, REG_EXTENDED | REG_ICASE | REG_NOSUB);
1847   if (!sre)
1848     return false;
1849 
1850 
1851   int r = row;
1852   if (rev) {
1853     for (; row >= 0; row--)
1854       if (search_row(data, row, col, nCols, sre, allcols))
1855 	return true;
1856     for (row = nRows - 1; row > r; row--)
1857       if (search_row(data, row, col, nCols, sre, allcols))
1858 	return true;
1859   }
1860   else {
1861     for (; row < nRows; row++)
1862       if (search_row(data, row, col, nCols, sre, allcols))
1863 	return true;
1864     for (row = 0; row < r; row++)
1865       if (search_row(data, row, col, nCols, sre, allcols))
1866 	return true;
1867   }
1868 
1869   return false;
1870 }
1871