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