File: table-sort.cxx

package info (click to toggle)
fltk1.3 1.3.11-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 25,224 kB
  • sloc: cpp: 105,017; ansic: 91,383; sh: 6,825; makefile: 1,948; perl: 24; xml: 7
file content (291 lines) | stat: -rw-r--r-- 9,861 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
//
// "$Id$"
//
//	table-sort -- An example application using a sortable Fl_Table
//                
//	Originally the 'sortapp.cxx' example program that came with 
//	erco's Fl_Table widget. Added to FLTK in 2010.
//
//      Example of a non-trivial application that uses Fl_Table 
//      with sortable columns. This example is not trying to be simple,
//      but to demonstrate the complexities of an actual app.
//
// Copyright 2010 Greg Ercolano.
// Copyright 1998-2010 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file.  If this
// file is missing or damaged, see the license at:
//
//     http://www.fltk.org/COPYING.php
//
// Please report all bugs and problems on the following page:
//
//     http://www.fltk.org/str.php
// 

#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/fl_draw.H>
#include <FL/Fl_Table_Row.H>

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <vector>
#include <algorithm>            // STL sort

#define MARGIN 20

#ifdef WIN32
// WINDOWS
#  define DIRCMD          "dir"
static const char *G_header[] = { "Date", "Time", "Size", "Filename", "", "", "", "", "", 0 };
#  ifdef _MSC_VER
#    define popen           _popen
#  endif
#else /*WIN32*/
// UNIX
#  define DIRCMD          "ls -l"
static const char *G_header[] = { "Perms", "#L", "Own", "Group", "Size", "Date", "", "", "Filename", 0 };
#endif /*WIN32*/

// Font face/sizes for header and rows
#define HEADER_FONTFACE FL_HELVETICA_BOLD
#define HEADER_FONTSIZE 16
#define ROW_FONTFACE    FL_HELVETICA
#define ROW_FONTSIZE    16

// A single row of columns
class Row {
public:
    std::vector<char*> cols;
};

// Sort class to handle sorting column using std::sort
class SortColumn {
    int _col, _reverse;
public:
    SortColumn(int col, int reverse) {
        _col = col;
        _reverse = reverse;
    }
    bool operator()(const Row &a, const Row &b) {
        const char *ap = ( _col < (int)a.cols.size() ) ? a.cols[_col] : "",
                   *bp = ( _col < (int)b.cols.size() ) ? b.cols[_col] : "";
        if ( isdigit(*ap) && isdigit(*bp) ) {           // cheezy detection of numeric data
            // Numeric sort
            int av=0; sscanf(ap, "%d", &av);
            int bv=0; sscanf(bp, "%d", &bv);
            return( _reverse ? av < bv : bv < av );
        } else {
            // Alphabetic sort
            return( _reverse ? strcmp(ap, bp) > 0 : strcmp(ap, bp) < 0 );
        }
    }
};

// Derive a custom class from Fl_Table_Row
class MyTable : public Fl_Table_Row {
private:
    std::vector<Row> _rowdata;                                  // data in each row
    int _sort_reverse;
    int _sort_lastcol;

    static void event_callback(Fl_Widget*, void*);
    void event_callback2();                                     // callback for table events

protected:
    void draw_cell(TableContext context, int R=0, int C=0,      // table cell drawing
                   int X=0, int Y=0, int W=0, int H=0);
    void sort_column(int col, int reverse=0);                   // sort table by a column
    void draw_sort_arrow(int X,int Y,int W,int H);

public:
    // Ctor
    MyTable(int x, int y, int w, int h, const char *l=0) : Fl_Table_Row(x,y,w,h,l) {
        _sort_reverse = 0;
        _sort_lastcol = -1;
        end();
        callback(event_callback, (void*)this);
    }
    ~MyTable() { }                              // Dtor
    void load_command(const char *cmd);         // Load the output of a command into table
    void autowidth(int pad);                    // Automatically set column widths to data
    void resize_window();                       // Resize parent window to size of table
};

// Sort a column up or down
void MyTable::sort_column(int col, int reverse) {
    std::sort(_rowdata.begin(), _rowdata.end(), SortColumn(col, reverse));
    redraw();
}

// Draw sort arrow
void MyTable::draw_sort_arrow(int X,int Y,int W,int H) {
    int xlft = X+(W-6)-8;
    int xctr = X+(W-6)-4;
    int xrit = X+(W-6)-0;
    int ytop = Y+(H/2)-4;
    int ybot = Y+(H/2)+4;
    if ( _sort_reverse ) {
        // Engraved down arrow
        fl_color(FL_WHITE);
        fl_line(xrit, ytop, xctr, ybot);
        fl_color(41);                   // dark gray
        fl_line(xlft, ytop, xrit, ytop);
        fl_line(xlft, ytop, xctr, ybot);
    } else {
        // Engraved up arrow
        fl_color(FL_WHITE);
        fl_line(xrit, ybot, xctr, ytop);
        fl_line(xrit, ybot, xlft, ybot);
        fl_color(41);                   // dark gray
        fl_line(xlft, ybot, xctr, ytop);
    }
}

// Handle drawing all cells in table
void MyTable::draw_cell(TableContext context, int R, int C, int X, int Y, int W, int H) {
    const char *s = ""; 
    if ( R < (int)_rowdata.size() && C < (int)_rowdata[R].cols.size() )
        s = _rowdata[R].cols[C]; 
    switch ( context ) {
        case CONTEXT_COL_HEADER:
            fl_push_clip(X,Y,W,H); {
                fl_draw_box(FL_THIN_UP_BOX, X,Y,W,H, FL_BACKGROUND_COLOR);
                if ( C < 9 ) {
		    fl_font(HEADER_FONTFACE, HEADER_FONTSIZE);
                    fl_color(FL_BLACK);
                    fl_draw(G_header[C], X+2,Y,W,H, FL_ALIGN_LEFT, 0, 0);         // +2=pad left 
                    // Draw sort arrow
                    if ( C == _sort_lastcol ) {
                        draw_sort_arrow(X,Y,W,H);
                    }
                }
            }
            fl_pop_clip();
            return; 
        case CONTEXT_CELL: {
            fl_push_clip(X,Y,W,H); {
                // Bg color
                Fl_Color bgcolor = row_selected(R) ? selection_color() : FL_WHITE;
                fl_color(bgcolor); fl_rectf(X,Y,W,H); 
		fl_font(ROW_FONTFACE, ROW_FONTSIZE);
                fl_color(FL_BLACK); fl_draw(s, X+2,Y,W,H, FL_ALIGN_LEFT);     // +2=pad left 
                // Border
                fl_color(FL_LIGHT2); fl_rect(X,Y,W,H);
            }
            fl_pop_clip();
            return;
        }
        default:
            return;
    }
}

// Automatically set column widths to widest data in each column
void MyTable::autowidth(int pad) {
    int w, h;
    // Initialize all column widths to header width
    fl_font(HEADER_FONTFACE, HEADER_FONTSIZE);
    for ( int c=0; G_header[c]; c++ ) {
	w=0; fl_measure(G_header[c], w, h, 0);                   // pixel width of header text
        col_width(c, w+pad);
    }
    fl_font(ROW_FONTFACE, ROW_FONTSIZE);
    for ( int r=0; r<(int)_rowdata.size(); r++ ) {
        for ( int c=0; c<(int)_rowdata[r].cols.size(); c++ ) {
            w=0; fl_measure(_rowdata[r].cols[c], w, h, 0);       // pixel width of row text
            if ( (w + pad) > col_width(c)) col_width(c, w + pad);
        }
    }
    table_resized();
    redraw();
}

// Resize parent window to size of table
void MyTable::resize_window() {
    // Determine exact outer width of table with all columns visible
    int width = 4;                                          // width of table borders
    for ( int t=0; t<cols(); t++ ) width += col_width(t);   // total width of all columns
    width += MARGIN*2;
    if ( width < 200 || width > Fl::w() ) return;
    window()->resize(window()->x(), window()->y(), width, window()->h());  // resize window to fit
}

// Load table with output of 'cmd'
void MyTable::load_command(const char *cmd) {
    char s[512];
    FILE *fp = popen(cmd, "r");
    cols(0);
    for ( int r=0; fgets(s, sizeof(s)-1, fp); r++ ) {
        // Add a new row
        Row newrow; _rowdata.push_back(newrow);
        std::vector<char*> &rc = _rowdata[r].cols;
        // Break line into separate word 'columns'
        char *ss;
        const char *delim = " \t\n";
        for(int t=0; (t==0)?(ss=strtok(s,delim)):(ss=strtok(NULL,delim)); t++) {
            rc.push_back(strdup(ss));
        }
        // Keep track of max # columns
        if ( (int)rc.size() > cols() ) {
            cols((int)rc.size());
        }
    } 
    // How many rows we loaded
    rows((int)_rowdata.size()); 
    // Auto-calculate widths, with 20 pixel padding
    autowidth(20);
}

// Callback whenever someone clicks on different parts of the table
void MyTable::event_callback(Fl_Widget*, void *data) {
    MyTable *o = (MyTable*)data;
    o->event_callback2();
}

void MyTable::event_callback2() {
    //int ROW = callback_row();                 // unused
    int COL = callback_col();
    TableContext context = callback_context();
    switch ( context ) {
        case CONTEXT_COL_HEADER: {              // someone clicked on column header
            if ( Fl::event() == FL_RELEASE && Fl::event_button() == 1 ) {
                if ( _sort_lastcol == COL ) {   // Click same column? Toggle sort
                    _sort_reverse ^= 1;
                } else {                        // Click diff column? Up sort 
                    _sort_reverse = 0;
                }
                sort_column(COL, _sort_reverse);
                _sort_lastcol = COL;
            }
            break;
        }
        default:
            return;
    }
}

int main() {
    Fl_Double_Window win(900,500,"Table Sorting");
        MyTable table(MARGIN, MARGIN, win.w()-MARGIN*2, win.h()-MARGIN*2);
        table.selection_color(FL_YELLOW);
        table.col_header(1);
        table.col_resize(1);
        table.when(FL_WHEN_RELEASE);            // handle table events on release
        table.load_command(DIRCMD);             // load table with a directory listing
        table.row_height_all(18);               // height of all rows
	table.tooltip("Click on column headings to toggle column sorting");
	table.color(FL_WHITE);
    win.end();
    win.resizable(table);
    table.resize_window();
    win.show();
    return(Fl::run());
}

//
// End of "$Id$".
//