| // |
| // "$Id: Fl_Table.H 8301 2011-01-22 22:40:11Z AlbrechtS $" |
| // |
| // Fl_Table -- A table widget |
| // |
| // Copyright 2002 by Greg Ercolano. |
| // Copyright (c) 2004 O'ksi'D |
| // |
| // This library is free software; you can redistribute it and/or |
| // modify it under the terms of the GNU Library General Public |
| // License as published by the Free Software Foundation; either |
| // version 2 of the License, or (at your option) any later version. |
| // |
| // This library is distributed in the hope that it will be useful, |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| // Library General Public License for more details. |
| // |
| // You should have received a copy of the GNU Library General Public |
| // License along with this library; if not, write to the Free Software |
| // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
| // USA. |
| // |
| // Please report all bugs and problems to "erco at seriss dot com". |
| // |
| // TODO: |
| // o Auto scroll during dragged selection |
| // o Keyboard navigation (up/down/left/right arrow) |
| // |
| |
| #ifndef _FL_TABLE_H |
| #define _FL_TABLE_H |
| |
| #include <sys/types.h> |
| #include <string.h> // memcpy |
| #ifdef WIN32 |
| #include <malloc.h> // WINDOWS: malloc/realloc |
| #else /*WIN32*/ |
| #include <stdlib.h> // UNIX: malloc/realloc |
| #endif /*WIN32*/ |
| |
| #include <FL/Fl.H> |
| #include <FL/Fl_Group.H> |
| #include <FL/Fl_Scroll.H> |
| #include <FL/Fl_Box.H> |
| #include <FL/Fl_Scrollbar.H> |
| |
| /** |
| A table of widgets or other content. |
| |
| This is the base class for table widgets. |
| |
| To be useful it must be subclassed and several virtual functions defined. |
| Normally applications use widgets derived from this widget, and do not use this |
| widget directly; this widget is usually too low level to be used directly by |
| applications. |
| |
| This widget does \em not handle the data in the table. The draw_cell() |
| method must be overridden by a subclass to manage drawing the contents of |
| the cells. |
| |
| This widget can be used in several ways: |
| |
| - As a custom widget; see examples/table-simple.cxx and test/table.cxx. |
| Very optimal for even extremely large tables. |
| - As a table made up of a single FLTK widget instanced all over the table, |
| simulating a numeric spreadsheet. See examples/table-spreadsheet.cxx and |
| examples/table-spreadsheet-with-keyboard-nav.cxx. Optimal for large tables. |
| - As a regular container of FLTK widgets, one widget per cell. |
| See examples/table-as-container.cxx. \em Not recommended for large tables. |
| |
| \image html table-simple.png |
| \image latex table-simple.png "table-simple example" width=6cm |
| |
| \image html table-as-container.png |
| \image latex table-as-container.png "table-as-container example" width=6cm |
| |
| When acting as part of a custom widget, events on the cells and/or headings |
| generate callbacks when they are clicked by the user. You control when events |
| are generated based on the setting for Fl_Table::when(). |
| |
| When acting as a container for FLTK widgets, the FLTK widgets maintain |
| themselves. Although the draw_cell() method must be overridden, its contents |
| can be very simple. See the draw_cell() code in examples/table-simple.cxx. |
| |
| The following variables are available to classes deriving from Fl_Table: |
| |
| \image html table-dimensions.png |
| \image latex table-dimensions.png "Fl_Table Dimensions" width=6cm |
| |
| <table border=0> |
| <tr><td>x()/y()/w()/h()</td> |
| <td>Fl_Table widget's outer dimension. The outer edge of the border of the |
| Fl_Table. (Red in the diagram above)</td></tr> |
| |
| <tr><td>wix/wiy/wiw/wih</td> |
| <td>Fl_Table widget's inner dimension. The inner edge of the border of the |
| Fl_Table. eg. if the Fl_Table's box() is FL_NO_BOX, these values are the same |
| as x()/y()/w()/h(). (Yellow in the diagram above)</td></tr> |
| |
| <tr><td>tox/toy/tow/toh</td> |
| <td>The table's outer dimension. The outer edge of the border around the cells, |
| but inside the row/col headings and scrollbars. (Green in the diagram above) |
| </td></tr> |
| |
| <tr><td>tix/tiy/tiw/tih</td> |
| <td>The table's inner dimension. The inner edge of the border around the cells, |
| but inside the row/col headings and scrollbars. AKA the table's clip region. |
| eg. if the table_box() is FL_NO_BOX, these values are the same as |
| tox/toyy/tow/toh. (Blue in the diagram above) |
| </td></tr></table> |
| |
| CORE DEVELOPERS |
| |
| - Greg Ercolano : 12/16/2002 - initial implementation 12/16/02. Fl_Table, Fl_Table_Row, docs. |
| - Jean-Marc Lienher : 02/22/2004 - added keyboard nav + mouse selection, and ported Fl_Table into fltk-utf8-1.1.4 |
| |
| OTHER CONTRIBUTORS |
| |
| - Inspired by the Feb 2000 version of FLVW's Flvw_Table widget. Mucho thanks to those folks. |
| - Mister Satan : 04/07/2003 - MinGW porting mods, and singleinput.cxx; a cool Fl_Input oriented spreadsheet example |
| - Marek Paliwoda : 01/08/2003 - Porting mods for Borland |
| - Ori Berger : 03/16/2006 - Optimizations for >500k rows/cols |
| |
| LICENSE |
| |
| Greg added the following license to the original distribution of Fl_Table. He |
| kindly gave his permission to integrate Fl_Table and Fl_Table_Row into FLTK, |
| allowing FLTK license to apply while his widgets are part of the library. |
| |
| If used on its own, this is the license that applies: |
| |
| \verbatim |
| Fl_Table License |
| December 16, 2002 |
| |
| The Fl_Table library and included programs are provided under the terms |
| of the GNU Library General Public License (LGPL) with the following |
| exceptions: |
| |
| 1. Modifications to the Fl_Table configure script, config |
| header file, and makefiles by themselves to support |
| a specific platform do not constitute a modified or |
| derivative work. |
| |
| The authors do request that such modifications be |
| contributed to the Fl_Table project - send all |
| contributions to "erco at seriss dot com". |
| |
| 2. Widgets that are subclassed from Fl_Table widgets do not |
| constitute a derivative work. |
| |
| 3. Static linking of applications and widgets to the |
| Fl_Table library does not constitute a derivative work |
| and does not require the author to provide source |
| code for the application or widget, use the shared |
| Fl_Table libraries, or link their applications or |
| widgets against a user-supplied version of Fl_Table. |
| |
| If you link the application or widget to a modified |
| version of Fl_Table, then the changes to Fl_Table must be |
| provided under the terms of the LGPL in sections |
| 1, 2, and 4. |
| |
| 4. You do not have to provide a copy of the Fl_Table license |
| with programs that are linked to the Fl_Table library, nor |
| do you have to identify the Fl_Table license in your |
| program or documentation as required by section 6 |
| of the LGPL. |
| |
| However, programs must still identify their use of Fl_Table. |
| The following example statement can be included in user |
| documentation to satisfy this requirement: |
| |
| [program/widget] is based in part on the work of |
| the Fl_Table project http://seriss.com/people/erco/fltk/Fl_Table/ |
| \endverbatim |
| |
| |
| */ |
| class FL_EXPORT Fl_Table : public Fl_Group { |
| public: |
| /** |
| The context bit flags for Fl_Table related callbacks (eg. draw_cell(), callback(), etc) |
| */ |
| enum TableContext { |
| CONTEXT_NONE = 0, ///< no known context |
| CONTEXT_STARTPAGE = 0x01, ///< before a page is redrawn |
| CONTEXT_ENDPAGE = 0x02, ///< after a page is redrawn |
| CONTEXT_ROW_HEADER = 0x04, ///< in the row header |
| CONTEXT_COL_HEADER = 0x08, ///< in the col header |
| CONTEXT_CELL = 0x10, ///< in one of the cells |
| CONTEXT_TABLE = 0x20, ///< in a dead zone of table |
| CONTEXT_RC_RESIZE = 0x40 ///< column or row being resized |
| }; |
| |
| private: |
| int _rows, _cols; // total rows/cols |
| int _row_header_w; // width of row header |
| int _col_header_h; // height of column header |
| int _row_position; // last row_position set (not necessarily == toprow!) |
| int _col_position; // last col_position set (not necessarily == leftcol!) |
| |
| char _row_header; // row header enabled? |
| char _col_header; // col header enabled? |
| char _row_resize; // row resizing enabled? |
| char _col_resize; // col resizing enabled? |
| int _row_resize_min; // row minimum resizing height (default=1) |
| int _col_resize_min; // col minimum resizing width (default=1) |
| |
| // OPTIMIZATION: partial row/column redraw variables |
| int _redraw_toprow; |
| int _redraw_botrow; |
| int _redraw_leftcol; |
| int _redraw_rightcol; |
| Fl_Color _row_header_color; |
| Fl_Color _col_header_color; |
| |
| int _auto_drag; |
| int _selecting; |
| |
| // An STL-ish vector without templates |
| class FL_EXPORT IntVector { |
| int *arr; |
| unsigned int _size; |
| void init() { |
| arr = NULL; |
| _size = 0; |
| } |
| void copy(int *newarr, unsigned int newsize) { |
| size(newsize); |
| memcpy(arr, newarr, newsize * sizeof(int)); |
| } |
| public: |
| IntVector() { init(); } // CTOR |
| ~IntVector() { if ( arr ) free(arr); arr = NULL; } // DTOR |
| IntVector(IntVector&o) { init(); copy(o.arr, o._size); } // COPY CTOR |
| IntVector& operator=(IntVector&o) { // ASSIGN |
| init(); |
| copy(o.arr, o._size); |
| return(*this); |
| } |
| int operator[](int x) const { return(arr[x]); } |
| int& operator[](int x) { return(arr[x]); } |
| unsigned int size() { return(_size); } |
| void size(unsigned int count) { |
| if ( count != _size ) { |
| arr = (int*)realloc(arr, count * sizeof(int)); |
| _size = count; |
| } |
| } |
| int pop_back() { int tmp = arr[_size-1]; _size--; return(tmp); } |
| void push_back(int val) { unsigned int x = _size; size(_size+1); arr[x] = val; } |
| int back() { return(arr[_size-1]); } |
| }; |
| |
| IntVector _colwidths; // column widths in pixels |
| IntVector _rowheights; // row heights in pixels |
| |
| Fl_Cursor _last_cursor; // last mouse cursor before changed to 'resize' cursor |
| |
| // EVENT CALLBACK DATA |
| TableContext _callback_context; // event context |
| int _callback_row, _callback_col; // event row/col |
| |
| // handle() state variables. |
| // Put here instead of local statics in handle(), so more |
| // than one Fl_Table can exist without crosstalk between them. |
| // |
| int _resizing_col; // column being dragged |
| int _resizing_row; // row being dragged |
| int _dragging_x; // starting x position for horiz drag |
| int _dragging_y; // starting y position for vert drag |
| int _last_row; // last row we FL_PUSH'ed |
| |
| // Redraw single cell |
| void _redraw_cell(TableContext context, int R, int C); |
| |
| void _start_auto_drag(); |
| void _stop_auto_drag(); |
| void _auto_drag_cb(); |
| static void _auto_drag_cb2(void *d); |
| |
| protected: |
| enum ResizeFlag { |
| RESIZE_NONE = 0, |
| RESIZE_COL_LEFT = 1, |
| RESIZE_COL_RIGHT = 2, |
| RESIZE_ROW_ABOVE = 3, |
| RESIZE_ROW_BELOW = 4 |
| }; |
| |
| int table_w, table_h; // table's virtual size (in pixels) |
| int toprow, botrow, leftcol, rightcol; // four corners of viewable table |
| |
| // selection |
| int current_row, current_col; |
| int select_row, select_col; |
| |
| // OPTIMIZATION: Precomputed scroll positions for the toprow/leftcol |
| int toprow_scrollpos; |
| int leftcol_scrollpos; |
| |
| // Dimensions |
| int tix, tiy, tiw, tih; // data table inner dimension xywh |
| int tox, toy, tow, toh; // data table outer dimension xywh |
| int wix, wiy, wiw, wih; // widget inner dimension xywh |
| |
| Fl_Scroll *table; // container for child fltk widgets (if any) |
| Fl_Scrollbar *vscrollbar; // vertical scrollbar |
| Fl_Scrollbar *hscrollbar; // horizontal scrollbar |
| |
| // Fltk |
| int handle(int e); // fltk handle() override |
| |
| // Class maintenance |
| void recalc_dimensions(); |
| void table_resized(); // table resized; recalc |
| void table_scrolled(); // table scrolled; recalc |
| void get_bounds(TableContext context, // return x/y/w/h bounds for context |
| int &X, int &Y, int &W, int &H); |
| void change_cursor(Fl_Cursor newcursor); // change mouse cursor to some other shape |
| TableContext cursor2rowcol(int &R, int &C, ResizeFlag &resizeflag); |
| // find r/c given current x/y event |
| int find_cell(TableContext context, // find cell's x/y/w/h given r/c |
| int R, int C, int &X, int &Y, int &W, int &H); |
| int row_col_clamp(TableContext context, int &R, int &C); |
| // clamp r/c to known universe |
| |
| /** |
| Subclass should override this method to handle drawing the cells. |
| |
| This method will be called whenever the table is redrawn, once per cell. |
| |
| Only cells that are completely (or partially) visible will be told to draw. |
| |
| \p context will be one of the following: |
| |
| <table border=1> |
| <tr> |
| <td>\p Fl_Table::CONTEXT_STARTPAGE</td> |
| <td>When table, or parts of the table, are about to be redrawn.<br> |
| Use to initialize static data, such as font selections.<p> |
| R/C will be zero,<br> |
| X/Y/W/H will be the dimensions of the table's entire data area.<br> |
| (Useful for locking a database before accessing; see |
| also visible_cells())</td> |
| </tr><tr> |
| <td>\p Fl_Table::CONTEXT_ENDPAGE</td> |
| <td>When table has completed being redrawn.<br> |
| R/C will be zero, X/Y/W/H dimensions of table's data area.<br> |
| (Useful for unlocking a database after accessing)</td> |
| </tr><tr> |
| <td>\p Fl_Table::CONTEXT_ROW_HEADER</td> |
| <td>Whenever a row header cell needs to be drawn.<br> |
| R will be the row number of the header being redrawn,<br> |
| C will be zero,<br> |
| X/Y/W/H will be the fltk drawing area of the row header in the window </td> |
| </tr><tr> |
| <td>\p Fl_Table::CONTEXT_COL_HEADER</td> |
| <td>Whenever a column header cell needs to be drawn.<br> |
| R will be zero, <br> |
| C will be the column number of the header being redrawn,<br> |
| X/Y/W/H will be the fltk drawing area of the column header in the window </td> |
| </tr><tr> |
| <td>\p Fl_Table::CONTEXT_CELL</td> |
| <td>Whenever a data cell in the table needs to be drawn.<br> |
| R/C will be the row/column of the cell to be drawn,<br> |
| X/Y/W/H will be the fltk drawing area of the cell in the window </td> |
| </tr><tr> |
| <td>\p Fl_Table::CONTEXT_RC_RESIZE</td> |
| <td>Whenever table or row/column is resized or scrolled, |
| either interactively or via col_width() or row_height().<br> |
| R/C/X/Y/W/H will all be zero. |
| <p> |
| Useful for fltk containers that need to resize or move |
| the child fltk widgets.</td> |
| </tr> |
| </table> |
| |
| \p row and \p col will be set to the row and column number |
| of the cell being drawn. In the case of row headers, \p col will be \a 0. |
| In the case of column headers, \p row will be \a 0. |
| |
| <tt>x/y/w/h</tt> will be the position and dimensions of where the cell |
| should be drawn. |
| |
| In the case of custom widgets, a minimal draw_cell() override might |
| look like the following. With custom widgets it is up to the caller to handle |
| drawing everything within the dimensions of the cell, including handling the |
| selection color. Note all clipping must be handled as well; this allows drawing |
| outside the dimensions of the cell if so desired for 'custom effects'. |
| |
| \code |
| // This is called whenever Fl_Table wants you to draw a cell |
| void MyTable::draw_cell(TableContext context, int R=0, int C=0, int X=0, int Y=0, int W=0, int H=0) { |
| static char s[40]; |
| sprintf(s, "%d/%d", R, C); // text for each cell |
| switch ( context ) { |
| case CONTEXT_STARTPAGE: // Fl_Table telling us its starting to draw page |
| fl_font(FL_HELVETICA, 16); |
| return; |
| |
| case CONTEXT_ROW_HEADER: // Fl_Table telling us it's draw row/col headers |
| case CONTEXT_COL_HEADER: |
| fl_push_clip(X, Y, W, H); |
| { |
| fl_draw_box(FL_THIN_UP_BOX, X, Y, W, H, color()); |
| fl_color(FL_BLACK); |
| fl_draw(s, X, Y, W, H, FL_ALIGN_CENTER); |
| } |
| fl_pop_clip(); |
| return; |
| |
| case CONTEXT_CELL: // Fl_Table telling us to draw cells |
| fl_push_clip(X, Y, W, H); |
| { |
| // BG COLOR |
| fl_color( row_selected(R) ? selection_color() : FL_WHITE); |
| fl_rectf(X, Y, W, H); |
| |
| // TEXT |
| fl_color(FL_BLACK); |
| fl_draw(s, X, Y, W, H, FL_ALIGN_CENTER); |
| |
| // BORDER |
| fl_color(FL_LIGHT2); |
| fl_rect(X, Y, W, H); |
| } |
| fl_pop_clip(); |
| return; |
| |
| default: |
| return; |
| } |
| //NOTREACHED |
| } |
| \endcode |
| */ |
| virtual void draw_cell(TableContext context, int R=0, int C=0, |
| int X=0, int Y=0, int W=0, int H=0) |
| { } // overridden by deriving class |
| |
| long row_scroll_position(int row); // find scroll position of row (in pixels) |
| long col_scroll_position(int col); // find scroll position of col (in pixels) |
| |
| int is_fltk_container() { // does table contain fltk widgets? |
| return( Fl_Group::children() > 3 ); // (ie. more than box and 2 scrollbars?) |
| } |
| |
| static void scroll_cb(Fl_Widget*,void*); // h/v scrollbar callback |
| |
| void damage_zone(int r1, int c1, int r2, int c2, int r3 = 0, int c3 = 0); |
| |
| void redraw_range(int toprow, int botrow, int leftcol, int rightcol) { |
| if ( _redraw_toprow == -1 ) { |
| // Initialize redraw range |
| _redraw_toprow = toprow; |
| _redraw_botrow = botrow; |
| _redraw_leftcol = leftcol; |
| _redraw_rightcol = rightcol; |
| } else { |
| // Extend redraw range |
| if ( toprow < _redraw_toprow ) _redraw_toprow = toprow; |
| if ( botrow > _redraw_botrow ) _redraw_botrow = botrow; |
| if ( leftcol < _redraw_leftcol ) _redraw_leftcol = leftcol; |
| if ( rightcol > _redraw_rightcol ) _redraw_rightcol = rightcol; |
| } |
| |
| // Indicate partial redraw needed of some cells |
| damage(FL_DAMAGE_CHILD); |
| } |
| |
| public: |
| /** |
| The constructor for the Fl_Table. |
| This creates an empty table with no rows or columns, |
| with headers and row/column resize behavior disabled. |
| */ |
| Fl_Table(int X, int Y, int W, int H, const char *l=0); |
| |
| /** |
| The destructor for the Fl_Table. |
| Destroys the table and its associated widgets. |
| */ |
| ~Fl_Table(); |
| |
| /** |
| Clears the table to zero rows, zero columns. |
| Same as rows(0); cols(0); |
| \see rows(int), cols(int) |
| */ |
| virtual void clear() { rows(0); cols(0); } |
| |
| // \todo: add topline(), middleline(), bottomline() |
| |
| /** |
| Sets the kind of box drawn around the data table, |
| the default being FL_NO_BOX. Changing this value will cause the table |
| to redraw. |
| */ |
| inline void table_box(Fl_Boxtype val) { |
| table->box(val); |
| table_resized(); |
| } |
| |
| /** |
| Returns the current box type used for the data table. |
| */ |
| inline Fl_Boxtype table_box( void ) { |
| return(table->box()); |
| } |
| |
| /** |
| Sets the number of rows in the table, and the table is redrawn. |
| */ |
| virtual void rows(int val); // set/get number of rows |
| |
| /** |
| Returns the number of rows in the table. |
| */ |
| inline int rows() { |
| return(_rows); |
| } |
| |
| /** |
| Set the number of columns in the table and redraw. |
| */ |
| virtual void cols(int val); // set/get number of columns |
| |
| /** |
| Get the number of columns in the table. |
| */ |
| inline int cols() { |
| return(_cols); |
| } |
| |
| /** |
| Returns the range of row and column numbers for all visible |
| and partially visible cells in the table. |
| |
| These values can be used e.g. by your draw_cell() routine during |
| CONTEXT_STARTPAGE to figure out what cells are about to be redrawn |
| for the purposes of locking the data from a database before it's drawn. |
| |
| \code |
| leftcol rightcol |
| : : |
| toprow .. .-------------------. |
| | | |
| | V I S I B L E | |
| | | |
| | T A B L E | |
| | | |
| botrow .. '-------------------` |
| \endcode |
| |
| e.g. in a table where the visible rows are 5-20, and the |
| visible columns are 100-120, then those variables would be: |
| |
| - toprow = 5 |
| - botrow = 20 |
| - leftcol = 100 |
| - rightcol = 120 |
| */ |
| inline void visible_cells(int& r1, int& r2, int& c1, int& c2) { |
| r1 = toprow; |
| r2 = botrow; |
| c1 = leftcol; |
| c2 = rightcol; |
| } |
| |
| /** |
| Returns 1 if someone is interactively resizing a row or column. |
| You can currently call this only from within your callback(). |
| */ |
| int is_interactive_resize() { |
| return(_resizing_row != -1 || _resizing_col != -1); |
| } |
| |
| /** |
| Returns the current value of this flag. |
| */ |
| inline int row_resize() { |
| return(_row_resize); |
| } |
| |
| /** |
| Allows/disallows row resizing by the user. |
| 1=allow interactive resizing, 0=disallow interactive resizing. |
| Since interactive resizing is done via the row headers, |
| row_header() must also be enabled to allow resizing. |
| */ |
| void row_resize(int flag) { // enable row resizing |
| _row_resize = flag; |
| } |
| |
| /** |
| Returns the current value of this flag. |
| */ |
| inline int col_resize() { |
| return(_col_resize); |
| } |
| /** |
| Allows/disallows column resizing by the user. |
| 1=allow interactive resizing, 0=disallow interactive resizing. |
| Since interactive resizing is done via the column headers, |
| \p col_header() must also be enabled to allow resizing. |
| */ |
| void col_resize(int flag) { // enable col resizing |
| _col_resize = flag; |
| } |
| |
| /** |
| Sets the current column minimum resize value. |
| This is used to prevent the user from interactively resizing |
| any column to be smaller than 'pixels'. Must be a value >=1. |
| */ |
| inline int col_resize_min() { // column minimum resizing width |
| return(_col_resize_min); |
| } |
| |
| /** |
| Returns the current column minimum resize value. |
| */ |
| void col_resize_min(int val) { |
| _col_resize_min = ( val < 1 ) ? 1 : val; |
| } |
| |
| /** |
| Returns the current row minimum resize value. |
| */ |
| inline int row_resize_min() { // column minimum resizing width |
| return(_row_resize_min); |
| } |
| |
| /** |
| Sets the current row minimum resize value. |
| This is used to prevent the user from interactively resizing |
| any row to be smaller than 'pixels'. Must be a value >=1. |
| */ |
| void row_resize_min(int val) { |
| _row_resize_min = ( val < 1 ) ? 1 : val; |
| } |
| |
| /** |
| Returns the value of this flag. |
| */ |
| inline int row_header() { // set/get row header enable flag |
| return(_row_header); |
| } |
| |
| /** |
| Enables/disables showing the row headers. 1=enabled, 0=disabled. |
| If changed, the table is redrawn. |
| */ |
| void row_header(int flag) { |
| _row_header = flag; |
| table_resized(); |
| redraw(); |
| } |
| |
| /** |
| Returns if column headers are enabled or not. |
| */ |
| inline int col_header() { // set/get col header enable flag |
| return(_col_header); |
| } |
| |
| /** |
| Enable or disable column headers. |
| If changed, the table is redrawn. |
| */ |
| void col_header(int flag) { |
| _col_header = flag; |
| table_resized(); |
| redraw(); |
| } |
| |
| /** |
| Sets the height in pixels for column headers and redraws the table. |
| */ |
| inline void col_header_height(int height) { // set/get col header height |
| _col_header_h = height; |
| table_resized(); |
| redraw(); |
| } |
| |
| /** |
| Gets the column header height. |
| */ |
| inline int col_header_height() { |
| return(_col_header_h); |
| } |
| |
| /** |
| Sets the row header width to n and causes the screen to redraw. |
| */ |
| inline void row_header_width(int width) { // set/get row header width |
| _row_header_w = width; |
| table_resized(); |
| redraw(); |
| } |
| |
| /** |
| Returns the current row header width (in pixels). |
| */ |
| inline int row_header_width() { |
| return(_row_header_w); |
| } |
| |
| /** |
| Sets the row header color and causes the screen to redraw. |
| */ |
| inline void row_header_color(Fl_Color val) { // set/get row header color |
| _row_header_color = val; |
| redraw(); |
| } |
| |
| /** |
| Returns the current row header color. |
| */ |
| inline Fl_Color row_header_color() { |
| return(_row_header_color); |
| } |
| |
| /** |
| Sets the color for column headers and redraws the table. |
| */ |
| inline void col_header_color(Fl_Color val) { // set/get col header color |
| _col_header_color = val; |
| redraw(); |
| } |
| |
| /** |
| Gets the color for column headers. |
| */ |
| inline Fl_Color col_header_color() { |
| return(_col_header_color); |
| } |
| |
| /** |
| Sets the height of the specified row in pixels, |
| and the table is redrawn. |
| callback() will be invoked with CONTEXT_RC_RESIZE |
| if the row's height was actually changed, and when() is FL_WHEN_CHANGED. |
| */ |
| void row_height(int row, int height); // set/get row height |
| |
| /** |
| Returns the current height of the specified row as a value in pixels. |
| */ |
| inline int row_height(int row) { |
| return((row<0 || row>=(int)_rowheights.size()) ? 0 : _rowheights[row]); |
| } |
| |
| /** |
| Sets the width of the specified column in pixels, and the table is redrawn. |
| callback() will be invoked with CONTEXT_RC_RESIZE |
| if the column's width was actually changed, and when() is FL_WHEN_CHANGED. |
| */ |
| void col_width(int col, int width); // set/get a column's width |
| |
| /** |
| Returns the current width of the specified column in pixels. |
| */ |
| inline int col_width(int col) { |
| return((col<0 || col>=(int)_colwidths.size()) ? 0 : _colwidths[col]); |
| } |
| |
| /** |
| Convenience method to set the height of all rows to the |
| same value, in pixels. The screen is redrawn. |
| */ |
| void row_height_all(int height) { // set all row/col heights |
| for ( int r=0; r<rows(); r++ ) { |
| row_height(r, height); |
| } |
| } |
| |
| /** |
| Convenience method to set the width of all columns to the |
| same value, in pixels. The screen is redrawn. |
| */ |
| void col_width_all(int width) { |
| for ( int c=0; c<cols(); c++ ) { |
| col_width(c, width); |
| } |
| } |
| |
| /** |
| Sets the row scroll position to 'row', and causes the screen to redraw. |
| */ |
| void row_position(int row); // set/get table's current scroll position |
| |
| /** |
| Sets the column scroll position to column 'col', and causes the screen to redraw. |
| */ |
| void col_position(int col); |
| |
| /** |
| Returns the current row scroll position as a row number. |
| */ |
| int row_position() { // current row position |
| return(_row_position); |
| } |
| |
| /** |
| Returns the current column scroll position as a column number. |
| */ |
| int col_position() { // current col position |
| return(_col_position); |
| } |
| |
| /** |
| Sets which row should be at the top of the table, |
| scrolling as necessary, and the table is redrawn. If the table |
| cannot be scrolled that far, it is scrolled as far as possible. |
| */ |
| inline void top_row(int row) { // set/get top row (deprecated) |
| row_position(row); |
| } |
| |
| /** |
| Returns the current top row shown in the table. |
| This row may be partially obscured. |
| */ |
| inline int top_row() { |
| return(row_position()); |
| } |
| int is_selected(int r, int c); // selected cell |
| void get_selection(int &row_top, int &col_left, int &row_bot, int &col_right); |
| void set_selection(int row_top, int col_left, int row_bot, int col_right); |
| int move_cursor(int R, int C); |
| |
| /** |
| Changes the size of the Fl_Table, causing it to redraw. |
| */ |
| void resize(int X, int Y, int W, int H); // fltk resize() override |
| void draw(void); // fltk draw() override |
| |
| // This crashes sortapp() during init. |
| // void box(Fl_Boxtype val) { |
| // Fl_Group::box(val); |
| // if ( table ) { |
| // resize(x(), y(), w(), h()); |
| // } |
| // } |
| // Fl_Boxtype box(void) const { |
| // return(Fl_Group::box()); |
| // } |
| |
| // Child group |
| void init_sizes() { |
| table->init_sizes(); |
| table->redraw(); |
| } |
| void add(Fl_Widget& w) { |
| table->add(w); |
| } |
| void add(Fl_Widget* w) { |
| table->add(w); |
| } |
| void insert(Fl_Widget& w, int n) { |
| table->insert(w,n); |
| } |
| void insert(Fl_Widget& w, Fl_Widget* w2) { |
| table->insert(w,w2); |
| } |
| void remove(Fl_Widget& w) { |
| table->remove(w); |
| } |
| void begin() { |
| table->begin(); |
| } |
| void end() { |
| table->end(); |
| // HACK: Avoid showing Fl_Scroll; seems to erase screen |
| // causing unnecessary flicker, even if its box() is FL_NO_BOX. |
| // |
| if ( table->children() > 2 ) { |
| table->show(); |
| } else { |
| table->hide(); |
| } |
| Fl_Group::current(Fl_Group::parent()); |
| } |
| Fl_Widget * const *array() { |
| return(table->array()); |
| } |
| |
| /** |
| Returns the child widget by an index. |
| |
| When using the Fl_Table as a container for FLTK widgets, this method returns |
| the widget pointer from the internal array of widgets in the container. |
| |
| Typically used in loops, eg: |
| \code |
| for ( int i=0; i<children(); i++ ) { |
| Fl_Widget *w = child(i); |
| [..] |
| } |
| \endcode |
| */ |
| Fl_Widget *child(int n) const { |
| return(table->child(n)); |
| } |
| |
| /** |
| Returns the number of children in the table. |
| |
| When using the Fl_Table as a container for FLTK widgets, this method returns |
| how many child widgets the table has. |
| |
| \see child(int) |
| */ |
| int children() const { |
| return(table->children()-2); // -2: skip Fl_Scroll's h/v scrollbar widgets |
| } |
| int find(const Fl_Widget *w) const { |
| return(table->find(w)); |
| } |
| int find(const Fl_Widget &w) const { |
| return(table->find(w)); |
| } |
| // CALLBACKS |
| |
| /** |
| * Returns the current row the event occurred on. |
| * |
| * This function should only be used from within the user's callback function |
| */ |
| int callback_row() { |
| return(_callback_row); |
| } |
| |
| /** |
| * Returns the current column the event occurred on. |
| * |
| * This function should only be used from within the user's callback function |
| */ |
| int callback_col() { |
| return(_callback_col); |
| } |
| |
| /** |
| * Returns the current 'table context'. |
| * |
| * This function should only be used from within the user's callback function |
| */ |
| TableContext callback_context() { |
| return(_callback_context); |
| } |
| |
| void do_callback(TableContext context, int row, int col) { |
| _callback_context = context; |
| _callback_row = row; |
| _callback_col = col; |
| Fl_Widget::do_callback(); |
| } |
| |
| #if FL_DOXYGEN |
| /** |
| The Fl_Widget::when() function is used to set a group of flags, determining |
| when the widget callback is called: |
| |
| <table border=1> |
| <tr> |
| <td>\p FL_WHEN_CHANGED</td> |
| <td> |
| callback() will be called when rows or columns are resized (interactively or |
| via col_width() or row_height()), passing CONTEXT_RC_RESIZE via |
| callback_context(). |
| </td> |
| </tr><tr> |
| <td>\p FL_WHEN_RELEASE</td> |
| <td> |
| callback() will be called during FL_RELEASE events, such as when someone |
| releases a mouse button somewhere on the table. |
| </td> |
| </tr> |
| </table> |
| |
| The callback() routine is sent a TableContext that indicates the context the |
| event occurred in, such as in a cell, in a header, or elsewhere on the table. |
| When an event occurs in a cell or header, callback_row() and |
| callback_col() can be used to determine the row and column. The callback can |
| also look at the regular fltk event values (ie. Fl::event() and Fl::button()) |
| to determine what kind of event is occurring. |
| */ |
| void when(Fl_When flags); |
| #endif |
| |
| #if FL_DOXYGEN |
| /** |
| Callbacks will be called depending on the setting of Fl_Widget::when(). |
| |
| Callback functions should use the following functions to determine the |
| context/row/column: |
| |
| * Fl_Table::callback_row() returns current row |
| * Fl_Table::callback_col() returns current column |
| * Fl_Table::callback_context() returns current table context |
| |
| callback_row() and callback_col() will be set to the row and column number the |
| event occurred on. If someone clicked on a row header, \p col will be \a 0. |
| If someone clicked on a column header, \p row will be \a 0. |
| |
| callback_context() will return one of the following: |
| |
| <table border=1> |
| <tr><td><tt>Fl_Table::CONTEXT_ROW_HEADER</tt></td> |
| <td>Someone clicked on a row header. Excludes resizing.</td> |
| </tr><tr> |
| <td><tt>Fl_Table::CONTEXT_COL_HEADER</tt></td> |
| <td>Someone clicked on a column header. Excludes resizing.</td> |
| </tr><tr> |
| <td><tt>Fl_Table::CONTEXT_CELL</tt></td> |
| <td> |
| Someone clicked on a cell. |
| |
| To receive callbacks for FL_RELEASE events, you must set |
| when(FL_WHEN_RELEASE). |
| </td> |
| </tr><tr> |
| <td><tt>Fl_Table::CONTEXT_RC_RESIZE</tt></td> |
| <td> |
| Someone is resizing rows/columns either interactively, |
| or via the col_width() or row_height() API. |
| |
| Use is_interactive_resize() |
| to determine interactive resizing. |
| |
| If resizing a column, R=0 and C=column being resized. |
| |
| If resizing a row, C=0 and R=row being resized. |
| |
| NOTE: To receive resize events, you must set when(FL_WHEN_CHANGED). |
| </td> |
| </tr> |
| </table> |
| |
| \code |
| class MyTable : public Fl_Table { |
| [..] |
| private: |
| // Handle events that happen on the table |
| void event_callback2() { |
| int R = callback_row(), // row where event occurred |
| C = callback_col(); // column where event occurred |
| TableContext context = callback_context(); // which part of table |
| fprintf(stderr, "callback: Row=%d Col=%d Context=%d Event=%d\n", |
| R, C, (int)context, (int)Fl::event()); |
| } |
| |
| // Actual static callback |
| static void event_callback(Fl_Widget*, void* data) { |
| MyTable *o = (MyTable*)data; |
| o->event_callback2(); |
| } |
| |
| public: |
| // Constructor |
| MyTable() { |
| [..] |
| table.callback(&event_callback, (void*)this); // setup callback |
| table.when(FL_WHEN_CHANGED|FL_WHEN_RELEASE); // when to call it |
| } |
| }; |
| \endcode |
| */ |
| void callback(Fl_Widget*, void*); |
| #endif |
| }; |
| |
| #endif /*_FL_TABLE_H*/ |
| |
| // |
| // End of "$Id: Fl_Table.H 8301 2011-01-22 22:40:11Z AlbrechtS $". |
| // |