Tweak themes for more color consistency.
[ntk.git] / examples / table-sort.cxx
blobbe141130b3370ff6f59457aa249a66c870f88e52
1 //
2 // "$Id: table-sort.cxx 8193 2011-01-05 17:58:16Z greg.ercolano $"
3 //
4 // table-sort -- An example application using a sortable Fl_Table
5 //
6 // Originally the 'sortapp.cxx' example program that came with
7 // erco's Fl_Table widget. Added to FLTK in 2010.
8 //
9 // Example of a non-trivial application that uses Fl_Table
10 // with sortable columns. This example is not trying to be simple,
11 // but to demonstrate the complexities of an actual app.
13 // Copyright 2010 Greg Ercolano.
14 // Copyright 1998-2010 by Bill Spitzak and others.
16 // This library is free software; you can redistribute it and/or
17 // modify it under the terms of the GNU Library General Public
18 // License as published by the Free Software Foundation; either
19 // version 2 of the License, or (at your option) any later version.
21 // This library is distributed in the hope that it will be useful,
22 // but WITHOUT ANY WARRANTY; without even the implied warranty of
23 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 // Library General Public License for more details.
26 // You should have received a copy of the GNU Library General Public
27 // License along with this library; if not, write to the Free Software
28 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
29 // USA.
31 // Please report all bugs and problems on the following page:
33 // http://www.fltk.org/str.php
34 //
36 #include <FL/Fl.H>
37 #include <FL/Fl_Double_Window.H>
38 #include <FL/fl_draw.H>
39 #include <FL/Fl_Table_Row.H>
41 #include <stdio.h>
42 #include <string.h>
43 #include <vector>
44 #include <algorithm> // STL sort
46 #define MARGIN 20
48 #ifdef WIN32
49 // WINDOWS
50 # define DIRCMD "dir"
51 # define DIRHEADER { "Date", "Time", "Size", "Filename", "", "", "", "", "" }
52 # ifdef _MSC_VER
53 # define popen _popen
54 # endif
55 #else /*WIN32*/
56 // UNIX
57 # include <ctype.h>
58 # define DIRCMD "ls -l"
59 # define DIRHEADER { "Perms", "#L", "Own", "Group", "Size", "Date", "", "", "Filename" }
60 #endif /*WIN32*/
62 // A single row of columns
63 class Row {
64 public:
65 std::vector<char*> cols;
68 // Sort class to handle sorting column using std::sort
69 class SortColumn {
70 int _col, _reverse;
71 public:
72 SortColumn(int col, int reverse) {
73 _col = col;
74 _reverse = reverse;
76 bool operator()(const Row &a, const Row &b) {
77 const char *ap = ( _col < (int)a.cols.size() ) ? a.cols[_col] : "",
78 *bp = ( _col < (int)b.cols.size() ) ? b.cols[_col] : "";
79 if ( isdigit(*ap) && isdigit(*bp) ) { // cheezy detection of numeric data
80 // Numeric sort
81 int av=0; sscanf(ap, "%d", &av);
82 int bv=0; sscanf(bp, "%d", &bv);
83 return( _reverse ? av < bv : bv < av );
84 } else {
85 // Alphabetic sort
86 return( _reverse ? strcmp(ap, bp) > 0 : strcmp(ap, bp) < 0 );
91 // Derive a custom class from Fl_Table_Row
92 class MyTable : public Fl_Table_Row {
93 private:
94 std::vector<Row> _rowdata; // data in each row
95 int _sort_reverse;
96 int _sort_lastcol;
98 static void event_callback(Fl_Widget*, void*);
99 void event_callback2(); // callback for table events
101 protected:
102 void draw_cell(TableContext context, int R=0, int C=0, // table cell drawing
103 int X=0, int Y=0, int W=0, int H=0);
104 void sort_column(int col, int reverse=0); // sort table by a column
105 void draw_sort_arrow(int X,int Y,int W,int H,int sort);
107 public:
108 // Ctor
109 MyTable(int x, int y, int w, int h, const char *l=0) : Fl_Table_Row(x,y,w,h,l) {
110 _sort_reverse = 0;
111 _sort_lastcol = -1;
112 end();
113 callback(event_callback, (void*)this);
115 ~MyTable() { } // Dtor
116 void load_command(const char *cmd); // Load the output of a command into table
117 void autowidth(int pad); // Automatically set column widths to data
118 void resize_window(); // Resize parent window to size of table
121 // Sort a column up or down
122 void MyTable::sort_column(int col, int reverse) {
123 std::sort(_rowdata.begin(), _rowdata.end(), SortColumn(col, reverse));
124 redraw();
127 // Draw sort arrow
128 void MyTable::draw_sort_arrow(int X,int Y,int W,int H,int sort) {
129 int xlft = X+(W-6)-8;
130 int xctr = X+(W-6)-4;
131 int xrit = X+(W-6)-0;
132 int ytop = Y+(H/2)-4;
133 int ybot = Y+(H/2)+4;
134 if ( _sort_reverse ) {
135 // Engraved down arrow
136 fl_color(FL_WHITE);
137 fl_line(xrit, ytop, xctr, ybot);
138 fl_color(41); // dark gray
139 fl_line(xlft, ytop, xrit, ytop);
140 fl_line(xlft, ytop, xctr, ybot);
141 } else {
142 // Engraved up arrow
143 fl_color(FL_WHITE);
144 fl_line(xrit, ybot, xctr, ytop);
145 fl_line(xrit, ybot, xlft, ybot);
146 fl_color(41); // dark gray
147 fl_line(xlft, ybot, xctr, ytop);
151 // Handle drawing all cells in table
152 void MyTable::draw_cell(TableContext context, int R, int C, int X, int Y, int W, int H) {
153 const char *s = "";
154 if ( R < (int)_rowdata.size() && C < (int)_rowdata[R].cols.size() )
155 s = _rowdata[R].cols[C];
156 switch ( context ) {
157 case CONTEXT_COL_HEADER:
158 fl_push_clip(X,Y,W,H); {
159 static const char *head[] = DIRHEADER;
160 fl_draw_box(FL_THIN_UP_BOX, X,Y,W,H, FL_BACKGROUND_COLOR);
161 if ( C < 9 ) {
162 fl_font(FL_HELVETICA_BOLD, 16);
163 fl_color(FL_BLACK);
164 fl_draw(head[C], X+2,Y,W,H, FL_ALIGN_LEFT, 0, 0); // +2=pad left
165 // Draw sort arrow
166 if ( C == _sort_lastcol ) {
167 draw_sort_arrow(X,Y,W,H, _sort_reverse);
171 fl_pop_clip();
172 return;
173 case CONTEXT_CELL: {
174 fl_push_clip(X,Y,W,H); {
175 // Bg color
176 Fl_Color bgcolor = row_selected(R) ? selection_color() : FL_WHITE;
177 fl_color(bgcolor); fl_rectf(X,Y,W,H);
178 fl_font(FL_HELVETICA, 16);
179 fl_color(FL_BLACK); fl_draw(s, X+2,Y,W,H, FL_ALIGN_LEFT); // +2=pad left
180 // Border
181 fl_color(FL_LIGHT2); fl_rect(X,Y,W,H);
183 fl_pop_clip();
184 return;
186 default:
187 return;
191 // Automatically set column widths to widest data in each column
192 void MyTable::autowidth(int pad) {
193 fl_font(FL_COURIER, 16);
194 // Initialize all column widths to lowest value
195 for ( int c=0; c<cols(); c++ ) col_width(c, pad);
196 for ( int r=0; r<(int)_rowdata.size(); r++ ) {
197 int w, h;
198 for ( int c=0; c<(int)_rowdata[r].cols.size(); c++ ) {
199 fl_measure(_rowdata[r].cols[c], w, h, 0); // get pixel width of text
200 if ( (w + pad) > col_width(c)) col_width(c, w + pad);
203 table_resized();
204 redraw();
207 // Resize parent window to size of table
208 void MyTable::resize_window() {
209 // Determine exact outer width of table with all columns visible
210 int width = 4; // width of table borders
211 for ( int t=0; t<cols(); t++ ) width += col_width(t); // total width of all columns
212 width += MARGIN*2;
213 if ( width < 200 || width > Fl::w() ) return;
214 window()->resize(window()->x(), window()->y(), width, window()->h()); // resize window to fit
217 // Load table with output of 'cmd'
218 void MyTable::load_command(const char *cmd) {
219 char s[512];
220 FILE *fp = popen(cmd, "r");
221 cols(0);
222 for ( int r=0; fgets(s, sizeof(s)-1, fp); r++ ) {
223 // Add a new row
224 Row newrow; _rowdata.push_back(newrow);
225 std::vector<char*> &rc = _rowdata[r].cols;
226 // Break line into separate word 'columns'
227 char *ss;
228 const char *delim = " \t\n";
229 for(int t=0; (t==0)?(ss=strtok(s,delim)):(ss=strtok(NULL,delim)); t++) {
230 rc.push_back(strdup(ss));
232 // Keep track of max # columns
233 if ( (int)rc.size() > cols() ) {
234 cols((int)rc.size());
237 // How many rows we loaded
238 rows((int)_rowdata.size());
239 // Auto-calculate widths, with 20 pixel padding
240 autowidth(20);
243 // Callback whenever someone clicks on different parts of the table
244 void MyTable::event_callback(Fl_Widget*, void *data) {
245 MyTable *o = (MyTable*)data;
246 o->event_callback2();
249 void MyTable::event_callback2() {
250 //int ROW = callback_row(); // unused
251 int COL = callback_col();
252 TableContext context = callback_context();
253 switch ( context ) {
254 case CONTEXT_COL_HEADER: { // someone clicked on column header
255 if ( Fl::event() == FL_RELEASE && Fl::event_button() == 1 ) {
256 if ( _sort_lastcol == COL ) { // Click same column? Toggle sort
257 _sort_reverse ^= 1;
258 } else { // Click diff column? Up sort
259 _sort_reverse = 0;
261 sort_column(COL, _sort_reverse);
262 _sort_lastcol = COL;
264 break;
266 default:
267 return;
271 int main() {
272 Fl_Double_Window win(900,500,"Table Sorting");
273 MyTable table(MARGIN, MARGIN, win.w()-MARGIN*2, win.h()-MARGIN*2);
274 table.selection_color(FL_YELLOW);
275 table.col_header(1);
276 table.col_resize(1);
277 table.when(FL_WHEN_RELEASE); // handle table events on release
278 table.load_command(DIRCMD); // load table with a directory listing
279 table.row_height_all(18); // height of all rows
280 table.tooltip("Click on column headings to toggle column sorting");
281 table.color(FL_WHITE);
282 win.end();
283 win.resizable(table);
284 table.resize_window();
285 win.show();
286 return(Fl::run());
290 // End of "$Id: table-sort.cxx 8193 2011-01-05 17:58:16Z greg.ercolano $".