diff options
| author | Matthias Melcher <fltk@matthiasm.com> | 2009-11-14 13:05:37 +0000 |
|---|---|---|
| committer | Matthias Melcher <fltk@matthiasm.com> | 2009-11-14 13:05:37 +0000 |
| commit | fefa82e0b1e98d784455148940d84b454a128aad (patch) | |
| tree | 9d888cb9bc402708e0ad035a1c55d4b7703376a4 | |
| parent | 1238d1576b8e2bc15c097b23ebe0fdc4d3cdb527 (diff) | |
Adding Gerg's Fl_Table widget to FLTK 1.3 with kind permission. Documentation not yet added Support for both Visual Studio IDE's not yet added.
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@6929 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
| -rw-r--r-- | FL/Fl_Table.H | 451 | ||||
| -rw-r--r-- | FL/Fl_Table_Row.H | 150 | ||||
| -rw-r--r-- | ide/Xcode3.1/FLTK.xcodeproj/project.pbxproj | 170 | ||||
| -rw-r--r-- | ide/Xcode3.1/plists/table-Info.plist | 20 | ||||
| -rw-r--r-- | src/Fl_Table.cxx | 1218 | ||||
| -rw-r--r-- | src/Fl_Table_Row.cxx | 317 | ||||
| -rw-r--r-- | src/Fl_Tabs.o | bin | 0 -> 51336 bytes | |||
| -rw-r--r-- | src/Makefile | 2 | ||||
| -rw-r--r-- | test/Makefile | 2 | ||||
| -rw-r--r-- | test/demo.menu | 4 | ||||
| -rw-r--r-- | test/table.cxx | 493 |
11 files changed, 2822 insertions, 5 deletions
diff --git a/FL/Fl_Table.H b/FL/Fl_Table.H new file mode 100644 index 000000000..3dfc18400 --- /dev/null +++ b/FL/Fl_Table.H @@ -0,0 +1,451 @@ +// +// 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> + +class Fl_Table : public Fl_Group { +public: + enum TableContext { + CONTEXT_NONE = 0, + 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 the 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 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 + + // Called to draw cells + 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: + Fl_Table(int X, int Y, int W, int H, const char *l=0); + ~Fl_Table(); + + virtual void clear() { rows(0); cols(0); } + + // topline() + // middleline() + // bottomline() + + inline void table_box(Fl_Boxtype val) { + table->box(val); + table_resized(); + } + inline Fl_Boxtype table_box( void ) { + return(table->box()); + } + virtual void rows(int val); // set/get number of rows + inline int rows() { + return(_rows); + } + virtual void cols(int val); // set/get number of columns + inline int cols() { + return(_cols); + } + inline void visible_cells(int& r1, int& r2, int& c1, int& c2) { + r1 = toprow; + r2 = botrow; + c1 = leftcol; + c2 = rightcol; + } + // Interactive resizing happening? + int is_interactive_resize() { + return(_resizing_row != -1 || _resizing_col != -1); + } + inline int row_resize() { + return(_row_resize); + } + void row_resize(int flag) { // enable row resizing + _row_resize = flag; + } + inline int col_resize() { + return(_col_resize); + } + void col_resize(int flag) { // enable col resizing + _col_resize = flag; + } + inline int col_resize_min() { // column minimum resizing width + return(_col_resize_min); + } + void col_resize_min(int val) { + _col_resize_min = ( val < 1 ) ? 1 : val; + } + inline int row_resize_min() { // column minimum resizing width + return(_row_resize_min); + } + void row_resize_min(int val) { + _row_resize_min = ( val < 1 ) ? 1 : val; + } + inline int row_header() { // set/get row header enable flag + return(_row_header); + } + void row_header(int flag) { + _row_header = flag; + table_resized(); + redraw(); + } + inline int col_header() { // set/get col header enable flag + return(_col_header); + } + void col_header(int flag) { + _col_header = flag; + table_resized(); + redraw(); + } + inline void col_header_height(int height) { // set/get col header height + _col_header_h = height; + table_resized(); + redraw(); + } + inline int col_header_height() { + return(_col_header_h); + } + inline void row_header_width(int width) { // set/get row header width + _row_header_w = width; + table_resized(); + redraw(); + } + inline int row_header_width() { + return(_row_header_w); + } + inline void row_header_color(Fl_Color val) { // set/get row header color + _row_header_color = val; + redraw(); + } + inline Fl_Color row_header_color() { + return(_row_header_color); + } + inline void col_header_color(Fl_Color val) { // set/get col header color + _col_header_color = val; + redraw(); + } + inline Fl_Color col_header_color() { + return(_col_header_color); + } + void row_height(int row, int height); // set/get row height + inline int row_height(int row) { + return((row<0 || row>=(int)_rowheights.size()) ? 0 : _rowheights[row]); + } + void col_width(int col, int width); // set/get a column's width + inline int col_width(int col) { + return((col<0 || col>=(int)_colwidths.size()) ? 0 : _colwidths[col]); + } + void row_height_all(int height) { // set all row/col heights + for ( int r=0; r<rows(); r++ ) { + row_height(r, height); + } + } + void col_width_all(int width) { + for ( int c=0; c<cols(); c++ ) { + col_width(c, width); + } + } + void row_position(int row); // set/get table's current scroll position + void col_position(int col); + int row_position() { // current row position + return(_row_position); + } + int col_position() { // current col position + return(_col_position); + } + inline void top_row(int row) { // set/get top row (deprecated) + row_position(row); + } + inline int top_row() { + return(row_position()); + } + int is_selected(int r, int c); // selected cell + void get_selection(int& s_top, int& s_left, int& s_bottom, int& s_right); + void set_selection(int s_top, int s_left, int s_bottom, int s_right); + int move_cursor(int R, int C); + + 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*)(Fl_Group::parent())); + } + Fl_Widget * const *array() { + return(table->array()); + } + Fl_Widget *child(int n) const { + return(table->child(n)); + } + 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 + int callback_row() { + return(_callback_row); + } + int callback_col() { + return(_callback_col); + } + 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(); + } +}; + +#endif /*_FL_TABLE_H*/ diff --git a/FL/Fl_Table_Row.H b/FL/Fl_Table_Row.H new file mode 100644 index 000000000..e7b38a48d --- /dev/null +++ b/FL/Fl_Table_Row.H @@ -0,0 +1,150 @@ +#ifndef _FL_TABLE_ROW_H +#define _FL_TABLE_ROW_H + +// +// Fl_Table_Row -- A row oriented table widget +// +// A class specializing in a table of rows. +// Handles row-specific selection behavior. +// +// Copyright 2002 by Greg Ercolano. +// +// 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". +// +// + +#include "Fl_Table.H" + +class Fl_Table_Row : public Fl_Table { +public: + enum TableRowSelectMode { + SELECT_NONE, // no selection allowed + SELECT_SINGLE, // single row selection + SELECT_MULTI // multiple row selection (default) + }; +private: + // An STL-ish vector without templates + class CharVector { + char *arr; + int _size; + void init() { + arr = NULL; + _size = 0; + } + void copy(char *newarr, int newsize) { + size(newsize); + memcpy(arr, newarr, newsize * sizeof(char)); + } + public: + CharVector() { // CTOR + init(); + } + ~CharVector() { // DTOR + if ( arr ) free(arr); + arr = NULL; + } + CharVector(CharVector&o) { // COPY CTOR + init(); + copy(o.arr, o._size); + } + CharVector& operator=(CharVector&o) { // ASSIGN + init(); + copy(o.arr, o._size); + return(*this); + } + char operator[](int x) const { + return(arr[x]); + } + char& operator[](int x) { + return(arr[x]); + } + int size() { + return(_size); + } + void size(int count) { + if ( count != _size ) { + arr = (char*)realloc(arr, count * sizeof(char)); + _size = count; + } + } + char pop_back() { + char tmp = arr[_size-1]; + _size--; + return(tmp); + } + void push_back(char val) { + int x = _size; + size(_size+1); + arr[x] = val; + } + char back() { + return(arr[_size-1]); + } + }; + CharVector _rowselect; // selection flag for each row + + // handle() state variables. + // Put here instead of local statics in handle(), so more + // than one instance can exist without crosstalk between. + // + int _dragging_select; // dragging out a selection? + int _last_row; + int _last_y; // last event's Y position + int _last_push_x; // last PUSH event's X position + int _last_push_y; // last PUSH event's Y position + + TableRowSelectMode _selectmode; + +protected: + int handle(int 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) { + return(Fl_Table::find_cell(context, R, C, X, Y, W, H)); + } + +public: + Fl_Table_Row(int X, int Y, int W, int H, const char *l=0) : Fl_Table(X,Y,W,H,l) { + _dragging_select = 0; + _last_row = -1; + _last_y = -1; + _last_push_x = -1; + _last_push_y = -1; + _selectmode = SELECT_MULTI; + } + ~Fl_Table_Row() { } + + void rows(int val); // set number of rows + int rows() { // get number of rows + return(Fl_Table::rows()); + } + void type(TableRowSelectMode val); // set selection mode + TableRowSelectMode type() const { // get selection mode + return(_selectmode); + } + int row_selected(int row); // is row selected? (0=no, 1=yes, -1=range err) + int select_row(int row, int flag=1); // select state for row: flag:0=off, 1=on, 2=toggle + // returns: 0=no change, 1=changed, -1=range err + void select_all_rows(int flag=1); // all rows to a known state + void clear() { + rows(0); // implies clearing selection + cols(0); + Fl_Table::clear(); // clear the table + } +}; + +#endif /*_FL_TABLE_ROW_H*/ diff --git a/ide/Xcode3.1/FLTK.xcodeproj/project.pbxproj b/ide/Xcode3.1/FLTK.xcodeproj/project.pbxproj index eea296c0c..5e42a6d02 100644 --- a/ide/Xcode3.1/FLTK.xcodeproj/project.pbxproj +++ b/ide/Xcode3.1/FLTK.xcodeproj/project.pbxproj @@ -7,6 +7,11 @@ objects = { /* Begin PBXBuildFile section */ + C904DE6E10AEDD2A00266003 /* Fl_Table_Row.cxx in Sources */ = {isa = PBXBuildFile; fileRef = C904DE6C10AEDD2A00266003 /* Fl_Table_Row.cxx */; }; + C904DE6F10AEDD2A00266003 /* Fl_Table.cxx in Sources */ = {isa = PBXBuildFile; fileRef = C904DE6D10AEDD2A00266003 /* Fl_Table.cxx */; }; + C904DE7910AEDD3700266003 /* table.cxx in Sources */ = {isa = PBXBuildFile; fileRef = C918E1410DDA11BA00167E99 /* table.cxx */; }; + C904DE7B10AEDD3700266003 /* fltk.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9A3E9510DD6336500486E4F /* fltk.framework */; }; + C904DE7D10AEDD3700266003 /* fltk.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = C9A3E9510DD6336500486E4F /* fltk.framework */; }; C918DB410DD9EE7500167E99 /* editor.cxx in Sources */ = {isa = PBXBuildFile; fileRef = C918DB400DD9EE7500167E99 /* editor.cxx */; }; C918DB4D0DD9EE8F00167E99 /* fltk.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9A3E9510DD6336500486E4F /* fltk.framework */; }; C918DB5F0DD9EED100167E99 /* fast_slow.fl in Resources */ = {isa = PBXBuildFile; fileRef = C918DB5E0DD9EED100167E99 /* fast_slow.fl */; }; @@ -124,7 +129,7 @@ C918E1180DD9FFB900167E99 /* fltk.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9A3E9510DD6336500486E4F /* fltk.framework */; }; C918E1320DDA117700167E99 /* tabs.fl in Resources */ = {isa = PBXBuildFile; fileRef = C918E1310DDA117700167E99 /* tabs.fl */; }; C918E1390DDA11AF00167E99 /* fltk.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9A3E9510DD6336500486E4F /* fltk.framework */; }; - C918E1420DDA11BA00167E99 /* tabs.cxx in Sources */ = {isa = PBXBuildFile; fileRef = C918E1410DDA11BA00167E99 /* tabs.cxx */; }; + C918E1420DDA11BA00167E99 /* table.cxx in Sources */ = {isa = PBXBuildFile; fileRef = C918E1410DDA11BA00167E99 /* table.cxx */; }; C918E15C0DDA11FE00167E99 /* threads.cxx in Sources */ = {isa = PBXBuildFile; fileRef = C918E15B0DDA11FE00167E99 /* threads.cxx */; }; C918E1630DDA120800167E99 /* fltk.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9A3E9510DD6336500486E4F /* fltk.framework */; }; C918E17C0DDA124500167E99 /* tile.cxx in Sources */ = {isa = PBXBuildFile; fileRef = C918E17B0DDA124500167E99 /* tile.cxx */; }; @@ -562,6 +567,18 @@ ); script = "cp -f ${INPUT_FILE_DIR}../../test/demo.menu ${TARGET_BUILD_DIR}"; }; + C904DE7E10AEDD3700266003 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.proxy.script; + filePatterns = "*.fl"; + fileType = pattern.proxy; + isEditable = 1; + outputFiles = ( + "${INPUT_FILE_DIR}/${INPUT_FILE_BASE}.cxx", + "${INPUT_FILE_DIR}/${INPUT_FILE_BASE}.h", + ); + script = "export DYLD_FRAMEWORK_PATH=${TARGET_BUILD_DIR} && cd ../../test && ${TARGET_BUILD_DIR}/Fluid.app/Contents/MacOS/Fluid -c ${INPUT_FILE_NAME}"; + }; C918DB780DD9EF6800167E99 /* PBXBuildRule */ = { isa = PBXBuildRule; compilerSpec = com.apple.compilers.proxy.script; @@ -699,6 +716,20 @@ remoteGlobalIDString = C9A3E9500DD6336500486E4F; remoteInfo = fltk; }; + C904DE7510AEDD3700266003 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = C9A3E93C0DD6332D00486E4F /* Project object */; + proxyType = 1; + remoteGlobalIDString = C9A3E9500DD6336500486E4F; + remoteInfo = fltk; + }; + C904DEB810AEE0BD00266003 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = C9A3E93C0DD6332D00486E4F /* Project object */; + proxyType = 1; + remoteGlobalIDString = C904DE7110AEDD3700266003 /* table */; + remoteInfo = table; + }; C918DB3D0DD9EE4600167E99 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = C9A3E93C0DD6332D00486E4F /* Project object */; @@ -1927,6 +1958,16 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + C904DE7C10AEDD3700266003 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + C904DE7D10AEDD3700266003 /* fltk.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; C94A76BC0E76D4A800AAA38E /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -2621,6 +2662,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + C904DE6C10AEDD2A00266003 /* Fl_Table_Row.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Fl_Table_Row.cxx; path = ../../src/Fl_Table_Row.cxx; sourceTree = SOURCE_ROOT; }; + C904DE6D10AEDD2A00266003 /* Fl_Table.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Fl_Table.cxx; path = ../../src/Fl_Table.cxx; sourceTree = SOURCE_ROOT; }; + C904DE8210AEDD3700266003 /* table.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = table.app; sourceTree = BUILT_PRODUCTS_DIR; }; + C904DE8F10AEDD3800266003 /* tabs-Info copy.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "tabs-Info copy.plist"; path = "plists/tabs-Info copy.plist"; sourceTree = "<group>"; }; C918DB370DD9EE4100167E99 /* editor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = editor.app; sourceTree = BUILT_PRODUCTS_DIR; }; C918DB390DD9EE4100167E99 /* editor-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "editor-Info.plist"; path = "plists/editor-Info.plist"; sourceTree = "<group>"; }; C918DB400DD9EE7500167E99 /* editor.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = editor.cxx; path = ../../test/editor.cxx; sourceTree = SOURCE_ROOT; }; @@ -2748,7 +2793,7 @@ C918E1260DDA112A00167E99 /* tabs.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = tabs.app; sourceTree = BUILT_PRODUCTS_DIR; }; C918E1280DDA112B00167E99 /* tabs-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "tabs-Info.plist"; path = "plists/tabs-Info.plist"; sourceTree = "<group>"; }; C918E1310DDA117700167E99 /* tabs.fl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = tabs.fl; path = ../../test/tabs.fl; sourceTree = SOURCE_ROOT; }; - C918E1410DDA11BA00167E99 /* tabs.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = tabs.cxx; path = ../../test/tabs.cxx; sourceTree = SOURCE_ROOT; }; + C918E1410DDA11BA00167E99 /* table.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = table.cxx; path = ../../test/table.cxx; sourceTree = SOURCE_ROOT; }; C918E1520DDA11E400167E99 /* threads.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = threads.app; sourceTree = BUILT_PRODUCTS_DIR; }; C918E1540DDA11E400167E99 /* threads-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "threads-Info.plist"; path = "plists/threads-Info.plist"; sourceTree = "<group>"; }; C918E15B0DDA11FE00167E99 /* threads.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = threads.cxx; path = ../../test/threads.cxx; sourceTree = SOURCE_ROOT; }; @@ -3121,6 +3166,14 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + C904DE7A10AEDD3700266003 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C904DE7B10AEDD3700266003 /* fltk.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; C918DB350DD9EE4100167E99 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -3843,6 +3896,7 @@ C94A76DA0E77F59F00AAA38E /* utf8.app */, C99E1EAE0E78628600AECCF6 /* curve.app */, C9EAC2DC0E786725004F64F7 /* colbrowser.app */, + C904DE8210AEDD3700266003 /* table.app */, ); name = Products; sourceTree = "<group>"; @@ -3921,6 +3975,7 @@ C9A3E9610DD633C300486E4F /* hello-Info.plist */, C9A3E9530DD6336500486E4F /* fltk-Info.plist */, C94A76DD0E77F5A800AAA38E /* symbols-Info copy.plist */, + C904DE8F10AEDD3800266003 /* tabs-Info copy.plist */, ); name = plists; sourceTree = "<group>"; @@ -3928,6 +3983,8 @@ C9A3E9590DD6338B00486E4F /* Library Sources */ = { isa = PBXGroup; children = ( + C904DE6C10AEDD2A00266003 /* Fl_Table_Row.cxx */, + C904DE6D10AEDD2A00266003 /* Fl_Table.cxx */, C9A3EAD80DD634CC00486E4F /* Fl_get_key_mac.cxx */, C9A3EAD70DD634CC00486E4F /* Fl_get_key_win32.cxx */, C9A3EAD60DD634CC00486E4F /* Fl_get_system_colors.cxx */, @@ -4196,7 +4253,7 @@ C918E0E10DD9FF2C00167E99 /* sudoku.cxx */, C918E1100DD9FFAD00167E99 /* symbols.cxx */, C918E1310DDA117700167E99 /* tabs.fl */, - C918E1410DDA11BA00167E99 /* tabs.cxx */, + C918E1410DDA11BA00167E99 /* table.cxx */, C918E15B0DDA11FE00167E99 /* threads.cxx */, C918E17B0DDA124500167E99 /* tile.cxx */, C918E1A10DDA128900167E99 /* tiled_image.cxx */, @@ -4374,6 +4431,26 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + C904DE7110AEDD3700266003 /* table */ = { + isa = PBXNativeTarget; + buildConfigurationList = C904DE7F10AEDD3700266003 /* Build configuration list for PBXNativeTarget "table" */; + buildPhases = ( + C904DE7610AEDD3700266003 /* Resources */, + C904DE7810AEDD3700266003 /* Sources */, + C904DE7A10AEDD3700266003 /* Frameworks */, + C904DE7C10AEDD3700266003 /* CopyFiles */, + ); + buildRules = ( + C904DE7E10AEDD3700266003 /* PBXBuildRule */, + ); + dependencies = ( + C904DE7410AEDD3700266003 /* PBXTargetDependency */, + ); + name = table; + productName = tabs; + productReference = C904DE8210AEDD3700266003 /* table.app */; + productType = "com.apple.product-type.application"; + }; C918DB360DD9EE4100167E99 /* editor */ = { isa = PBXNativeTarget; buildConfigurationList = C918DB3C0DD9EE4100167E99 /* Build configuration list for PBXNativeTarget "editor" */; @@ -5344,6 +5421,7 @@ C918E2300DDA13BC00167E99 /* PBXTargetDependency */, C918E2320DDA13BC00167E99 /* PBXTargetDependency */, C918E2340DDA13BC00167E99 /* PBXTargetDependency */, + C904DEB910AEE0BD00266003 /* PBXTargetDependency */, C918E2360DDA13BC00167E99 /* PBXTargetDependency */, C918E2380DDA13BC00167E99 /* PBXTargetDependency */, C918E23A0DDA13BC00167E99 /* PBXTargetDependency */, @@ -5919,6 +5997,7 @@ C918E0B80DD9FE9500167E99 /* subwindow */, C918E0D70DD9FED700167E99 /* sudoku */, C918E1060DD9FF9300167E99 /* symbols */, + C904DE7110AEDD3700266003 /* table */, C918E1250DDA112A00167E99 /* tabs */, C918E1510DDA11E400167E99 /* threads */, C918E1710DDA122D00167E99 /* tile */, @@ -5930,6 +6009,13 @@ /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + C904DE7610AEDD3700266003 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; C918DB330DD9EE4100167E99 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -6441,6 +6527,14 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + C904DE7810AEDD3700266003 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C904DE7910AEDD3700266003 /* table.cxx in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; C918DB340DD9EE4100167E99 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -6752,7 +6846,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - C918E1420DDA11BA00167E99 /* tabs.cxx in Sources */, + C918E1420DDA11BA00167E99 /* table.cxx in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -6981,6 +7075,8 @@ C99E1E9B0E78620B00AECCF6 /* case.c in Sources */, C99E1E9C0E78620B00AECCF6 /* is_right2left.c in Sources */, C99E1E9D0E78620B00AECCF6 /* is_spacing.c in Sources */, + C904DE6E10AEDD2A00266003 /* Fl_Table_Row.cxx in Sources */, + C904DE6F10AEDD2A00266003 /* Fl_Table.cxx in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -7263,6 +7359,16 @@ target = C9A3E9500DD6336500486E4F /* fltk */; targetProxy = 01FBE4930E630405009E95B1 /* PBXContainerItemProxy */; }; + C904DE7410AEDD3700266003 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C9A3E9500DD6336500486E4F /* fltk */; + targetProxy = C904DE7510AEDD3700266003 /* PBXContainerItemProxy */; + }; + C904DEB910AEE0BD00266003 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C904DE7110AEDD3700266003 /* table */; + targetProxy = C904DEB810AEE0BD00266003 /* PBXContainerItemProxy */; + }; C918DB3E0DD9EE4600167E99 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = C9A3E9500DD6336500486E4F /* fltk */; @@ -8141,6 +8247,53 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + C904DE8010AEDD3700266003 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Carbon.framework/Headers/Carbon.h"; + HEADER_SEARCH_PATHS = ../../; + INFOPLIST_FILE = "plists/table-Info.plist"; + INSTALL_PATH = "$(HOME)/Applications"; + OTHER_LDFLAGS = ( + "-framework", + Carbon, + ); + PREBINDING = NO; + PRODUCT_NAME = table; + WRAPPER_EXTENSION = app; + ZERO_LINK = YES; + }; + name = Debug; + }; + C904DE8110AEDD3700266003 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Carbon.framework/Headers/Carbon.h"; + HEADER_SEARCH_PATHS = ../../; + INFOPLIST_FILE = "plists/table-Info.plist"; + INSTALL_PATH = "$(HOME)/Applications"; + OTHER_LDFLAGS = ( + "-framework", + Carbon, + ); + PREBINDING = NO; + PRODUCT_NAME = table; + WRAPPER_EXTENSION = app; + ZERO_LINK = NO; + }; + name = Release; + }; C918DB3A0DD9EE4100167E99 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -11585,6 +11738,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + C904DE7F10AEDD3700266003 /* Build configuration list for PBXNativeTarget "table" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C904DE8010AEDD3700266003 /* Debug */, + C904DE8110AEDD3700266003 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; C918DB3C0DD9EE4100167E99 /* Build configuration list for PBXNativeTarget "editor" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/ide/Xcode3.1/plists/table-Info.plist b/ide/Xcode3.1/plists/table-Info.plist new file mode 100644 index 000000000..6002a2474 --- /dev/null +++ b/ide/Xcode3.1/plists/table-Info.plist @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleIdentifier</key> + <string>org.fltk.table</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleSignature</key> + <string>FLTK</string> + <key>CFBundleVersion</key> + <string>1.0</string> +</dict> +</plist> diff --git a/src/Fl_Table.cxx b/src/Fl_Table.cxx new file mode 100644 index 000000000..32d35cfea --- /dev/null +++ b/src/Fl_Table.cxx @@ -0,0 +1,1218 @@ +// +// 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. +// + +#include <stdio.h> // fprintf +#include <FL/fl_draw.H> +#include <FL/Fl_Table.H> + +#if defined(USE_UTF8) && ( defined(MICROSOFT) || defined(LINUX) ) +#include <FL/fl_utf8.H> // currently only Windows and Linux +#endif + +#define SCROLLBAR_SIZE 16 + +// Scroll display so 'row' is at top +void Fl_Table::row_position(int row) { + if ( _row_position == row ) return; // OPTIMIZATION: no change? avoid redraw + if ( row < 0 ) row = 0; + else if ( row >= rows() ) row = rows() - 1; + if ( table_h <= tih ) return; // don't scroll if table smaller than window + double newtop = row_scroll_position(row); + if ( newtop > vscrollbar->maximum() ) { + newtop = vscrollbar->maximum(); + } + vscrollbar->Fl_Slider::value(newtop); + table_scrolled(); + redraw(); + _row_position = row; // HACK: override what table_scrolled() came up with +} + +// Scroll display so 'col' is at left +void Fl_Table::col_position(int col) { + if ( _col_position == col ) return; // OPTIMIZATION: no change? avoid redraw + if ( col < 0 ) col = 0; + else if ( col >= cols() ) col = cols() - 1; + if ( table_w <= tiw ) return; // don't scroll if table smaller than window + double newleft = col_scroll_position(col); + if ( newleft > hscrollbar->maximum() ) { + newleft = hscrollbar->maximum(); + } + hscrollbar->Fl_Slider::value(newleft); + table_scrolled(); + redraw(); + _col_position = col; // HACK: override what table_scrolled() came up with +} + +// Find scroll position of a row (in pixels) +long Fl_Table::row_scroll_position(int row) { + int startrow = 0; + long scroll = 0; + // OPTIMIZATION: + // Attempt to use precomputed row scroll position + // + if ( toprow_scrollpos != -1 && row >= toprow ) { + scroll = toprow_scrollpos; + startrow = toprow; + } + for ( int t=startrow; t<row; t++ ) { + scroll += row_height(t); + } + return(scroll); +} + +// Find scroll position of a column (in pixels) +long Fl_Table::col_scroll_position(int col) { + int startcol = 0; + long scroll = 0; + // OPTIMIZATION: + // Attempt to use precomputed row scroll position + // + if ( leftcol_scrollpos != -1 && col >= leftcol ) { + scroll = leftcol_scrollpos; + startcol = leftcol; + } + for ( int t=startcol; t<col; t++ ) { + scroll += col_width(t); + } + return(scroll); +} + +// Ctor +Fl_Table::Fl_Table(int X, int Y, int W, int H, const char *l) : Fl_Group(X,Y,W,H,l) { + _rows = 0; + _cols = 0; + _row_header_w = 40; + _col_header_h = 18; + _row_header = 0; + _col_header = 0; + _row_header_color = color(); + _col_header_color = color(); + _row_resize = 0; + _col_resize = 0; + _row_resize_min = 1; + _col_resize_min = 1; + _redraw_toprow = -1; + _redraw_botrow = -1; + _redraw_leftcol = -1; + _redraw_rightcol = -1; + table_w = 0; + table_h = 0; + toprow = 0; + botrow = 0; + leftcol = 0; + rightcol = 0; + toprow_scrollpos = -1; + leftcol_scrollpos = -1; + _last_cursor = FL_CURSOR_DEFAULT; + _resizing_col = -1; + _resizing_row = -1; + _dragging_x = -1; + _dragging_y = -1; + _last_row = -1; + _auto_drag = 0; + current_col = -1; + current_row = -1; + select_row = -1; + select_col = -1; + + box(FL_THIN_DOWN_FRAME); + + vscrollbar = new Fl_Scrollbar(x()+w()-SCROLLBAR_SIZE, y(), + SCROLLBAR_SIZE, h()-SCROLLBAR_SIZE); + vscrollbar->type(FL_VERTICAL); + vscrollbar->callback(scroll_cb, (void*)this); + + hscrollbar = new Fl_Scrollbar(x(), y()+h()-SCROLLBAR_SIZE, + w(), SCROLLBAR_SIZE); + hscrollbar->type(FL_HORIZONTAL); + hscrollbar->callback(scroll_cb, (void*)this); + + table = new Fl_Scroll(x(), y(), w(), h()); + table->box(FL_NO_BOX); + table->type(0); // don't show Fl_Scroll's scrollbars -- use our own + table->hide(); // hide unless children are present + table->end(); + + table_resized(); + redraw(); + + Fl_Group::end(); // end the group's begin() + + table->begin(); // leave with fltk children getting added to the scroll +} + +// Dtor +Fl_Table::~Fl_Table() { + // The parent Fl_Group takes care of destroying scrollbars +} + +// Set height of a row +void Fl_Table::row_height(int row, int height) { + if ( row < 0 ) return; + if ( row < (int)_rowheights.size() && _rowheights[row] == height ) { + return; // OPTIMIZATION: no change? avoid redraw + } + // Add row heights, even if none yet + int now_size = (int)_rowheights.size(); + if ( row >= now_size ) { + _rowheights.size(row); + while (now_size < row) + _rowheights[now_size++] = height; + } + _rowheights[row] = height; + table_resized(); + if ( row <= botrow ) { // OPTIMIZATION: only redraw if onscreen or above screen + redraw(); + } + // ROW RESIZE CALLBACK + if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED ) { + do_callback(CONTEXT_RC_RESIZE, row, 0); + } +} + +// Set width of a column +void Fl_Table::col_width(int col, int width) +{ + if ( col < 0 ) return; + if ( col < (int)_colwidths.size() && _colwidths[col] == width ) { + return; // OPTIMIZATION: no change? avoid redraw + } + // Add column widths, even if none yet + int now_size = (int)_colwidths.size(); + if ( col >= now_size ) { + _colwidths.size(col); + while (now_size < col) { + _colwidths[now_size++] = width; + } + } + _colwidths[col] = width; + table_resized(); + if ( col <= rightcol ) { // OPTIMIZATION: only redraw if onscreen or to the left + redraw(); + } + // COLUMN RESIZE CALLBACK + if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED ) { + do_callback(CONTEXT_RC_RESIZE, 0, col); + } +} + +// Return row/col clamped to reality +int Fl_Table::row_col_clamp(TableContext context, int &R, int &C) { + int clamped = 0; + if ( R < 0 ) { R = 0; clamped = 1; } + if ( C < 0 ) { C = 0; clamped = 1; } + switch ( context ) { + case CONTEXT_COL_HEADER: + // Allow col headers to draw even if no rows + if ( R >= _rows && R != 0 ) { R = _rows - 1; clamped = 1; } + break; + + case CONTEXT_ROW_HEADER: + // Allow row headers to draw even if no columns + if ( C >= _cols && C != 0 ) { C = _cols - 1; clamped = 1; } + break; + + case CONTEXT_CELL: + default: + // CLAMP R/C TO _rows/_cols + if ( R >= _rows ) { R = _rows - 1; clamped = 1; } + if ( C >= _cols ) { C = _cols - 1; clamped = 1; } + break; + } + return(clamped); +} + +// Return bounding region for given context +void Fl_Table::get_bounds(TableContext context, int &X, int &Y, int &W, int &H) { + switch ( context ) { + case CONTEXT_COL_HEADER: + // Column header clipping. + X = tox; + Y = wiy; + W = tow; + H = col_header_height(); + return; + + case CONTEXT_ROW_HEADER: + // Row header clipping. + X = wix; + Y = toy; + W = row_header_width(); + H = toh; + return; + + case CONTEXT_TABLE: + // Table inner dimensions + X = tix; Y = tiy; W = tiw; H = tih; + return; + + // TODO: Add other contexts.. + default: + fprintf(stderr, "Fl_Table::get_bounds(): context %d unimplemented\n", (int)context); + return; + } + //NOTREACHED +} + +// Find row/col beneath cursor +// +// Returns R/C and context. +// Also returns resizeflag, if mouse is hovered over a resize boundary. +// +Fl_Table::TableContext Fl_Table::cursor2rowcol(int &R, int &C, ResizeFlag &resizeflag) { + // return values + R = C = 0; + resizeflag = RESIZE_NONE; + // Row header? + int X, Y, W, H; + if ( row_header() ) { + // Inside a row heading? + get_bounds(CONTEXT_ROW_HEADER, X, Y, W, H); + if ( Fl::event_inside(X, Y, W, H) ) { + // Scan visible rows until found + for ( R = toprow; R <= botrow; R++ ) { + find_cell(CONTEXT_ROW_HEADER, R, 0, X, Y, W, H); + if ( Fl::event_y() >= Y && Fl::event_y() < (Y+H) ) { + // Found row? + // If cursor over resize boundary, and resize enabled, + // enable the appropriate resize flag. + // + if ( row_resize() ) { + if ( Fl::event_y() <= (Y+3-0) ) { resizeflag = RESIZE_ROW_ABOVE; } + if ( Fl::event_y() >= (Y+H-3) ) { resizeflag = RESIZE_ROW_BELOW; } + } + return(CONTEXT_ROW_HEADER); + } + } + // Must be in row header dead zone + return(CONTEXT_NONE); + } + } + // Column header? + if ( col_header() ) { + // Inside a column heading? + get_bounds(CONTEXT_COL_HEADER, X, Y, W, H); + if ( Fl::event_inside(X, Y, W, H) ) { + // Scan visible columns until found + for ( C = leftcol; C <= rightcol; C++ ) { + find_cell(CONTEXT_COL_HEADER, 0, C, X, Y, W, H); + if ( Fl::event_x() >= X && Fl::event_x() < (X+W) ) { + // Found column? + // If cursor over resize boundary, and resize enabled, + // enable the appropriate resize flag. + // + if ( col_resize() ) { + if ( Fl::event_x() <= (X+3-0) ) { resizeflag = RESIZE_COL_LEFT; } + if ( Fl::event_x() >= (X+W-3) ) { resizeflag = RESIZE_COL_RIGHT; } + } + return(CONTEXT_COL_HEADER); + } + } + // Must be in column header dead zone + return(CONTEXT_NONE); + } + } + // Mouse somewhere in table? + // Scan visible r/c's until we find it. + // + if ( Fl::event_inside(tox, toy, tow, toh) ) { + for ( R = toprow; R <= botrow; R++ ) { + find_cell(CONTEXT_CELL, R, C, X, Y, W, H); + if ( Fl::event_y() < Y ) break; // OPT: thanks lars + if ( Fl::event_y() >= (Y+H) ) continue; // OPT: " " + for ( C = leftcol; C <= rightcol; C++ ) { + find_cell(CONTEXT_CELL, R, C, X, Y, W, H); + if ( Fl::event_inside(X, Y, W, H) ) { + return(CONTEXT_CELL); // found it + } + } + } + // Must be in a dead zone of the table + R = C = 0; + return(CONTEXT_TABLE); + } + // Somewhere else + return(CONTEXT_NONE); +} + +// Find X/Y/W/H for cell at R/C +// If R or C are out of range, returns -1 +// with X/Y/W/H set to zero. +// +int Fl_Table::find_cell(TableContext context, int R, int C, int &X, int &Y, int &W, int &H) { + if ( row_col_clamp(context, R, C) ) { // row or col out of range? error + X=Y=W=H=0; + return(-1); + } + X = col_scroll_position(C) - hscrollbar->value() + tix; + Y = row_scroll_position(R) - vscrollbar->value() + tiy; + W = col_width(C); + H = row_height(R); + + switch ( context ) { + case CONTEXT_COL_HEADER: + Y = wiy; + H = col_header_height(); + return(0); + + case CONTEXT_ROW_HEADER: + X = wix; + W = row_header_width(); + return(0); + + case CONTEXT_CELL: + return(0); + + case CONTEXT_TABLE: + return(0); + + // TODO -- HANDLE OTHER CONTEXTS + default: + fprintf(stderr, "Fl_Table::find_cell: unknown context %d\n", (int)context); + return(-1); + } + //NOTREACHED +} + +// Enable automatic scroll-selection +void Fl_Table::_start_auto_drag() { + if (_auto_drag) return; + _auto_drag = 1; + Fl::add_timeout(0.3, _auto_drag_cb2, this); +} + +// Disable automatic scroll-selection +void Fl_Table::_stop_auto_drag() { + if (!_auto_drag) return; + Fl::remove_timeout(_auto_drag_cb2, this); + _auto_drag = 0; +} + +void Fl_Table::_auto_drag_cb2(void *d) { + ((Fl_Table*)d)->_auto_drag_cb(); +} + +// Handle automatic scroll-selection if mouse selection dragged off table edge +void Fl_Table::_auto_drag_cb() { + int lx = Fl::e_x; + int ly = Fl::e_y; + if (_selecting == CONTEXT_COL_HEADER) + { ly = y() + col_header_height(); } + else if (_selecting == CONTEXT_ROW_HEADER) + { lx = x() + row_header_width(); } + if (lx > x() + w() - 20) { + Fl::e_x = x() + w() - 20; + if (hscrollbar->visible()) + ((Fl_Slider*)hscrollbar)->value( + hscrollbar->clamp(hscrollbar->value() + 30)); + hscrollbar->do_callback(); + _dragging_x = Fl::e_x - 30; + } + else if (lx < (x() + row_header_width())) { + Fl::e_x = x() + row_header_width() + 1; + if (hscrollbar->visible()) { + ((Fl_Slider*)hscrollbar)->value(hscrollbar->clamp(hscrollbar->value() - 30)); + } + hscrollbar->do_callback(); + _dragging_x = Fl::e_x + 30; + } + if (ly > y() + h() - 20) { + Fl::e_y = y() + h() - 20; + if (vscrollbar->visible()) { + ((Fl_Slider*)vscrollbar)->value(vscrollbar->clamp(vscrollbar->value() + 30)); + } + vscrollbar->do_callback(); + _dragging_y = Fl::e_y - 30; + } + else if (ly < (y() + col_header_height())) { + Fl::e_y = y() + col_header_height() + 1; + if (vscrollbar->visible()) { + ((Fl_Slider*)vscrollbar)->value(vscrollbar->clamp(vscrollbar->value() - 30)); + } + vscrollbar->do_callback(); + _dragging_y = Fl::e_y + 30; + } + _auto_drag = 2; + handle(FL_DRAG); + _auto_drag = 1; + Fl::e_x = lx; + Fl::e_y = ly; + Fl::check(); + Fl::flush(); + if (Fl::event_buttons() && _auto_drag) { + Fl::add_timeout(0.05, _auto_drag_cb2, this); + } +} + +// Recalculate the window dimensions +void Fl_Table::recalc_dimensions() { + // Recalc to* (Table Outer), ti* (Table Inner), wi* ( Widget Inner) + wix = ( x() + Fl::box_dx(box())); tox = wix; tix = tox + Fl::box_dx(table->box()); + wiy = ( y() + Fl::box_dy(box())); toy = wiy; tiy = toy + Fl::box_dy(table->box()); + wiw = ( w() - Fl::box_dw(box())); tow = wiw; tiw = tow - Fl::box_dw(table->box()); + wih = ( h() - Fl::box_dh(box())); toh = wih; tih = toh - Fl::box_dh(table->box()); + // Trim window if headers enabled + if ( col_header() ) { + tiy += col_header_height(); toy += col_header_height(); + tih -= col_header_height(); toh -= col_header_height(); + } + if ( row_header() ) { + tix += row_header_width(); tox += row_header_width(); + tiw -= row_header_width(); tow -= row_header_width(); + } + // Make scroll bars disappear if window large enough + { + // First pass: can hide via window size? + int hidev = (table_h <= tih); + int hideh = (table_w <= tiw); + // Second pass: Check for interference + if ( !hideh & hidev ) { hidev = (( table_h - tih + SCROLLBAR_SIZE ) <= 0 ); } + if ( !hidev & hideh ) { hideh = (( table_w - tiw + SCROLLBAR_SIZE ) <= 0 ); } + // Determine scrollbar visibility, trim ti[xywh]/to[xywh] + if ( hidev ) { vscrollbar->hide(); } + else { vscrollbar->show(); tiw -= SCROLLBAR_SIZE; tow -= SCROLLBAR_SIZE; } + if ( hideh ) { hscrollbar->hide(); } + else { hscrollbar->show(); tih -= SCROLLBAR_SIZE; toh -= SCROLLBAR_SIZE; } + } + // Resize the child table + table->resize(tox, toy, tow, toh); + table->init_sizes(); +} + +// Recalculate internals after a scroll. +// +// Call this if table has been scrolled or resized. +// Does not handle redraw(). +// TODO: Assumes ti[xywh] has already been recalculated. +// +void Fl_Table::table_scrolled() { + // Find top row + int y, row, voff = vscrollbar->value(); + for ( row=y=0; row < _rows; row++ ) { + y += row_height(row); + if ( y > voff ) { y -= row_height(row); break; } + } + _row_position = toprow = ( row >= _rows ) ? (row - 1) : row; + toprow_scrollpos = y; // OPTIMIZATION: save for later use + // Find bottom row + voff = vscrollbar->value() + tih; + for ( ; row < _rows; row++ ) { + y += row_height(row); + if ( y >= voff ) { break; } + } + botrow = ( row >= _rows ) ? (row - 1) : row; + // Left column + int x, col, hoff = hscrollbar->value(); + for ( col=x=0; col < _cols; col++ ) { + x += col_width(col); + if ( x > hoff ) { x -= col_width(col); break; } + } + _col_position = leftcol = ( col >= _cols ) ? (col - 1) : col; + leftcol_scrollpos = x; // OPTIMIZATION: save for later use + // Right column + // Work with data left over from leftcol calculation + // + hoff = hscrollbar->value() + tiw; + for ( ; col < _cols; col++ ) { + x += col_width(col); + if ( x >= hoff ) { break; } + } + rightcol = ( col >= _cols ) ? (col - 1) : col; + // First tell children to scroll + draw_cell(CONTEXT_RC_RESIZE, 0,0,0,0,0,0); +} + +// Table resized: recalc internal data +// Call this whenever the window is resized. +// Recalculates the scrollbar sizes. +// Makes no assumptions about any pre-initialized data. +// +void Fl_Table::table_resized() { + table_h = row_scroll_position(rows()); + table_w = col_scroll_position(cols()); + recalc_dimensions(); + // Recalc scrollbar sizes + // Clamp scrollbar value() after a resize. + // Resize scrollbars to enforce a constant trough width after a window resize. + // + { + // Vertical scrollbar + float vscrolltab = ( table_h == 0 || tih > table_h ) ? 1 : (float)tih / table_h; + float hscrolltab = ( table_w == 0 || tiw > table_w ) ? 1 : (float)tiw / table_w; + vscrollbar->bounds(0, table_h-tih); + vscrollbar->precision(10); + vscrollbar->slider_size(vscrolltab); + vscrollbar->resize(wix+wiw-SCROLLBAR_SIZE, wiy, + SCROLLBAR_SIZE, + wih - ((hscrollbar->visible())?SCROLLBAR_SIZE:0)); + vscrollbar->Fl_Valuator::value(vscrollbar->clamp(vscrollbar->value())); + // Horizontal scrollbar + hscrollbar->bounds(0, table_w-tiw); + hscrollbar->precision(10); + hscrollbar->slider_size(hscrolltab); + hscrollbar->resize(wix, wiy+wih-SCROLLBAR_SIZE, + wiw - ((vscrollbar->visible())?SCROLLBAR_SIZE:0), + SCROLLBAR_SIZE); + hscrollbar->Fl_Valuator::value(hscrollbar->clamp(hscrollbar->value())); + } + + // Tell FLTK child widgets were resized + Fl_Group::init_sizes(); + + // Recalc top/bot/left/right + table_scrolled(); + + // DO *NOT* REDRAW -- LEAVE THIS UP TO THE CALLER + // redraw(); +} + +// Someone moved a scrollbar +void Fl_Table::scroll_cb(Fl_Widget*w, void *data) { + Fl_Table *o = (Fl_Table*)data; + o->recalc_dimensions(); // recalc tix, tiy, etc. + o->table_scrolled(); + o->redraw(); +} + +// Set number of rows +void Fl_Table::rows(int val) { + int oldrows = _rows; + _rows = val; + { + int default_h = ( _rowheights.size() > 0 ) ? _rowheights.back() : 25; + int now_size = _rowheights.size(); + _rowheights.size(val); // enlarge or shrink as needed + while ( now_size < val ) { + _rowheights[now_size++] = default_h; // fill new + } + } + table_resized(); + + // OPTIMIZATION: redraw only if change is visible. + if ( val >= oldrows && oldrows > botrow ) { + // NO REDRAW + } else { + redraw(); + } +} + +// Set number of cols +void Fl_Table::cols(int val) { + _cols = val; + { + int default_w = ( _colwidths.size() > 0 ) ? _colwidths[_colwidths.size()-1] : 80; + int now_size = _colwidths.size(); + _colwidths.size(val); // enlarge or shrink as needed + while ( now_size < val ) { + _colwidths[now_size++] = default_w; // fill new + } + } + table_resized(); + redraw(); +} + +// Change mouse cursor to different type +void Fl_Table::change_cursor(Fl_Cursor newcursor) { + if ( newcursor != _last_cursor ) { + fl_cursor(newcursor, FL_BLACK, FL_WHITE); + _last_cursor = newcursor; + } +} + +void Fl_Table::damage_zone(int r1, int c1, int r2, int c2, int r3, int c3) { + int R1 = r1, C1 = c1; + int R2 = r2, C2 = c2; + if (r1 > R2) R2 = r1; + if (r2 < R1) R1 = r2; + if (r3 > R2) R2 = r3; + if (r3 < R1) R1 = r3; + if (c1 > C2) C2 = c1; + if (c2 < C1) C1 = c2; + if (c3 > C2) C2 = c3; + if (c3 < C1) C1 = c3; + if (R1 < 0) { + if (R2 < 0) return; + R1 = 0; + } + if (C1 < 0) { + if (C2 < 0) return; + C1 = 0; + } + if (R1 < toprow) R1 = toprow; + if (R2 > botrow) R2 = botrow; + if (C1 < leftcol) C1 = leftcol; + if (C2 > rightcol) C2 = rightcol; + redraw_range(R1, R2, C1, C2); +} + +int Fl_Table::move_cursor(int R, int C) { + if (select_row == -1) R++; + if (select_col == -1) C++; + R += select_row; + C += select_col; + if (R < 0) R = 0; + if (R >= rows()) R = rows() - 1; + if (C < 0) C = 0; + if (C >= cols()) C = cols() - 1; + if (R == select_row && C == select_col) return 0; + damage_zone(current_row, current_col, select_row, select_col, R, C); + select_row = R; + select_col = C; + if (!Fl::event_state(FL_SHIFT)) { + current_row = R; + current_col = C; + } + if (R < toprow + 1 || R > botrow - 1) row_position(R); + if (C < leftcol + 1 || C > rightcol - 1) col_position(C); + return 1; +} + +// #define DEBUG 1 +#ifdef DEBUG +#include "eventnames.h" +#define PRINTEVENT \ + fprintf(stderr,"Table %s: ** Event: %s --\n", (label()?label():"none"), eventnames[event]); +#else +#define PRINTEVENT +#endif + +// Handle FLTK events +int Fl_Table::handle(int event) { + PRINTEVENT; + int ret = Fl_Group::handle(event); // let FLTK group handle events first + if (ret) { + if (Fl::event_inside(hscrollbar) || Fl::event_inside(vscrollbar)) return 1; + if (Fl::focus() != this && contains(Fl::focus())) return 1; + } + // Which row/column are we over? + int R, C; // row/column being worked on + ResizeFlag resizeflag; // which resizing area are we over? (0=none) + TableContext context = cursor2rowcol(R, C, resizeflag); + switch ( event ) { + case FL_PUSH: + if (Fl::event_button() == 1 && !Fl::event_clicks()) { + if (Fl::focus() != this) { + take_focus(); + do_callback(CONTEXT_TABLE, -1, -1); + ret = 1; + } + damage_zone(current_row, current_col, select_row, select_col, R, C); + if (context == CONTEXT_CELL) { + current_row = select_row = R; + current_col = select_col = C; + _selecting = CONTEXT_CELL; + } else { + current_row = select_row = -1; + current_col = select_col = -1; + } + } + // Need this for eg. right click to pop up a menu + if ( Fl_Widget::callback() && // callback defined? + resizeflag == RESIZE_NONE ) { // not resizing? + do_callback(context, R, C); // do callback + } + switch ( context ) { + case CONTEXT_CELL: + // FL_PUSH on a cell? + ret = 1; // express interest in FL_RELEASE + break; + + case CONTEXT_NONE: + // FL_PUSH on table corner? + if ( Fl::event_button() == 1 && + Fl::event_x() < x() + row_header_width()) { + current_col = 0; + select_col = cols() - 1; + current_row = 0; + select_row = rows() - 1; + damage_zone(current_row, current_col, select_row, select_col); + ret = 1; + } + break; + + case CONTEXT_COL_HEADER: + // FL_PUSH on a column header? + if ( Fl::event_button() == 1) { + // Resizing? Handle it + if ( resizeflag ) { + // Start resize if left click on column border. + // "ret=1" ensures we get drag events from now on. + // (C-1) is used if mouse is over the left hand side + // of cell, so we resize the next column on the left. + // + _resizing_col = ( resizeflag & RESIZE_COL_LEFT ) ? C-1 : C; + _resizing_row = -1; + _dragging_x = Fl::event_x(); + ret = 1; + } else { + // Not resizing? Select the column + current_col = select_col = C; + current_row = 0; + select_row = rows() - 1; + _selecting = CONTEXT_COL_HEADER; + damage_zone(current_row, current_col, select_row, select_col); + ret = 1; + } + } + break; + + case CONTEXT_ROW_HEADER: + // FL_PUSH on a row header? + if ( Fl::event_button() == 1 ) { + // Resizing? Handle it + if ( resizeflag ) { + // Start resize if left mouse clicked on row border. + // "ret = 1" ensures we get drag events from now on. + // (R-1) is used if mouse is over the top of the cell, + // so that we resize the row above. + // + _resizing_row = ( resizeflag & RESIZE_ROW_ABOVE ) ? R-1 : R; + _resizing_col = -1; + _dragging_y = Fl::event_y(); + ret = 1; + } else { + // Not resizing? Select the row + current_row = select_row = R; + current_col = 0; + select_col = cols() - 1; + _selecting = CONTEXT_ROW_HEADER; + damage_zone(current_row, current_col, select_row, select_col); + ret = 1; + } + } + break; + + default: + ret = 0; // express disinterest + break; + } + _last_row = R; + break; + + case FL_DRAG: + if (_auto_drag == 1) { + ret = 1; + break; + } + if ( _resizing_col > -1 ) { + // Dragging column? + // + // Let user drag even /outside/ the row/col widget. + // Don't allow column width smaller than 1. + // Continue to show FL_CURSOR_WE at all times during drag. + // + int offset = _dragging_x - Fl::event_x(); + int new_w = col_width(_resizing_col) - offset; + if ( new_w < _col_resize_min ) new_w = _col_resize_min; + col_width(_resizing_col, new_w); + _dragging_x = Fl::event_x(); + table_resized(); + redraw(); + change_cursor(FL_CURSOR_WE); + ret = 1; + if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED ) { + do_callback(CONTEXT_RC_RESIZE, R, C); + } + } + else if ( _resizing_row > -1 ) { + // Dragging row? + // + // Let user drag even /outside/ the row/col widget. + // Don't allow row width smaller than 1. + // Continue to show FL_CURSOR_NS at all times during drag. + // + int offset = _dragging_y - Fl::event_y(); + int new_h = row_height(_resizing_row) - offset; + if ( new_h < _row_resize_min ) new_h = _row_resize_min; + row_height(_resizing_row, new_h); + _dragging_y = Fl::event_y(); + table_resized(); + redraw(); + change_cursor(FL_CURSOR_NS); + ret = 1; + if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED ) { + do_callback(CONTEXT_RC_RESIZE, R, C); + } + } else { + if (Fl::event_button() == 1 && + _selecting == CONTEXT_CELL && + context == CONTEXT_CELL) { + if (select_row != R || select_col != C) { + damage_zone(current_row, current_col, select_row, select_col, R, C); + } + select_row = R; + select_col = C; + ret = 1; + } + else if (Fl::event_button() == 1 && + _selecting == CONTEXT_ROW_HEADER && + context & (CONTEXT_ROW_HEADER|CONTEXT_COL_HEADER|CONTEXT_CELL)) { + if (select_row != R) { + damage_zone(current_row, current_col, select_row, select_col, R, C); + } + select_row = R; + ret = 1; + } + else if (Fl::event_button() == 1 && + _selecting == CONTEXT_COL_HEADER + && context & (CONTEXT_ROW_HEADER|CONTEXT_COL_HEADER|CONTEXT_CELL)) { + if (select_col != C) { + damage_zone(current_row, current_col, select_row, select_col, R, C); + } + select_col = C; + ret = 1; + } + } + // Enable autodrag if not resizing, and mouse has moved off table edge + if ( _resizing_row < 0 && _resizing_col < 0 && _auto_drag == 0 && + ( Fl::event_x() > x() + w() - 20 || + Fl::event_x() < x() + row_header_width() || + Fl::event_y() > y() + h() - 20 || + Fl::event_y() < y() + col_header_height() + ) ) { + _start_auto_drag(); + } + break; + + case FL_RELEASE: + _stop_auto_drag(); + switch ( context ) { + case CONTEXT_ROW_HEADER: // release on row header + case CONTEXT_COL_HEADER: // release on col header + case CONTEXT_CELL: // release on a cell + case CONTEXT_TABLE: // release on dead zone + if ( _resizing_col == -1 && // not resizing a column + _resizing_row == -1 && // not resizing a row + Fl_Widget::callback() && // callback defined + when() & FL_WHEN_RELEASE && // on button release + _last_row == R ) { // release on same row PUSHed? + // Need this for eg. left clicking on a cell to select it + do_callback(context, R, C); + } + break; + + default: + break; + } + if ( Fl::event_button() == 1 ) { + change_cursor(FL_CURSOR_DEFAULT); + _resizing_col = -1; + _resizing_row = -1; + ret = 1; + } + break; + + case FL_MOVE: + if ( context == CONTEXT_COL_HEADER && // in column header? + resizeflag ) { // resize + near boundary? + change_cursor(FL_CURSOR_WE); // show resize cursor + } + else if ( context == CONTEXT_ROW_HEADER && // in row header? + resizeflag ) { // resize + near boundary? + change_cursor(FL_CURSOR_NS); // show resize cursor + } else { + change_cursor(FL_CURSOR_DEFAULT); // normal cursor + } + ret = 1; + break; + + case FL_ENTER: // See FLTK event docs on the FL_ENTER widget + if (!ret) take_focus(); + ret = 1; + //FALLTHROUGH + + case FL_LEAVE: // We want to track the mouse if resizing is allowed. + if ( resizeflag ) { + ret = 1; + } + if ( event == FL_LEAVE ) { + _stop_auto_drag(); + change_cursor(FL_CURSOR_DEFAULT); + } + break; + + case FL_FOCUS: + Fl::focus(this); + //FALLTHROUGH + + case FL_UNFOCUS: + _stop_auto_drag(); + ret = 1; + break; + + case FL_KEYBOARD: { + ret = 0; + int is_row = select_row; + int is_col = select_col; + switch(Fl::event_key()) { + case FL_Home: + ret = move_cursor(0, -1000000); + break; + case FL_End: + ret = move_cursor(0, 1000000); + break; + case FL_Page_Up: + ret = move_cursor(-(botrow - toprow - 1), 0); + break; + case FL_Page_Down: + ret = move_cursor(botrow - toprow - 1 , 0); + break; + case FL_Left: + ret = move_cursor(0, -1); + break; + case FL_Right: + ret = move_cursor(0, 1); + break; + case FL_Up: + ret = move_cursor(-1, 0); + break; + case FL_Down: + ret = move_cursor(1, 0); + break; + } + if (ret && Fl::focus() != this) { + do_callback(CONTEXT_TABLE, -1, -1); + take_focus(); + } + //if (!ret && Fl_Widget::callback() && when() & FL_WHEN_NOT_CHANGED ) + if ( Fl_Widget::callback() && + ( + ( !ret && when() & FL_WHEN_NOT_CHANGED ) || + ( is_row!= select_row || is_col!= select_col ) + ) + ) { + do_callback(CONTEXT_CELL, select_row, select_col); + //damage_zone(current_row, current_col, select_row, select_col); + ret = 1; + } + break; + } + + default: + change_cursor(FL_CURSOR_DEFAULT); + break; + } + return(ret); +} + +// Resize FLTK override +// Handle resize events if user resizes parent window. +// +void Fl_Table::resize(int X, int Y, int W, int H) { + // Tell group to resize, and recalc our own widget as well + Fl_Group::resize(X, Y, W, H); + table_resized(); + redraw(); +} + +// Draw a cell +void Fl_Table::_redraw_cell(TableContext context, int r, int c) { + if ( r < 0 || c < 0 ) return; + int X,Y,W,H; + find_cell(context, r, c, X, Y, W, H); // find positions of cell + draw_cell(context, r, c, X, Y, W, H); // call users' function to draw it +} + +int Fl_Table::is_selected(int r, int c) { + int s_left, s_right, s_top, s_bottom; + + if (select_col > current_col) { + s_left = current_col; + s_right = select_col; + } else { + s_right = current_col; + s_left = select_col; + } + if (select_row > current_row) { + s_top = current_row; + s_bottom = select_row; + } else { + s_bottom = current_row; + s_top = select_row; + } + if (r >= s_top && r <= s_bottom && c >= s_left && c <= s_right) { + return 1; + } + return 0; +} + +void Fl_Table::get_selection(int& s_top, int& s_left, int& s_bottom, int& s_right) { + if (select_col > current_col) { + s_left = current_col; + s_right = select_col; + } else { + s_right = current_col; + s_left = select_col; + } + if (select_row > current_row) { + s_top = current_row; + s_bottom = select_row; + } else { + s_bottom = current_row; + s_top = select_row; + } +} + +void Fl_Table::set_selection(int s_top, int s_left, int s_bottom, int s_right) { + damage_zone(current_row, current_col, select_row, select_col); + current_col = s_left; + current_row = s_top; + select_col = s_right; + select_row = s_bottom; + damage_zone(current_row, current_col, select_row, select_col); +} + +// Draw the entire Fl_Table +// Override the draw() routine to draw the table. +// Then tell the group to draw over us. +// +void Fl_Table::draw() { + draw_cell(CONTEXT_STARTPAGE, 0, 0, // let user's drawing routine + tix, tiy, tiw, tih); // prep new page + + // Let fltk widgets draw themselves first. Do this after + // draw_cell(CONTEXT_STARTPAGE) in case user moves widgets around. + // Use window 'inner' clip to prevent drawing into table border. + // (unfortunately this clips FLTK's border, so we must draw it explicity below) + // + fl_push_clip(wix, wiy, wiw, wih); + { + Fl_Group::draw(); + } + fl_pop_clip(); + + // Explicitly draw border around widget, if any + draw_box(box(), x(), y(), w(), h(), color()); + + // If Fl_Scroll 'table' is hidden, draw its box + // Do this after Fl_Group::draw() so we draw over scrollbars + // that leak around the border. + // + if ( ! table->visible() ) { + if ( damage() & FL_DAMAGE_ALL || damage() & FL_DAMAGE_CHILD ) { + draw_box(table->box(), tox, toy, tow, toh, table->color()); + } + } + // Clip all further drawing to the inner widget dimensions + fl_push_clip(wix, wiy, wiw, wih); + { + // Only redraw a few cells? + if ( ! ( damage() & FL_DAMAGE_ALL ) && _redraw_leftcol != -1 ) { + fl_push_clip(tix, tiy, tiw, tih); + for ( int c = _redraw_leftcol; c <= _redraw_rightcol; c++ ) { + for ( int r = _redraw_toprow; r <= _redraw_botrow; r++ ) { + _redraw_cell(CONTEXT_CELL, r, c); + } + } + fl_pop_clip(); + } + if ( damage() & FL_DAMAGE_ALL ) { + int X,Y,W,H; + // Draw row headers, if any + if ( row_header() ) { + get_bounds(CONTEXT_ROW_HEADER, X, Y, W, H); + fl_push_clip(X,Y,W,H); + for ( int r = toprow; r <= botrow; r++ ) { + _redraw_cell(CONTEXT_ROW_HEADER, r, 0); + } + fl_pop_clip(); + } + // Draw column headers, if any + if ( col_header() ) { + get_bounds(CONTEXT_COL_HEADER, X, Y, W, H); + fl_push_clip(X,Y,W,H); + for ( int c = leftcol; c <= rightcol; c++ ) { + _redraw_cell(CONTEXT_COL_HEADER, 0, c); + } + fl_pop_clip(); + } + // Draw all cells. + // This includes cells partially obscured off edges of table. + // No longer do this last; you might think it would be nice + // to draw over dead zones, but on redraws it flickers. Avoid + // drawing over deadzones; prevent deadzones by sizing columns. + // + fl_push_clip(tix, tiy, tiw, tih); { + for ( int r = toprow; r <= botrow; r++ ) { + for ( int c = leftcol; c <= rightcol; c++ ) { + _redraw_cell(CONTEXT_CELL, r, c); + } + } + } + fl_pop_clip(); + // Draw little rectangle in corner of headers + if ( row_header() && col_header() ) { + fl_rectf(wix, wiy, row_header_width(), col_header_height(), color()); + } + + // Table has a boxtype? Close those few dead pixels + if ( table->box() ) { + if ( col_header() ) { + fl_rectf(tox, wiy, Fl::box_dx(table->box()), col_header_height(), color()); + } + if ( row_header() ) { + fl_rectf(wix, toy, row_header_width(), Fl::box_dx(table->box()), color()); + } + } + + // Table width smaller than window? Fill remainder with rectangle + if ( table_w < tiw ) { + fl_rectf(tix + table_w, tiy, tiw - table_w, tih, color()); + // Col header? fill that too + if ( col_header() ) { + fl_rectf(tix + table_w, + wiy, + // get that corner just right.. + (tiw - table_w + Fl::box_dw(table->box()) - + Fl::box_dx(table->box())), + col_header_height(), + color()); + } + } + // Table height smaller than window? Fill remainder with rectangle + if ( table_h < tih ) { + fl_rectf(tix, tiy + table_h, tiw, tih - table_h, color()); + if ( row_header() ) { + // NOTE: + // Careful with that lower corner; don't use tih; when eg. + // table->box(FL_THIN_UPFRAME) and hscrollbar hidden, + // leaves a row of dead pixels. + // + fl_rectf(wix, tiy + table_h, row_header_width(), + (wiy+wih) - (tiy+table_h) - + ( hscrollbar->visible() ? SCROLLBAR_SIZE : 0), + color()); + } + } + } + // Both scrollbars? Draw little box in lower right + if ( vscrollbar->visible() && hscrollbar->visible() ) { + fl_rectf(vscrollbar->x(), hscrollbar->y(), + vscrollbar->w(), hscrollbar->h(), color()); + } + draw_cell(CONTEXT_ENDPAGE, 0, 0, // let user's drawing + tix, tiy, tiw, tih); // routines cleanup + + _redraw_leftcol = _redraw_rightcol = _redraw_toprow = _redraw_botrow = -1; + } + fl_pop_clip(); +} diff --git a/src/Fl_Table_Row.cxx b/src/Fl_Table_Row.cxx new file mode 100644 index 000000000..85fa5aac5 --- /dev/null +++ b/src/Fl_Table_Row.cxx @@ -0,0 +1,317 @@ +// +// Fl_Table_Row -- A row oriented table widget +// +// A class specializing in a table of rows. +// Handles row-specific selection behavior. +// +// Copyright 2002 by Greg Ercolano. +// +// 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 Row headings (only column headings supported currently) +// + +#include <stdio.h> // for debugging +#include <FL/Fl.H> +#include <FL/fl_draw.H> +#include <FL/Fl_Table_Row.H> + +// Is row selected? +int Fl_Table_Row::row_selected(int row) { + if ( row < 0 || row >= rows() ) return(-1); + return(_rowselect[row]); +} + +// Change row selection type +void Fl_Table_Row::type(TableRowSelectMode val) { + _selectmode = val; + switch ( _selectmode ) { + case SELECT_NONE: + { + for ( int row=0; row<rows(); row++ ) { + _rowselect[row] = 0; + } + redraw(); + break; + } + case SELECT_SINGLE: + { + int count = 0; + for ( int row=0; row<rows(); row++ ) { + if ( _rowselect[row] ) { + if ( ++count > 1 ) { // only one allowed + _rowselect[row] = 0; + } + } + } + redraw(); + break; + } + case SELECT_MULTI: + break; + } +} + +// Change selection state for row +// +// flag: +// 0 - clear selection +// 1 - set selection +// 2 - toggle selection +// +// Returns: +// 0 - selection state did not change +// 1 - selection state changed +// -1 - row out of range or incorrect selection mode +// +int Fl_Table_Row::select_row(int row, int flag) { + int ret = 0; + if ( row < 0 || row >= rows() ) { return(-1); } + switch ( _selectmode ) { + case SELECT_NONE: + return(-1); + + case SELECT_SINGLE: + { + int oldval; + for ( int t=0; t<rows(); t++ ) { + if ( t == row ) { + oldval = _rowselect[row]; + if ( flag == 2 ) { _rowselect[row] ^= 1; } + else { _rowselect[row] = flag; } + if ( oldval != _rowselect[row] ) { + redraw_range(row, row, leftcol, rightcol); + ret = 1; + } + } + else if ( _rowselect[t] ) { + _rowselect[t] = 0; + redraw_range(t, t, leftcol, rightcol); + } + } + break; + } + + case SELECT_MULTI: + { + int oldval = _rowselect[row]; + if ( flag == 2 ) { _rowselect[row] ^= 1; } + else { _rowselect[row] = flag; } + if ( _rowselect[row] != oldval ) { // select state changed? + if ( row >= toprow && row <= botrow ) { // row visible? + // Extend partial redraw range + redraw_range(row, row, leftcol, rightcol); + } + ret = 1; + } + } + } + return(ret); +} + +// Select all rows to a known state +void Fl_Table_Row::select_all_rows(int flag) { + switch ( _selectmode ) { + case SELECT_NONE: + return; + + case SELECT_SINGLE: + if ( flag != 0 ) return; + //FALLTHROUGH + + case SELECT_MULTI: + { + char changed = 0; + if ( flag == 2 ) { + for ( int row=0; row<(int)_rowselect.size(); row++ ) { + _rowselect[row] ^= 1; + } + changed = 1; + } else { + for ( int row=0; row<(int)_rowselect.size(); row++ ) { + changed |= (_rowselect[row] != flag)?1:0; + _rowselect[row] = flag; + } + } + if ( changed ) { + redraw(); + } + } + } +} + +// Set number of rows +void Fl_Table_Row::rows(int val) { + Fl_Table::rows(val); + while ( val > (int)_rowselect.size() ) { _rowselect.push_back(0); } // enlarge + while ( val < (int)_rowselect.size() ) { _rowselect.pop_back(); } // shrink +} + +// #include "eventnames.h" // debugging +// #include <stdio.h> + +// Handle events +int Fl_Table_Row::handle(int event) { + + // fprintf(stderr, "** EVENT: %s: EVENT XY=%d,%d\n", + // eventnames[event], Fl::event_x(), Fl::event_y()); // debugging + + // Let base class handle event + int ret = Fl_Table::handle(event); + + // The following code disables cell selection.. why was it added? -erco 05/18/03 + // if ( ret ) { _last_y = Fl::event_y(); return(1); } // base class 'handled' it (eg. column resize) + + int shiftstate = (Fl::event_state() & FL_CTRL) ? FL_CTRL : + (Fl::event_state() & FL_SHIFT) ? FL_SHIFT : 0; + + // Which row/column are we over? + int R, C; // row/column being worked on + ResizeFlag resizeflag; // which resizing area are we over? (0=none) + TableContext context = cursor2rowcol(R, C, resizeflag); + switch ( event ) { + case FL_PUSH: + if ( Fl::event_button() == 1 ) { + _last_push_x = Fl::event_x(); // save regardless of context + _last_push_y = Fl::event_y(); // " " + + // Handle selection in table. + // Select cell under cursor, and enable drag selection mode. + // + if ( context == CONTEXT_CELL ) { + // Ctrl key? Toggle selection state + switch ( shiftstate ) { + case FL_CTRL: + select_row(R, 2); // toggle + break; + + case FL_SHIFT: + { + select_row(R, 1); + if ( _last_row > -1 ) { + int srow = R, erow = _last_row; + if ( srow > erow ) { + srow = _last_row; + erow = R; + } + for ( int row = srow; row <= erow; row++ ) { + select_row(row, 1); + } + } + break; + } + + default: + select_all_rows(0); // clear all previous selections + select_row(R, 1); + break; + } + + _last_row = R; + _dragging_select = 1; + ret = 1; // FL_PUSH handled (ensures FL_DRAG will be sent) + // redraw(); // redraw() handled by select_row() + } + } + break; + + case FL_DRAG: + { + if ( _dragging_select ) { + // Dragged off table edges? Handle scrolling + int offtop = toy - _last_y; // >0 if off top of table + int offbot = _last_y - (toy + toh); // >0 if off bottom of table + + if ( offtop > 0 && row_position() > 0 ) { + // Only scroll in upward direction + int diff = _last_y - Fl::event_y(); + if ( diff < 1 ) { + ret = 1; + break; + } + row_position(row_position() - diff); + context = CONTEXT_CELL; C = 0; R = row_position(); // HACK: fake it + if ( R < 0 || R > rows() ) { ret = 1; break; } // HACK: ugly + } + else if ( offbot > 0 && botrow < rows() ) { + // Only scroll in downward direction + int diff = Fl::event_y() - _last_y; + if ( diff < 1 ) { + ret = 1; + break; + } + row_position(row_position() + diff); + context = CONTEXT_CELL; C = 0; R = botrow; // HACK: fake it + if ( R < 0 || R > rows() ) { ret = 1; break; } // HACK: ugly + } + if ( context == CONTEXT_CELL ) { + switch ( shiftstate ) { + case FL_CTRL: + if ( R != _last_row ) { // toggle if dragged to new row + select_row(R, 2); // 2=toggle + } + break; + + case FL_SHIFT: + default: + select_row(R, 1); + if ( _last_row > -1 ) { + int srow = R, erow = _last_row; + if ( srow > erow ) { + srow = _last_row; + erow = R; + } + for ( int row = srow; row <= erow; row++ ) { + select_row(row, 1); + } + } + break; + } + ret = 1; // drag handled + _last_row = R; + } + } + break; + } + + case FL_RELEASE: + if ( Fl::event_button() == 1 ) { + _dragging_select = 0; + ret = 1; // release handled + // Clicked off edges of data table? + // A way for user to clear the current selection. + // + int databot = tiy + table_h, + dataright = tix + table_w; + if ( + ( _last_push_x > dataright && Fl::event_x() > dataright ) || + ( _last_push_y > databot && Fl::event_y() > databot ) + ) { + select_all_rows(0); // clear previous selections + } + } + break; + + default: + break; + } + _last_y = Fl::event_y(); + return(ret); +} diff --git a/src/Fl_Tabs.o b/src/Fl_Tabs.o Binary files differnew file mode 100644 index 000000000..c029f300a --- /dev/null +++ b/src/Fl_Tabs.o diff --git a/src/Makefile b/src/Makefile index 2e050e14f..dce506936 100644 --- a/src/Makefile +++ b/src/Makefile @@ -78,6 +78,8 @@ CPPFILES = \ Fl_Shared_Image.cxx \ Fl_Single_Window.cxx \ Fl_Slider.cxx \ + Fl_Table.cxx \ + Fl_Table_Row.cxx \ Fl_Tabs.cxx \ Fl_Text_Buffer.cxx \ Fl_Text_Display.cxx \ diff --git a/test/Makefile b/test/Makefile index eb249b433..1901fc9ad 100644 --- a/test/Makefile +++ b/test/Makefile @@ -90,6 +90,7 @@ CPPFILES =\ subwindow.cxx \ sudoku.cxx \ symbols.cxx \ + table.cxx \ tabs.cxx \ threads.cxx \ tile.cxx \ @@ -152,6 +153,7 @@ ALL = \ subwindow$(EXEEXT) \ sudoku$(EXEEXT) \ symbols$(EXEEXT) \ + table$(EXEEXT) \ tabs$(EXEEXT) \ $(THREADS) \ tile$(EXEEXT) \ diff --git a/test/demo.menu b/test/demo.menu index 32f6d08c4..0606d6b02 100644 --- a/test/demo.menu +++ b/test/demo.menu @@ -18,7 +18,9 @@ @x:Fl_Tile:tile @x:Fl_Scroll:scroll @x:Fl_Pack:pack - @x:Fl_Menu:menubar + @x:more...:@xm + @xm:Fl_Menu:menubar + @xm:Fl_Table:table @main:Window\nTests:@w @w:overlay:overlay diff --git a/test/table.cxx b/test/table.cxx new file mode 100644 index 000000000..eab8b69bb --- /dev/null +++ b/test/table.cxx @@ -0,0 +1,493 @@ +// +// exercisetablerow -- Exercise all aspects of the Fl_Table_Row widget +// + +#include <stdio.h> +#include <string.h> +#ifdef _WIN32 +#include <stdlib.h> // atoi +#endif /*_WIN32*/ + +#include <FL/Fl.H> +#include <FL/Fl_Window.H> +#include <FL/Fl_Input.H> +#include <FL/Fl_Check_Button.H> +#include <FL/Fl_Choice.H> +#include <FL/fl_draw.H> +#include <FL/fl_ask.H> +#include <FL/Fl_Table_Row.H> + +// Simple demonstration class to derive from Fl_Table_Row +class DemoTable : public Fl_Table_Row +{ +private: + Fl_Color cell_bgcolor; // color of cell's bg color + Fl_Color cell_fgcolor; // color of cell's fg color + +protected: + void draw_cell(TableContext context, // table cell drawing + int R=0, int C=0, int X=0, int Y=0, int W=0, int H=0); + static void event_callback(Fl_Widget*, void*); + void event_callback2(); // callback for table events + +public: + DemoTable(int x, int y, int w, int h, const char *l=0) : Fl_Table_Row(x,y,w,h,l) + { + cell_bgcolor = FL_WHITE; + cell_fgcolor = FL_BLACK; + callback(&event_callback, (void*)this); + end(); + } + ~DemoTable() { } + Fl_Color GetCellFGColor() const { return(cell_fgcolor); } + Fl_Color GetCellBGColor() const { return(cell_bgcolor); } + void SetCellFGColor(Fl_Color val) { cell_fgcolor = val; } + void SetCellBGColor(Fl_Color val) { cell_bgcolor = val; } +}; + +// Handle drawing all cells in table +void DemoTable::draw_cell(TableContext context, + int R, int C, int X, int Y, int W, int H) +{ + static char s[40]; + sprintf(s, "%d/%d", R, C); // text for each cell + + switch ( context ) + { + case CONTEXT_STARTPAGE: + fl_font(FL_HELVETICA, 16); + return; + + case CONTEXT_COL_HEADER: + fl_push_clip(X, Y, W, H); + { + fl_draw_box(FL_THIN_UP_BOX, X, Y, W, H, col_header_color()); + fl_color(FL_BLACK); + fl_draw(s, X, Y, W, H, FL_ALIGN_CENTER); + } + fl_pop_clip(); + return; + + case CONTEXT_ROW_HEADER: + fl_push_clip(X, Y, W, H); + { + fl_draw_box(FL_THIN_UP_BOX, X, Y, W, H, row_header_color()); + fl_color(FL_BLACK); + fl_draw(s, X, Y, W, H, FL_ALIGN_CENTER); + } + fl_pop_clip(); + return; + + case CONTEXT_CELL: + { + fl_push_clip(X, Y, W, H); + { + // BG COLOR + fl_color( row_selected(R) ? selection_color() : cell_bgcolor); + fl_rectf(X, Y, W, H); + + // TEXT + fl_color(cell_fgcolor); + fl_draw(s, X, Y, W, H, FL_ALIGN_CENTER); + + // BORDER + fl_color(color()); + fl_rect(X, Y, W, H); + } + fl_pop_clip(); + return; + } + + case CONTEXT_TABLE: + fprintf(stderr, "TABLE CONTEXT CALLED\n"); + return; + + case CONTEXT_ENDPAGE: + case CONTEXT_RC_RESIZE: + case CONTEXT_NONE: + return; + } +} + +// Callback whenever someone clicks on different parts of the table +void DemoTable::event_callback(Fl_Widget*, void *data) +{ + DemoTable *o = (DemoTable*)data; + o->event_callback2(); +} + +void DemoTable::event_callback2() +{ + int R = callback_row(), + C = callback_col(); + TableContext context = callback_context(); + printf("'%s' callback: ", (label() ? label() : "?")); + printf("Row=%d Col=%d Context=%d Event=%d InteractiveResize? %d\n", + R, C, (int)context, (int)Fl::event(), (int)is_interactive_resize()); +} + +// GLOBAL TABLE WIDGET +static DemoTable *G_table = 0; + +void setrows_cb(Fl_Widget*, void *data) +{ + Fl_Input *in = (Fl_Input*)data; + int rows = atoi(in->value()); + if ( rows < 0 ) rows = 0; + G_table->rows(rows); +} + +void setcols_cb(Fl_Widget*, void *data) +{ + Fl_Input *in = (Fl_Input*)data; + int cols = atoi(in->value()); + if ( cols < 0 ) cols = 0; + G_table->cols(cols); +} + +void setrowheader_cb(Fl_Widget*, void *data) +{ + Fl_Check_Button *check = (Fl_Check_Button*)data; + G_table->row_header(check->value()); +} + +void setcolheader_cb(Fl_Widget*, void *data) +{ + Fl_Check_Button *check = (Fl_Check_Button*)data; + G_table->col_header(check->value()); +} + +void setrowresize_cb(Fl_Widget*, void *data) +{ + Fl_Check_Button *check = (Fl_Check_Button*)data; + G_table->row_resize(check->value()); +} + +void setcolresize_cb(Fl_Widget*, void *data) +{ + Fl_Check_Button *check = (Fl_Check_Button*)data; + G_table->col_resize(check->value()); +} + +void setpositionrow_cb(Fl_Widget *w, void *data) +{ + Fl_Input *in = (Fl_Input*)data; + int toprow = atoi(in->value()); + if ( toprow < 0 || toprow >= G_table->rows() ) + { fl_alert("Must be in range 0 thru #rows"); } + else + { G_table->row_position(toprow); } +} + +void setpositioncol_cb(Fl_Widget *w, void *data) +{ + Fl_Input *in = (Fl_Input*)data; + int leftcol = atoi(in->value()); + if ( leftcol < 0 || leftcol >= G_table->cols() ) + { fl_alert("Must be in range 0 thru #cols"); } + else + { G_table->col_position(leftcol); } +} + +void setrowheaderwidth_cb(Fl_Widget *w, void *data) +{ + Fl_Input *in = (Fl_Input*)data; + int val = atoi(in->value()); + if ( val < 1 ) { val = 1; in->value("1"); } + G_table->row_header_width(val); +} + +void setcolheaderheight_cb(Fl_Widget *w, void *data) +{ + Fl_Input *in = (Fl_Input*)data; + int val = atoi(in->value()); + if ( val < 1 ) { val = 1; in->value("1"); } + G_table->col_header_height(val); +} + +void setrowheadercolor_cb(Fl_Widget *w, void *data) +{ + Fl_Input *in = (Fl_Input*)data; + int val = atoi(in->value()); + if ( val < 0 ) { fl_alert("Must be a color >0"); } + else { G_table->row_header_color(Fl_Color(val)); } +} + +void setcolheadercolor_cb(Fl_Widget *w, void *data) +{ + Fl_Input *in = (Fl_Input*)data; + int val = atoi(in->value()); + if ( val < 0 ) { fl_alert("Must be a color >0"); } + else { G_table->col_header_color(Fl_Color(val)); } +} + +void setrowheightall_cb(Fl_Widget *w, void *data) +{ + Fl_Input *in = (Fl_Input*)data; + int val = atoi(in->value()); + if ( val < 0 ) { val = 0; in->value("0"); } + G_table->row_height_all(val); +} + +void setcolwidthall_cb(Fl_Widget *w, void *data) +{ + Fl_Input *in = (Fl_Input*)data; + int val = atoi(in->value()); + if ( val < 0 ) { val = 0; in->value("0"); } + G_table->col_width_all(val); +} + +void settablecolor_cb(Fl_Widget *w, void *data) +{ + Fl_Input *in = (Fl_Input*)data; + int val = atoi(in->value()); + if ( val < 0 ) { fl_alert("Must be a color >0"); } + else { G_table->color(Fl_Color(val)); } + G_table->redraw(); +} + +void setcellfgcolor_cb(Fl_Widget *w, void *data) +{ + Fl_Input *in = (Fl_Input*)data; + int val = atoi(in->value()); + if ( val < 0 ) { fl_alert("Must be a color >0"); } + else { G_table->SetCellFGColor(Fl_Color(val)); } + G_table->redraw(); +} + +void setcellbgcolor_cb(Fl_Widget *w, void *data) +{ + Fl_Input *in = (Fl_Input*)data; + int val = atoi(in->value()); + if ( val < 0 ) { fl_alert("Must be a color >0"); } + else { G_table->SetCellBGColor(Fl_Color(val)); } + G_table->redraw(); +} + +char *itoa(int val) +{ + static char s[80]; + sprintf(s, "%d", val); + return(s); +} + +void tablebox_choice_cb(Fl_Widget *w, void *data) +{ + G_table->table_box((Fl_Boxtype)(int)data); + G_table->redraw(); +} + +void widgetbox_choice_cb(Fl_Widget *w, void *data) +{ + G_table->box((Fl_Boxtype)(int)data); + G_table->resize(G_table->x(), G_table->y(), G_table->w(), G_table->h()); +} + +void type_choice_cb(Fl_Widget *w, void *data) +{ + G_table->type((Fl_Table_Row::TableRowSelectMode)(int)data); +} + +Fl_Menu_Item tablebox_choices[] = { + {"No Box", 0, tablebox_choice_cb, (void*)FL_NO_BOX }, + {"Flat Box", 0, tablebox_choice_cb, (void*)FL_FLAT_BOX }, + {"Up Box", 0, tablebox_choice_cb, (void*)FL_UP_BOX }, + {"Down Box", 0, tablebox_choice_cb, (void*)FL_DOWN_BOX }, + {"Up Frame", 0, tablebox_choice_cb, (void*)FL_UP_FRAME }, + {"Down Frame", 0, tablebox_choice_cb, (void*)FL_DOWN_FRAME }, + {"Thin Up Box", 0, tablebox_choice_cb, (void*)FL_THIN_UP_BOX }, + {"Thin Down Box", 0, tablebox_choice_cb, (void*)FL_THIN_DOWN_BOX }, + {"Thin Up Frame", 0, tablebox_choice_cb, (void*)FL_THIN_UP_FRAME }, + {"Thin Down Frame",0, tablebox_choice_cb, (void*)FL_THIN_DOWN_FRAME }, + {"Engraved Box", 0, tablebox_choice_cb, (void*)FL_ENGRAVED_BOX }, + {"Embossed Box", 0, tablebox_choice_cb, (void*)FL_EMBOSSED_BOX }, + {"Engraved Frame", 0, tablebox_choice_cb, (void*)FL_ENGRAVED_FRAME }, + {"Embossed Frame", 0, tablebox_choice_cb, (void*)FL_EMBOSSED_FRAME }, + {"Border Box", 0, tablebox_choice_cb, (void*)FL_BORDER_BOX }, + {"Shadow Box", 0, tablebox_choice_cb, (void*)FL_SHADOW_BOX }, + {"Border Frame", 0, tablebox_choice_cb, (void*)FL_BORDER_FRAME }, + {0} +}; + +Fl_Menu_Item widgetbox_choices[] = { + {"No Box", 0, widgetbox_choice_cb, (void*)FL_NO_BOX }, +//{"Flat Box", 0, widgetbox_choice_cb, (void*)FL_FLAT_BOX }, +//{"Up Box", 0, widgetbox_choice_cb, (void*)FL_UP_BOX }, +//{"Down Box", 0, widgetbox_choice_cb, (void*)FL_DOWN_BOX }, + {"Up Frame", 0, widgetbox_choice_cb, (void*)FL_UP_FRAME }, + {"Down Frame", 0, widgetbox_choice_cb, (void*)FL_DOWN_FRAME }, +//{"Thin Up Box", 0, widgetbox_choice_cb, (void*)FL_THIN_UP_BOX }, +//{"Thin Down Box", 0, widgetbox_choice_cb, (void*)FL_THIN_DOWN_BOX }, + {"Thin Up Frame", 0, widgetbox_choice_cb, (void*)FL_THIN_UP_FRAME }, + {"Thin Down Frame",0, widgetbox_choice_cb, (void*)FL_THIN_DOWN_FRAME }, +//{"Engraved Box", 0, widgetbox_choice_cb, (void*)FL_ENGRAVED_BOX }, +//{"Embossed Box", 0, widgetbox_choice_cb, (void*)FL_EMBOSSED_BOX }, + {"Engraved Frame", 0, widgetbox_choice_cb, (void*)FL_ENGRAVED_FRAME }, + {"Embossed Frame", 0, widgetbox_choice_cb, (void*)FL_EMBOSSED_FRAME }, +//{"Border Box", 0, widgetbox_choice_cb, (void*)FL_BORDER_BOX }, +//{"Shadow Box", 0, widgetbox_choice_cb, (void*)FL_SHADOW_BOX }, + {"Border Frame", 0, widgetbox_choice_cb, (void*)FL_BORDER_FRAME }, + {0} +}; + +Fl_Menu_Item type_choices[] = { + {"SelectNone", 0, type_choice_cb, (void*)Fl_Table_Row::SELECT_NONE }, + {"SelectSingle", 0, type_choice_cb, (void*)Fl_Table_Row::SELECT_SINGLE }, + {"SelectMulti", 0, type_choice_cb, (void*)Fl_Table_Row::SELECT_MULTI }, + {0} +}; + +int main(int argc, char **argv) +{ + Fl_Window win(900, 730); + + G_table = new DemoTable(20, 20, 860, 460, "Demo"); + G_table->selection_color(FL_YELLOW); + G_table->when(FL_WHEN_RELEASE|FL_WHEN_CHANGED); + G_table->table_box(FL_NO_BOX); + G_table->col_resize_min(4); + G_table->row_resize_min(4); + + // ROWS + G_table->row_header(1); + G_table->row_header_width(60); + G_table->row_resize(1); + G_table->rows(500); + G_table->row_height_all(20); + + // COLS + G_table->cols(500); + G_table->col_header(1); + G_table->col_header_height(25); + G_table->col_resize(1); + G_table->col_width_all(80); + + // Add children to window + win.begin(); + + // ROW + Fl_Input setrows(150, 500, 120, 25, "Rows"); + setrows.labelsize(12); + setrows.value(itoa(G_table->rows())); + setrows.callback(setrows_cb, (void*)&setrows); + setrows.when(FL_WHEN_RELEASE); + + Fl_Input rowheightall(400, 500, 120, 25, "Row Height"); + rowheightall.labelsize(12); + rowheightall.value(itoa(G_table->row_height(0))); + rowheightall.callback(setrowheightall_cb, (void*)&rowheightall); + rowheightall.when(FL_WHEN_RELEASE); + + Fl_Input positionrow(650, 500, 120, 25, "Row Position"); + positionrow.labelsize(12); + positionrow.value("1"); + positionrow.callback(setpositionrow_cb, (void*)&positionrow); + positionrow.when(FL_WHEN_RELEASE); + + // COL + Fl_Input setcols(150, 530, 120, 25, "Cols"); + setcols.labelsize(12); + setcols.value(itoa(G_table->cols())); + setcols.callback(setcols_cb, (void*)&setcols); + setcols.when(FL_WHEN_RELEASE); + + Fl_Input colwidthall(400, 530, 120, 25, "Col Width"); + colwidthall.labelsize(12); + colwidthall.value(itoa(G_table->col_width(0))); + colwidthall.callback(setcolwidthall_cb, (void*)&colwidthall); + colwidthall.when(FL_WHEN_RELEASE); + + Fl_Input positioncol(650, 530, 120, 25, "Col Position"); + positioncol.labelsize(12); + positioncol.value("1"); + positioncol.callback(setpositioncol_cb, (void*)&positioncol); + positioncol.when(FL_WHEN_RELEASE); + + // ROW HEADER + Fl_Input rowheaderwidth(150, 570, 120, 25, "Row Header Width"); + rowheaderwidth.labelsize(12); + rowheaderwidth.value(itoa(G_table->row_header_width())); + rowheaderwidth.callback(setrowheaderwidth_cb, (void*)&rowheaderwidth); + rowheaderwidth.when(FL_WHEN_RELEASE); + + Fl_Input rowheadercolor(400, 570, 120, 25, "Row Header Color"); + rowheadercolor.labelsize(12); + rowheadercolor.value(itoa((int)G_table->row_header_color())); + rowheadercolor.callback(setrowheadercolor_cb, (void*)&rowheadercolor); + rowheadercolor.when(FL_WHEN_RELEASE); + + Fl_Check_Button rowheader(550, 570, 120, 25, "Row Headers?"); + rowheader.labelsize(12); + rowheader.callback(setrowheader_cb, (void*)&rowheader); + rowheader.value(G_table->row_header() ? 1 : 0); + + Fl_Check_Button rowresize(700, 570, 120, 25, "Row Resize?"); + rowresize.labelsize(12); + rowresize.callback(setrowresize_cb, (void*)&rowresize); + rowresize.value(G_table->row_resize() ? 1 : 0); + + // COL HEADER + Fl_Input colheaderheight(150, 600, 120, 25, "Col Header Height"); + colheaderheight.labelsize(12); + colheaderheight.value(itoa(G_table->col_header_height())); + colheaderheight.callback(setcolheaderheight_cb, (void*)&colheaderheight); + colheaderheight.when(FL_WHEN_RELEASE); + + Fl_Input colheadercolor(400, 600, 120, 25, "Col Header Color"); + colheadercolor.labelsize(12); + colheadercolor.value(itoa((int)G_table->col_header_color())); + colheadercolor.callback(setcolheadercolor_cb, (void*)&colheadercolor); + colheadercolor.when(FL_WHEN_RELEASE); + + Fl_Check_Button colheader(550, 600, 120, 25, "Col Headers?"); + colheader.labelsize(12); + colheader.callback(setcolheader_cb, (void*)&colheader); + colheader.value(G_table->col_header() ? 1 : 0); + + Fl_Check_Button colresize(700, 600, 120, 25, "Col Resize?"); + colresize.labelsize(12); + colresize.callback(setcolresize_cb, (void*)&colresize); + colresize.value(G_table->col_resize() ? 1 : 0); + + Fl_Choice tablebox(150, 640, 120, 25, "Table Box"); + tablebox.labelsize(12); + tablebox.textsize(12); + tablebox.menu(tablebox_choices); + tablebox.value(0); + + Fl_Choice widgetbox(150, 670, 120, 25, "Widget Box"); + widgetbox.labelsize(12); + widgetbox.textsize(12); + widgetbox.menu(widgetbox_choices); + widgetbox.value(2); // down frame + + Fl_Input tablecolor(400, 640, 120, 25, "Table Color"); + tablecolor.labelsize(12); + tablecolor.value(itoa((int)G_table->color())); + tablecolor.callback(settablecolor_cb, (void*)&tablecolor); + tablecolor.when(FL_WHEN_RELEASE); + + Fl_Input cellbgcolor(400, 670, 120, 25, "Cell BG Color"); + cellbgcolor.labelsize(12); + cellbgcolor.value(itoa((int)G_table->GetCellBGColor())); + cellbgcolor.callback(setcellbgcolor_cb, (void*)&cellbgcolor); + cellbgcolor.when(FL_WHEN_RELEASE); + + Fl_Input cellfgcolor(400, 700, 120, 25, "Cell FG Color"); + cellfgcolor.labelsize(12); + cellfgcolor.value(itoa((int)G_table->GetCellFGColor())); + cellfgcolor.callback(setcellfgcolor_cb, (void*)&cellfgcolor); + cellfgcolor.when(FL_WHEN_RELEASE); + + Fl_Choice type(650, 640, 120, 25, "Type"); + type.labelsize(12); + type.textsize(12); + type.menu(type_choices); + type.value(2); + + win.end(); + win.resizable(*G_table); + win.show(argc, argv); + + return(Fl::run()); +} |
