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