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