summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Melcher <fltk@matthiasm.com>2009-11-14 13:05:37 +0000
committerMatthias Melcher <fltk@matthiasm.com>2009-11-14 13:05:37 +0000
commitfefa82e0b1e98d784455148940d84b454a128aad (patch)
tree9d888cb9bc402708e0ad035a1c55d4b7703376a4
parent1238d1576b8e2bc15c097b23ebe0fdc4d3cdb527 (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.H451
-rw-r--r--FL/Fl_Table_Row.H150
-rw-r--r--ide/Xcode3.1/FLTK.xcodeproj/project.pbxproj170
-rw-r--r--ide/Xcode3.1/plists/table-Info.plist20
-rw-r--r--src/Fl_Table.cxx1218
-rw-r--r--src/Fl_Table_Row.cxx317
-rw-r--r--src/Fl_Tabs.obin0 -> 51336 bytes
-rw-r--r--src/Makefile2
-rw-r--r--test/Makefile2
-rw-r--r--test/demo.menu4
-rw-r--r--test/table.cxx493
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
new file mode 100644
index 000000000..c029f300a
--- /dev/null
+++ b/src/Fl_Tabs.o
Binary files differ
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());
+}