diff options
| author | Matthias Melcher <fltk@matthiasm.com> | 2009-11-14 13:05:37 +0000 |
|---|---|---|
| committer | Matthias Melcher <fltk@matthiasm.com> | 2009-11-14 13:05:37 +0000 |
| commit | fefa82e0b1e98d784455148940d84b454a128aad (patch) | |
| tree | 9d888cb9bc402708e0ad035a1c55d4b7703376a4 /src | |
| parent | 1238d1576b8e2bc15c097b23ebe0fdc4d3cdb527 (diff) | |
Adding Gerg's Fl_Table widget to FLTK 1.3 with kind permission. Documentation not yet added Support for both Visual Studio IDE's not yet added.
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@6929 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
Diffstat (limited to 'src')
| -rw-r--r-- | src/Fl_Table.cxx | 1218 | ||||
| -rw-r--r-- | src/Fl_Table_Row.cxx | 317 | ||||
| -rw-r--r-- | src/Fl_Tabs.o | bin | 0 -> 51336 bytes | |||
| -rw-r--r-- | src/Makefile | 2 |
4 files changed, 1537 insertions, 0 deletions
diff --git a/src/Fl_Table.cxx b/src/Fl_Table.cxx new file mode 100644 index 000000000..32d35cfea --- /dev/null +++ b/src/Fl_Table.cxx @@ -0,0 +1,1218 @@ +// +// Fl_Table -- A table widget +// +// Copyright 2002 by Greg Ercolano. +// Copyright (c) 2004 O'ksi'D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA. +// + +#include <stdio.h> // fprintf +#include <FL/fl_draw.H> +#include <FL/Fl_Table.H> + +#if defined(USE_UTF8) && ( defined(MICROSOFT) || defined(LINUX) ) +#include <FL/fl_utf8.H> // currently only Windows and Linux +#endif + +#define SCROLLBAR_SIZE 16 + +// Scroll display so 'row' is at top +void Fl_Table::row_position(int row) { + if ( _row_position == row ) return; // OPTIMIZATION: no change? avoid redraw + if ( row < 0 ) row = 0; + else if ( row >= rows() ) row = rows() - 1; + if ( table_h <= tih ) return; // don't scroll if table smaller than window + double newtop = row_scroll_position(row); + if ( newtop > vscrollbar->maximum() ) { + newtop = vscrollbar->maximum(); + } + vscrollbar->Fl_Slider::value(newtop); + table_scrolled(); + redraw(); + _row_position = row; // HACK: override what table_scrolled() came up with +} + +// Scroll display so 'col' is at left +void Fl_Table::col_position(int col) { + if ( _col_position == col ) return; // OPTIMIZATION: no change? avoid redraw + if ( col < 0 ) col = 0; + else if ( col >= cols() ) col = cols() - 1; + if ( table_w <= tiw ) return; // don't scroll if table smaller than window + double newleft = col_scroll_position(col); + if ( newleft > hscrollbar->maximum() ) { + newleft = hscrollbar->maximum(); + } + hscrollbar->Fl_Slider::value(newleft); + table_scrolled(); + redraw(); + _col_position = col; // HACK: override what table_scrolled() came up with +} + +// Find scroll position of a row (in pixels) +long Fl_Table::row_scroll_position(int row) { + int startrow = 0; + long scroll = 0; + // OPTIMIZATION: + // Attempt to use precomputed row scroll position + // + if ( toprow_scrollpos != -1 && row >= toprow ) { + scroll = toprow_scrollpos; + startrow = toprow; + } + for ( int t=startrow; t<row; t++ ) { + scroll += row_height(t); + } + return(scroll); +} + +// Find scroll position of a column (in pixels) +long Fl_Table::col_scroll_position(int col) { + int startcol = 0; + long scroll = 0; + // OPTIMIZATION: + // Attempt to use precomputed row scroll position + // + if ( leftcol_scrollpos != -1 && col >= leftcol ) { + scroll = leftcol_scrollpos; + startcol = leftcol; + } + for ( int t=startcol; t<col; t++ ) { + scroll += col_width(t); + } + return(scroll); +} + +// Ctor +Fl_Table::Fl_Table(int X, int Y, int W, int H, const char *l) : Fl_Group(X,Y,W,H,l) { + _rows = 0; + _cols = 0; + _row_header_w = 40; + _col_header_h = 18; + _row_header = 0; + _col_header = 0; + _row_header_color = color(); + _col_header_color = color(); + _row_resize = 0; + _col_resize = 0; + _row_resize_min = 1; + _col_resize_min = 1; + _redraw_toprow = -1; + _redraw_botrow = -1; + _redraw_leftcol = -1; + _redraw_rightcol = -1; + table_w = 0; + table_h = 0; + toprow = 0; + botrow = 0; + leftcol = 0; + rightcol = 0; + toprow_scrollpos = -1; + leftcol_scrollpos = -1; + _last_cursor = FL_CURSOR_DEFAULT; + _resizing_col = -1; + _resizing_row = -1; + _dragging_x = -1; + _dragging_y = -1; + _last_row = -1; + _auto_drag = 0; + current_col = -1; + current_row = -1; + select_row = -1; + select_col = -1; + + box(FL_THIN_DOWN_FRAME); + + vscrollbar = new Fl_Scrollbar(x()+w()-SCROLLBAR_SIZE, y(), + SCROLLBAR_SIZE, h()-SCROLLBAR_SIZE); + vscrollbar->type(FL_VERTICAL); + vscrollbar->callback(scroll_cb, (void*)this); + + hscrollbar = new Fl_Scrollbar(x(), y()+h()-SCROLLBAR_SIZE, + w(), SCROLLBAR_SIZE); + hscrollbar->type(FL_HORIZONTAL); + hscrollbar->callback(scroll_cb, (void*)this); + + table = new Fl_Scroll(x(), y(), w(), h()); + table->box(FL_NO_BOX); + table->type(0); // don't show Fl_Scroll's scrollbars -- use our own + table->hide(); // hide unless children are present + table->end(); + + table_resized(); + redraw(); + + Fl_Group::end(); // end the group's begin() + + table->begin(); // leave with fltk children getting added to the scroll +} + +// Dtor +Fl_Table::~Fl_Table() { + // The parent Fl_Group takes care of destroying scrollbars +} + +// Set height of a row +void Fl_Table::row_height(int row, int height) { + if ( row < 0 ) return; + if ( row < (int)_rowheights.size() && _rowheights[row] == height ) { + return; // OPTIMIZATION: no change? avoid redraw + } + // Add row heights, even if none yet + int now_size = (int)_rowheights.size(); + if ( row >= now_size ) { + _rowheights.size(row); + while (now_size < row) + _rowheights[now_size++] = height; + } + _rowheights[row] = height; + table_resized(); + if ( row <= botrow ) { // OPTIMIZATION: only redraw if onscreen or above screen + redraw(); + } + // ROW RESIZE CALLBACK + if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED ) { + do_callback(CONTEXT_RC_RESIZE, row, 0); + } +} + +// Set width of a column +void Fl_Table::col_width(int col, int width) +{ + if ( col < 0 ) return; + if ( col < (int)_colwidths.size() && _colwidths[col] == width ) { + return; // OPTIMIZATION: no change? avoid redraw + } + // Add column widths, even if none yet + int now_size = (int)_colwidths.size(); + if ( col >= now_size ) { + _colwidths.size(col); + while (now_size < col) { + _colwidths[now_size++] = width; + } + } + _colwidths[col] = width; + table_resized(); + if ( col <= rightcol ) { // OPTIMIZATION: only redraw if onscreen or to the left + redraw(); + } + // COLUMN RESIZE CALLBACK + if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED ) { + do_callback(CONTEXT_RC_RESIZE, 0, col); + } +} + +// Return row/col clamped to reality +int Fl_Table::row_col_clamp(TableContext context, int &R, int &C) { + int clamped = 0; + if ( R < 0 ) { R = 0; clamped = 1; } + if ( C < 0 ) { C = 0; clamped = 1; } + switch ( context ) { + case CONTEXT_COL_HEADER: + // Allow col headers to draw even if no rows + if ( R >= _rows && R != 0 ) { R = _rows - 1; clamped = 1; } + break; + + case CONTEXT_ROW_HEADER: + // Allow row headers to draw even if no columns + if ( C >= _cols && C != 0 ) { C = _cols - 1; clamped = 1; } + break; + + case CONTEXT_CELL: + default: + // CLAMP R/C TO _rows/_cols + if ( R >= _rows ) { R = _rows - 1; clamped = 1; } + if ( C >= _cols ) { C = _cols - 1; clamped = 1; } + break; + } + return(clamped); +} + +// Return bounding region for given context +void Fl_Table::get_bounds(TableContext context, int &X, int &Y, int &W, int &H) { + switch ( context ) { + case CONTEXT_COL_HEADER: + // Column header clipping. + X = tox; + Y = wiy; + W = tow; + H = col_header_height(); + return; + + case CONTEXT_ROW_HEADER: + // Row header clipping. + X = wix; + Y = toy; + W = row_header_width(); + H = toh; + return; + + case CONTEXT_TABLE: + // Table inner dimensions + X = tix; Y = tiy; W = tiw; H = tih; + return; + + // TODO: Add other contexts.. + default: + fprintf(stderr, "Fl_Table::get_bounds(): context %d unimplemented\n", (int)context); + return; + } + //NOTREACHED +} + +// Find row/col beneath cursor +// +// Returns R/C and context. +// Also returns resizeflag, if mouse is hovered over a resize boundary. +// +Fl_Table::TableContext Fl_Table::cursor2rowcol(int &R, int &C, ResizeFlag &resizeflag) { + // return values + R = C = 0; + resizeflag = RESIZE_NONE; + // Row header? + int X, Y, W, H; + if ( row_header() ) { + // Inside a row heading? + get_bounds(CONTEXT_ROW_HEADER, X, Y, W, H); + if ( Fl::event_inside(X, Y, W, H) ) { + // Scan visible rows until found + for ( R = toprow; R <= botrow; R++ ) { + find_cell(CONTEXT_ROW_HEADER, R, 0, X, Y, W, H); + if ( Fl::event_y() >= Y && Fl::event_y() < (Y+H) ) { + // Found row? + // If cursor over resize boundary, and resize enabled, + // enable the appropriate resize flag. + // + if ( row_resize() ) { + if ( Fl::event_y() <= (Y+3-0) ) { resizeflag = RESIZE_ROW_ABOVE; } + if ( Fl::event_y() >= (Y+H-3) ) { resizeflag = RESIZE_ROW_BELOW; } + } + return(CONTEXT_ROW_HEADER); + } + } + // Must be in row header dead zone + return(CONTEXT_NONE); + } + } + // Column header? + if ( col_header() ) { + // Inside a column heading? + get_bounds(CONTEXT_COL_HEADER, X, Y, W, H); + if ( Fl::event_inside(X, Y, W, H) ) { + // Scan visible columns until found + for ( C = leftcol; C <= rightcol; C++ ) { + find_cell(CONTEXT_COL_HEADER, 0, C, X, Y, W, H); + if ( Fl::event_x() >= X && Fl::event_x() < (X+W) ) { + // Found column? + // If cursor over resize boundary, and resize enabled, + // enable the appropriate resize flag. + // + if ( col_resize() ) { + if ( Fl::event_x() <= (X+3-0) ) { resizeflag = RESIZE_COL_LEFT; } + if ( Fl::event_x() >= (X+W-3) ) { resizeflag = RESIZE_COL_RIGHT; } + } + return(CONTEXT_COL_HEADER); + } + } + // Must be in column header dead zone + return(CONTEXT_NONE); + } + } + // Mouse somewhere in table? + // Scan visible r/c's until we find it. + // + if ( Fl::event_inside(tox, toy, tow, toh) ) { + for ( R = toprow; R <= botrow; R++ ) { + find_cell(CONTEXT_CELL, R, C, X, Y, W, H); + if ( Fl::event_y() < Y ) break; // OPT: thanks lars + if ( Fl::event_y() >= (Y+H) ) continue; // OPT: " " + for ( C = leftcol; C <= rightcol; C++ ) { + find_cell(CONTEXT_CELL, R, C, X, Y, W, H); + if ( Fl::event_inside(X, Y, W, H) ) { + return(CONTEXT_CELL); // found it + } + } + } + // Must be in a dead zone of the table + R = C = 0; + return(CONTEXT_TABLE); + } + // Somewhere else + return(CONTEXT_NONE); +} + +// Find X/Y/W/H for cell at R/C +// If R or C are out of range, returns -1 +// with X/Y/W/H set to zero. +// +int Fl_Table::find_cell(TableContext context, int R, int C, int &X, int &Y, int &W, int &H) { + if ( row_col_clamp(context, R, C) ) { // row or col out of range? error + X=Y=W=H=0; + return(-1); + } + X = col_scroll_position(C) - hscrollbar->value() + tix; + Y = row_scroll_position(R) - vscrollbar->value() + tiy; + W = col_width(C); + H = row_height(R); + + switch ( context ) { + case CONTEXT_COL_HEADER: + Y = wiy; + H = col_header_height(); + return(0); + + case CONTEXT_ROW_HEADER: + X = wix; + W = row_header_width(); + return(0); + + case CONTEXT_CELL: + return(0); + + case CONTEXT_TABLE: + return(0); + + // TODO -- HANDLE OTHER CONTEXTS + default: + fprintf(stderr, "Fl_Table::find_cell: unknown context %d\n", (int)context); + return(-1); + } + //NOTREACHED +} + +// Enable automatic scroll-selection +void Fl_Table::_start_auto_drag() { + if (_auto_drag) return; + _auto_drag = 1; + Fl::add_timeout(0.3, _auto_drag_cb2, this); +} + +// Disable automatic scroll-selection +void Fl_Table::_stop_auto_drag() { + if (!_auto_drag) return; + Fl::remove_timeout(_auto_drag_cb2, this); + _auto_drag = 0; +} + +void Fl_Table::_auto_drag_cb2(void *d) { + ((Fl_Table*)d)->_auto_drag_cb(); +} + +// Handle automatic scroll-selection if mouse selection dragged off table edge +void Fl_Table::_auto_drag_cb() { + int lx = Fl::e_x; + int ly = Fl::e_y; + if (_selecting == CONTEXT_COL_HEADER) + { ly = y() + col_header_height(); } + else if (_selecting == CONTEXT_ROW_HEADER) + { lx = x() + row_header_width(); } + if (lx > x() + w() - 20) { + Fl::e_x = x() + w() - 20; + if (hscrollbar->visible()) + ((Fl_Slider*)hscrollbar)->value( + hscrollbar->clamp(hscrollbar->value() + 30)); + hscrollbar->do_callback(); + _dragging_x = Fl::e_x - 30; + } + else if (lx < (x() + row_header_width())) { + Fl::e_x = x() + row_header_width() + 1; + if (hscrollbar->visible()) { + ((Fl_Slider*)hscrollbar)->value(hscrollbar->clamp(hscrollbar->value() - 30)); + } + hscrollbar->do_callback(); + _dragging_x = Fl::e_x + 30; + } + if (ly > y() + h() - 20) { + Fl::e_y = y() + h() - 20; + if (vscrollbar->visible()) { + ((Fl_Slider*)vscrollbar)->value(vscrollbar->clamp(vscrollbar->value() + 30)); + } + vscrollbar->do_callback(); + _dragging_y = Fl::e_y - 30; + } + else if (ly < (y() + col_header_height())) { + Fl::e_y = y() + col_header_height() + 1; + if (vscrollbar->visible()) { + ((Fl_Slider*)vscrollbar)->value(vscrollbar->clamp(vscrollbar->value() - 30)); + } + vscrollbar->do_callback(); + _dragging_y = Fl::e_y + 30; + } + _auto_drag = 2; + handle(FL_DRAG); + _auto_drag = 1; + Fl::e_x = lx; + Fl::e_y = ly; + Fl::check(); + Fl::flush(); + if (Fl::event_buttons() && _auto_drag) { + Fl::add_timeout(0.05, _auto_drag_cb2, this); + } +} + +// Recalculate the window dimensions +void Fl_Table::recalc_dimensions() { + // Recalc to* (Table Outer), ti* (Table Inner), wi* ( Widget Inner) + wix = ( x() + Fl::box_dx(box())); tox = wix; tix = tox + Fl::box_dx(table->box()); + wiy = ( y() + Fl::box_dy(box())); toy = wiy; tiy = toy + Fl::box_dy(table->box()); + wiw = ( w() - Fl::box_dw(box())); tow = wiw; tiw = tow - Fl::box_dw(table->box()); + wih = ( h() - Fl::box_dh(box())); toh = wih; tih = toh - Fl::box_dh(table->box()); + // Trim window if headers enabled + if ( col_header() ) { + tiy += col_header_height(); toy += col_header_height(); + tih -= col_header_height(); toh -= col_header_height(); + } + if ( row_header() ) { + tix += row_header_width(); tox += row_header_width(); + tiw -= row_header_width(); tow -= row_header_width(); + } + // Make scroll bars disappear if window large enough + { + // First pass: can hide via window size? + int hidev = (table_h <= tih); + int hideh = (table_w <= tiw); + // Second pass: Check for interference + if ( !hideh & hidev ) { hidev = (( table_h - tih + SCROLLBAR_SIZE ) <= 0 ); } + if ( !hidev & hideh ) { hideh = (( table_w - tiw + SCROLLBAR_SIZE ) <= 0 ); } + // Determine scrollbar visibility, trim ti[xywh]/to[xywh] + if ( hidev ) { vscrollbar->hide(); } + else { vscrollbar->show(); tiw -= SCROLLBAR_SIZE; tow -= SCROLLBAR_SIZE; } + if ( hideh ) { hscrollbar->hide(); } + else { hscrollbar->show(); tih -= SCROLLBAR_SIZE; toh -= SCROLLBAR_SIZE; } + } + // Resize the child table + table->resize(tox, toy, tow, toh); + table->init_sizes(); +} + +// Recalculate internals after a scroll. +// +// Call this if table has been scrolled or resized. +// Does not handle redraw(). +// TODO: Assumes ti[xywh] has already been recalculated. +// +void Fl_Table::table_scrolled() { + // Find top row + int y, row, voff = vscrollbar->value(); + for ( row=y=0; row < _rows; row++ ) { + y += row_height(row); + if ( y > voff ) { y -= row_height(row); break; } + } + _row_position = toprow = ( row >= _rows ) ? (row - 1) : row; + toprow_scrollpos = y; // OPTIMIZATION: save for later use + // Find bottom row + voff = vscrollbar->value() + tih; + for ( ; row < _rows; row++ ) { + y += row_height(row); + if ( y >= voff ) { break; } + } + botrow = ( row >= _rows ) ? (row - 1) : row; + // Left column + int x, col, hoff = hscrollbar->value(); + for ( col=x=0; col < _cols; col++ ) { + x += col_width(col); + if ( x > hoff ) { x -= col_width(col); break; } + } + _col_position = leftcol = ( col >= _cols ) ? (col - 1) : col; + leftcol_scrollpos = x; // OPTIMIZATION: save for later use + // Right column + // Work with data left over from leftcol calculation + // + hoff = hscrollbar->value() + tiw; + for ( ; col < _cols; col++ ) { + x += col_width(col); + if ( x >= hoff ) { break; } + } + rightcol = ( col >= _cols ) ? (col - 1) : col; + // First tell children to scroll + draw_cell(CONTEXT_RC_RESIZE, 0,0,0,0,0,0); +} + +// Table resized: recalc internal data +// Call this whenever the window is resized. +// Recalculates the scrollbar sizes. +// Makes no assumptions about any pre-initialized data. +// +void Fl_Table::table_resized() { + table_h = row_scroll_position(rows()); + table_w = col_scroll_position(cols()); + recalc_dimensions(); + // Recalc scrollbar sizes + // Clamp scrollbar value() after a resize. + // Resize scrollbars to enforce a constant trough width after a window resize. + // + { + // Vertical scrollbar + float vscrolltab = ( table_h == 0 || tih > table_h ) ? 1 : (float)tih / table_h; + float hscrolltab = ( table_w == 0 || tiw > table_w ) ? 1 : (float)tiw / table_w; + vscrollbar->bounds(0, table_h-tih); + vscrollbar->precision(10); + vscrollbar->slider_size(vscrolltab); + vscrollbar->resize(wix+wiw-SCROLLBAR_SIZE, wiy, + SCROLLBAR_SIZE, + wih - ((hscrollbar->visible())?SCROLLBAR_SIZE:0)); + vscrollbar->Fl_Valuator::value(vscrollbar->clamp(vscrollbar->value())); + // Horizontal scrollbar + hscrollbar->bounds(0, table_w-tiw); + hscrollbar->precision(10); + hscrollbar->slider_size(hscrolltab); + hscrollbar->resize(wix, wiy+wih-SCROLLBAR_SIZE, + wiw - ((vscrollbar->visible())?SCROLLBAR_SIZE:0), + SCROLLBAR_SIZE); + hscrollbar->Fl_Valuator::value(hscrollbar->clamp(hscrollbar->value())); + } + + // Tell FLTK child widgets were resized + Fl_Group::init_sizes(); + + // Recalc top/bot/left/right + table_scrolled(); + + // DO *NOT* REDRAW -- LEAVE THIS UP TO THE CALLER + // redraw(); +} + +// Someone moved a scrollbar +void Fl_Table::scroll_cb(Fl_Widget*w, void *data) { + Fl_Table *o = (Fl_Table*)data; + o->recalc_dimensions(); // recalc tix, tiy, etc. + o->table_scrolled(); + o->redraw(); +} + +// Set number of rows +void Fl_Table::rows(int val) { + int oldrows = _rows; + _rows = val; + { + int default_h = ( _rowheights.size() > 0 ) ? _rowheights.back() : 25; + int now_size = _rowheights.size(); + _rowheights.size(val); // enlarge or shrink as needed + while ( now_size < val ) { + _rowheights[now_size++] = default_h; // fill new + } + } + table_resized(); + + // OPTIMIZATION: redraw only if change is visible. + if ( val >= oldrows && oldrows > botrow ) { + // NO REDRAW + } else { + redraw(); + } +} + +// Set number of cols +void Fl_Table::cols(int val) { + _cols = val; + { + int default_w = ( _colwidths.size() > 0 ) ? _colwidths[_colwidths.size()-1] : 80; + int now_size = _colwidths.size(); + _colwidths.size(val); // enlarge or shrink as needed + while ( now_size < val ) { + _colwidths[now_size++] = default_w; // fill new + } + } + table_resized(); + redraw(); +} + +// Change mouse cursor to different type +void Fl_Table::change_cursor(Fl_Cursor newcursor) { + if ( newcursor != _last_cursor ) { + fl_cursor(newcursor, FL_BLACK, FL_WHITE); + _last_cursor = newcursor; + } +} + +void Fl_Table::damage_zone(int r1, int c1, int r2, int c2, int r3, int c3) { + int R1 = r1, C1 = c1; + int R2 = r2, C2 = c2; + if (r1 > R2) R2 = r1; + if (r2 < R1) R1 = r2; + if (r3 > R2) R2 = r3; + if (r3 < R1) R1 = r3; + if (c1 > C2) C2 = c1; + if (c2 < C1) C1 = c2; + if (c3 > C2) C2 = c3; + if (c3 < C1) C1 = c3; + if (R1 < 0) { + if (R2 < 0) return; + R1 = 0; + } + if (C1 < 0) { + if (C2 < 0) return; + C1 = 0; + } + if (R1 < toprow) R1 = toprow; + if (R2 > botrow) R2 = botrow; + if (C1 < leftcol) C1 = leftcol; + if (C2 > rightcol) C2 = rightcol; + redraw_range(R1, R2, C1, C2); +} + +int Fl_Table::move_cursor(int R, int C) { + if (select_row == -1) R++; + if (select_col == -1) C++; + R += select_row; + C += select_col; + if (R < 0) R = 0; + if (R >= rows()) R = rows() - 1; + if (C < 0) C = 0; + if (C >= cols()) C = cols() - 1; + if (R == select_row && C == select_col) return 0; + damage_zone(current_row, current_col, select_row, select_col, R, C); + select_row = R; + select_col = C; + if (!Fl::event_state(FL_SHIFT)) { + current_row = R; + current_col = C; + } + if (R < toprow + 1 || R > botrow - 1) row_position(R); + if (C < leftcol + 1 || C > rightcol - 1) col_position(C); + return 1; +} + +// #define DEBUG 1 +#ifdef DEBUG +#include "eventnames.h" +#define PRINTEVENT \ + fprintf(stderr,"Table %s: ** Event: %s --\n", (label()?label():"none"), eventnames[event]); +#else +#define PRINTEVENT +#endif + +// Handle FLTK events +int Fl_Table::handle(int event) { + PRINTEVENT; + int ret = Fl_Group::handle(event); // let FLTK group handle events first + if (ret) { + if (Fl::event_inside(hscrollbar) || Fl::event_inside(vscrollbar)) return 1; + if (Fl::focus() != this && contains(Fl::focus())) return 1; + } + // Which row/column are we over? + int R, C; // row/column being worked on + ResizeFlag resizeflag; // which resizing area are we over? (0=none) + TableContext context = cursor2rowcol(R, C, resizeflag); + switch ( event ) { + case FL_PUSH: + if (Fl::event_button() == 1 && !Fl::event_clicks()) { + if (Fl::focus() != this) { + take_focus(); + do_callback(CONTEXT_TABLE, -1, -1); + ret = 1; + } + damage_zone(current_row, current_col, select_row, select_col, R, C); + if (context == CONTEXT_CELL) { + current_row = select_row = R; + current_col = select_col = C; + _selecting = CONTEXT_CELL; + } else { + current_row = select_row = -1; + current_col = select_col = -1; + } + } + // Need this for eg. right click to pop up a menu + if ( Fl_Widget::callback() && // callback defined? + resizeflag == RESIZE_NONE ) { // not resizing? + do_callback(context, R, C); // do callback + } + switch ( context ) { + case CONTEXT_CELL: + // FL_PUSH on a cell? + ret = 1; // express interest in FL_RELEASE + break; + + case CONTEXT_NONE: + // FL_PUSH on table corner? + if ( Fl::event_button() == 1 && + Fl::event_x() < x() + row_header_width()) { + current_col = 0; + select_col = cols() - 1; + current_row = 0; + select_row = rows() - 1; + damage_zone(current_row, current_col, select_row, select_col); + ret = 1; + } + break; + + case CONTEXT_COL_HEADER: + // FL_PUSH on a column header? + if ( Fl::event_button() == 1) { + // Resizing? Handle it + if ( resizeflag ) { + // Start resize if left click on column border. + // "ret=1" ensures we get drag events from now on. + // (C-1) is used if mouse is over the left hand side + // of cell, so we resize the next column on the left. + // + _resizing_col = ( resizeflag & RESIZE_COL_LEFT ) ? C-1 : C; + _resizing_row = -1; + _dragging_x = Fl::event_x(); + ret = 1; + } else { + // Not resizing? Select the column + current_col = select_col = C; + current_row = 0; + select_row = rows() - 1; + _selecting = CONTEXT_COL_HEADER; + damage_zone(current_row, current_col, select_row, select_col); + ret = 1; + } + } + break; + + case CONTEXT_ROW_HEADER: + // FL_PUSH on a row header? + if ( Fl::event_button() == 1 ) { + // Resizing? Handle it + if ( resizeflag ) { + // Start resize if left mouse clicked on row border. + // "ret = 1" ensures we get drag events from now on. + // (R-1) is used if mouse is over the top of the cell, + // so that we resize the row above. + // + _resizing_row = ( resizeflag & RESIZE_ROW_ABOVE ) ? R-1 : R; + _resizing_col = -1; + _dragging_y = Fl::event_y(); + ret = 1; + } else { + // Not resizing? Select the row + current_row = select_row = R; + current_col = 0; + select_col = cols() - 1; + _selecting = CONTEXT_ROW_HEADER; + damage_zone(current_row, current_col, select_row, select_col); + ret = 1; + } + } + break; + + default: + ret = 0; // express disinterest + break; + } + _last_row = R; + break; + + case FL_DRAG: + if (_auto_drag == 1) { + ret = 1; + break; + } + if ( _resizing_col > -1 ) { + // Dragging column? + // + // Let user drag even /outside/ the row/col widget. + // Don't allow column width smaller than 1. + // Continue to show FL_CURSOR_WE at all times during drag. + // + int offset = _dragging_x - Fl::event_x(); + int new_w = col_width(_resizing_col) - offset; + if ( new_w < _col_resize_min ) new_w = _col_resize_min; + col_width(_resizing_col, new_w); + _dragging_x = Fl::event_x(); + table_resized(); + redraw(); + change_cursor(FL_CURSOR_WE); + ret = 1; + if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED ) { + do_callback(CONTEXT_RC_RESIZE, R, C); + } + } + else if ( _resizing_row > -1 ) { + // Dragging row? + // + // Let user drag even /outside/ the row/col widget. + // Don't allow row width smaller than 1. + // Continue to show FL_CURSOR_NS at all times during drag. + // + int offset = _dragging_y - Fl::event_y(); + int new_h = row_height(_resizing_row) - offset; + if ( new_h < _row_resize_min ) new_h = _row_resize_min; + row_height(_resizing_row, new_h); + _dragging_y = Fl::event_y(); + table_resized(); + redraw(); + change_cursor(FL_CURSOR_NS); + ret = 1; + if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED ) { + do_callback(CONTEXT_RC_RESIZE, R, C); + } + } else { + if (Fl::event_button() == 1 && + _selecting == CONTEXT_CELL && + context == CONTEXT_CELL) { + if (select_row != R || select_col != C) { + damage_zone(current_row, current_col, select_row, select_col, R, C); + } + select_row = R; + select_col = C; + ret = 1; + } + else if (Fl::event_button() == 1 && + _selecting == CONTEXT_ROW_HEADER && + context & (CONTEXT_ROW_HEADER|CONTEXT_COL_HEADER|CONTEXT_CELL)) { + if (select_row != R) { + damage_zone(current_row, current_col, select_row, select_col, R, C); + } + select_row = R; + ret = 1; + } + else if (Fl::event_button() == 1 && + _selecting == CONTEXT_COL_HEADER + && context & (CONTEXT_ROW_HEADER|CONTEXT_COL_HEADER|CONTEXT_CELL)) { + if (select_col != C) { + damage_zone(current_row, current_col, select_row, select_col, R, C); + } + select_col = C; + ret = 1; + } + } + // Enable autodrag if not resizing, and mouse has moved off table edge + if ( _resizing_row < 0 && _resizing_col < 0 && _auto_drag == 0 && + ( Fl::event_x() > x() + w() - 20 || + Fl::event_x() < x() + row_header_width() || + Fl::event_y() > y() + h() - 20 || + Fl::event_y() < y() + col_header_height() + ) ) { + _start_auto_drag(); + } + break; + + case FL_RELEASE: + _stop_auto_drag(); + switch ( context ) { + case CONTEXT_ROW_HEADER: // release on row header + case CONTEXT_COL_HEADER: // release on col header + case CONTEXT_CELL: // release on a cell + case CONTEXT_TABLE: // release on dead zone + if ( _resizing_col == -1 && // not resizing a column + _resizing_row == -1 && // not resizing a row + Fl_Widget::callback() && // callback defined + when() & FL_WHEN_RELEASE && // on button release + _last_row == R ) { // release on same row PUSHed? + // Need this for eg. left clicking on a cell to select it + do_callback(context, R, C); + } + break; + + default: + break; + } + if ( Fl::event_button() == 1 ) { + change_cursor(FL_CURSOR_DEFAULT); + _resizing_col = -1; + _resizing_row = -1; + ret = 1; + } + break; + + case FL_MOVE: + if ( context == CONTEXT_COL_HEADER && // in column header? + resizeflag ) { // resize + near boundary? + change_cursor(FL_CURSOR_WE); // show resize cursor + } + else if ( context == CONTEXT_ROW_HEADER && // in row header? + resizeflag ) { // resize + near boundary? + change_cursor(FL_CURSOR_NS); // show resize cursor + } else { + change_cursor(FL_CURSOR_DEFAULT); // normal cursor + } + ret = 1; + break; + + case FL_ENTER: // See FLTK event docs on the FL_ENTER widget + if (!ret) take_focus(); + ret = 1; + //FALLTHROUGH + + case FL_LEAVE: // We want to track the mouse if resizing is allowed. + if ( resizeflag ) { + ret = 1; + } + if ( event == FL_LEAVE ) { + _stop_auto_drag(); + change_cursor(FL_CURSOR_DEFAULT); + } + break; + + case FL_FOCUS: + Fl::focus(this); + //FALLTHROUGH + + case FL_UNFOCUS: + _stop_auto_drag(); + ret = 1; + break; + + case FL_KEYBOARD: { + ret = 0; + int is_row = select_row; + int is_col = select_col; + switch(Fl::event_key()) { + case FL_Home: + ret = move_cursor(0, -1000000); + break; + case FL_End: + ret = move_cursor(0, 1000000); + break; + case FL_Page_Up: + ret = move_cursor(-(botrow - toprow - 1), 0); + break; + case FL_Page_Down: + ret = move_cursor(botrow - toprow - 1 , 0); + break; + case FL_Left: + ret = move_cursor(0, -1); + break; + case FL_Right: + ret = move_cursor(0, 1); + break; + case FL_Up: + ret = move_cursor(-1, 0); + break; + case FL_Down: + ret = move_cursor(1, 0); + break; + } + if (ret && Fl::focus() != this) { + do_callback(CONTEXT_TABLE, -1, -1); + take_focus(); + } + //if (!ret && Fl_Widget::callback() && when() & FL_WHEN_NOT_CHANGED ) + if ( Fl_Widget::callback() && + ( + ( !ret && when() & FL_WHEN_NOT_CHANGED ) || + ( is_row!= select_row || is_col!= select_col ) + ) + ) { + do_callback(CONTEXT_CELL, select_row, select_col); + //damage_zone(current_row, current_col, select_row, select_col); + ret = 1; + } + break; + } + + default: + change_cursor(FL_CURSOR_DEFAULT); + break; + } + return(ret); +} + +// Resize FLTK override +// Handle resize events if user resizes parent window. +// +void Fl_Table::resize(int X, int Y, int W, int H) { + // Tell group to resize, and recalc our own widget as well + Fl_Group::resize(X, Y, W, H); + table_resized(); + redraw(); +} + +// Draw a cell +void Fl_Table::_redraw_cell(TableContext context, int r, int c) { + if ( r < 0 || c < 0 ) return; + int X,Y,W,H; + find_cell(context, r, c, X, Y, W, H); // find positions of cell + draw_cell(context, r, c, X, Y, W, H); // call users' function to draw it +} + +int Fl_Table::is_selected(int r, int c) { + int s_left, s_right, s_top, s_bottom; + + if (select_col > current_col) { + s_left = current_col; + s_right = select_col; + } else { + s_right = current_col; + s_left = select_col; + } + if (select_row > current_row) { + s_top = current_row; + s_bottom = select_row; + } else { + s_bottom = current_row; + s_top = select_row; + } + if (r >= s_top && r <= s_bottom && c >= s_left && c <= s_right) { + return 1; + } + return 0; +} + +void Fl_Table::get_selection(int& s_top, int& s_left, int& s_bottom, int& s_right) { + if (select_col > current_col) { + s_left = current_col; + s_right = select_col; + } else { + s_right = current_col; + s_left = select_col; + } + if (select_row > current_row) { + s_top = current_row; + s_bottom = select_row; + } else { + s_bottom = current_row; + s_top = select_row; + } +} + +void Fl_Table::set_selection(int s_top, int s_left, int s_bottom, int s_right) { + damage_zone(current_row, current_col, select_row, select_col); + current_col = s_left; + current_row = s_top; + select_col = s_right; + select_row = s_bottom; + damage_zone(current_row, current_col, select_row, select_col); +} + +// Draw the entire Fl_Table +// Override the draw() routine to draw the table. +// Then tell the group to draw over us. +// +void Fl_Table::draw() { + draw_cell(CONTEXT_STARTPAGE, 0, 0, // let user's drawing routine + tix, tiy, tiw, tih); // prep new page + + // Let fltk widgets draw themselves first. Do this after + // draw_cell(CONTEXT_STARTPAGE) in case user moves widgets around. + // Use window 'inner' clip to prevent drawing into table border. + // (unfortunately this clips FLTK's border, so we must draw it explicity below) + // + fl_push_clip(wix, wiy, wiw, wih); + { + Fl_Group::draw(); + } + fl_pop_clip(); + + // Explicitly draw border around widget, if any + draw_box(box(), x(), y(), w(), h(), color()); + + // If Fl_Scroll 'table' is hidden, draw its box + // Do this after Fl_Group::draw() so we draw over scrollbars + // that leak around the border. + // + if ( ! table->visible() ) { + if ( damage() & FL_DAMAGE_ALL || damage() & FL_DAMAGE_CHILD ) { + draw_box(table->box(), tox, toy, tow, toh, table->color()); + } + } + // Clip all further drawing to the inner widget dimensions + fl_push_clip(wix, wiy, wiw, wih); + { + // Only redraw a few cells? + if ( ! ( damage() & FL_DAMAGE_ALL ) && _redraw_leftcol != -1 ) { + fl_push_clip(tix, tiy, tiw, tih); + for ( int c = _redraw_leftcol; c <= _redraw_rightcol; c++ ) { + for ( int r = _redraw_toprow; r <= _redraw_botrow; r++ ) { + _redraw_cell(CONTEXT_CELL, r, c); + } + } + fl_pop_clip(); + } + if ( damage() & FL_DAMAGE_ALL ) { + int X,Y,W,H; + // Draw row headers, if any + if ( row_header() ) { + get_bounds(CONTEXT_ROW_HEADER, X, Y, W, H); + fl_push_clip(X,Y,W,H); + for ( int r = toprow; r <= botrow; r++ ) { + _redraw_cell(CONTEXT_ROW_HEADER, r, 0); + } + fl_pop_clip(); + } + // Draw column headers, if any + if ( col_header() ) { + get_bounds(CONTEXT_COL_HEADER, X, Y, W, H); + fl_push_clip(X,Y,W,H); + for ( int c = leftcol; c <= rightcol; c++ ) { + _redraw_cell(CONTEXT_COL_HEADER, 0, c); + } + fl_pop_clip(); + } + // Draw all cells. + // This includes cells partially obscured off edges of table. + // No longer do this last; you might think it would be nice + // to draw over dead zones, but on redraws it flickers. Avoid + // drawing over deadzones; prevent deadzones by sizing columns. + // + fl_push_clip(tix, tiy, tiw, tih); { + for ( int r = toprow; r <= botrow; r++ ) { + for ( int c = leftcol; c <= rightcol; c++ ) { + _redraw_cell(CONTEXT_CELL, r, c); + } + } + } + fl_pop_clip(); + // Draw little rectangle in corner of headers + if ( row_header() && col_header() ) { + fl_rectf(wix, wiy, row_header_width(), col_header_height(), color()); + } + + // Table has a boxtype? Close those few dead pixels + if ( table->box() ) { + if ( col_header() ) { + fl_rectf(tox, wiy, Fl::box_dx(table->box()), col_header_height(), color()); + } + if ( row_header() ) { + fl_rectf(wix, toy, row_header_width(), Fl::box_dx(table->box()), color()); + } + } + + // Table width smaller than window? Fill remainder with rectangle + if ( table_w < tiw ) { + fl_rectf(tix + table_w, tiy, tiw - table_w, tih, color()); + // Col header? fill that too + if ( col_header() ) { + fl_rectf(tix + table_w, + wiy, + // get that corner just right.. + (tiw - table_w + Fl::box_dw(table->box()) - + Fl::box_dx(table->box())), + col_header_height(), + color()); + } + } + // Table height smaller than window? Fill remainder with rectangle + if ( table_h < tih ) { + fl_rectf(tix, tiy + table_h, tiw, tih - table_h, color()); + if ( row_header() ) { + // NOTE: + // Careful with that lower corner; don't use tih; when eg. + // table->box(FL_THIN_UPFRAME) and hscrollbar hidden, + // leaves a row of dead pixels. + // + fl_rectf(wix, tiy + table_h, row_header_width(), + (wiy+wih) - (tiy+table_h) - + ( hscrollbar->visible() ? SCROLLBAR_SIZE : 0), + color()); + } + } + } + // Both scrollbars? Draw little box in lower right + if ( vscrollbar->visible() && hscrollbar->visible() ) { + fl_rectf(vscrollbar->x(), hscrollbar->y(), + vscrollbar->w(), hscrollbar->h(), color()); + } + draw_cell(CONTEXT_ENDPAGE, 0, 0, // let user's drawing + tix, tiy, tiw, tih); // routines cleanup + + _redraw_leftcol = _redraw_rightcol = _redraw_toprow = _redraw_botrow = -1; + } + fl_pop_clip(); +} diff --git a/src/Fl_Table_Row.cxx b/src/Fl_Table_Row.cxx new file mode 100644 index 000000000..85fa5aac5 --- /dev/null +++ b/src/Fl_Table_Row.cxx @@ -0,0 +1,317 @@ +// +// Fl_Table_Row -- A row oriented table widget +// +// A class specializing in a table of rows. +// Handles row-specific selection behavior. +// +// Copyright 2002 by Greg Ercolano. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA. +// +// Please report all bugs and problems to "erco at seriss dot com". +// +// +// TODO: +// o Row headings (only column headings supported currently) +// + +#include <stdio.h> // for debugging +#include <FL/Fl.H> +#include <FL/fl_draw.H> +#include <FL/Fl_Table_Row.H> + +// Is row selected? +int Fl_Table_Row::row_selected(int row) { + if ( row < 0 || row >= rows() ) return(-1); + return(_rowselect[row]); +} + +// Change row selection type +void Fl_Table_Row::type(TableRowSelectMode val) { + _selectmode = val; + switch ( _selectmode ) { + case SELECT_NONE: + { + for ( int row=0; row<rows(); row++ ) { + _rowselect[row] = 0; + } + redraw(); + break; + } + case SELECT_SINGLE: + { + int count = 0; + for ( int row=0; row<rows(); row++ ) { + if ( _rowselect[row] ) { + if ( ++count > 1 ) { // only one allowed + _rowselect[row] = 0; + } + } + } + redraw(); + break; + } + case SELECT_MULTI: + break; + } +} + +// Change selection state for row +// +// flag: +// 0 - clear selection +// 1 - set selection +// 2 - toggle selection +// +// Returns: +// 0 - selection state did not change +// 1 - selection state changed +// -1 - row out of range or incorrect selection mode +// +int Fl_Table_Row::select_row(int row, int flag) { + int ret = 0; + if ( row < 0 || row >= rows() ) { return(-1); } + switch ( _selectmode ) { + case SELECT_NONE: + return(-1); + + case SELECT_SINGLE: + { + int oldval; + for ( int t=0; t<rows(); t++ ) { + if ( t == row ) { + oldval = _rowselect[row]; + if ( flag == 2 ) { _rowselect[row] ^= 1; } + else { _rowselect[row] = flag; } + if ( oldval != _rowselect[row] ) { + redraw_range(row, row, leftcol, rightcol); + ret = 1; + } + } + else if ( _rowselect[t] ) { + _rowselect[t] = 0; + redraw_range(t, t, leftcol, rightcol); + } + } + break; + } + + case SELECT_MULTI: + { + int oldval = _rowselect[row]; + if ( flag == 2 ) { _rowselect[row] ^= 1; } + else { _rowselect[row] = flag; } + if ( _rowselect[row] != oldval ) { // select state changed? + if ( row >= toprow && row <= botrow ) { // row visible? + // Extend partial redraw range + redraw_range(row, row, leftcol, rightcol); + } + ret = 1; + } + } + } + return(ret); +} + +// Select all rows to a known state +void Fl_Table_Row::select_all_rows(int flag) { + switch ( _selectmode ) { + case SELECT_NONE: + return; + + case SELECT_SINGLE: + if ( flag != 0 ) return; + //FALLTHROUGH + + case SELECT_MULTI: + { + char changed = 0; + if ( flag == 2 ) { + for ( int row=0; row<(int)_rowselect.size(); row++ ) { + _rowselect[row] ^= 1; + } + changed = 1; + } else { + for ( int row=0; row<(int)_rowselect.size(); row++ ) { + changed |= (_rowselect[row] != flag)?1:0; + _rowselect[row] = flag; + } + } + if ( changed ) { + redraw(); + } + } + } +} + +// Set number of rows +void Fl_Table_Row::rows(int val) { + Fl_Table::rows(val); + while ( val > (int)_rowselect.size() ) { _rowselect.push_back(0); } // enlarge + while ( val < (int)_rowselect.size() ) { _rowselect.pop_back(); } // shrink +} + +// #include "eventnames.h" // debugging +// #include <stdio.h> + +// Handle events +int Fl_Table_Row::handle(int event) { + + // fprintf(stderr, "** EVENT: %s: EVENT XY=%d,%d\n", + // eventnames[event], Fl::event_x(), Fl::event_y()); // debugging + + // Let base class handle event + int ret = Fl_Table::handle(event); + + // The following code disables cell selection.. why was it added? -erco 05/18/03 + // if ( ret ) { _last_y = Fl::event_y(); return(1); } // base class 'handled' it (eg. column resize) + + int shiftstate = (Fl::event_state() & FL_CTRL) ? FL_CTRL : + (Fl::event_state() & FL_SHIFT) ? FL_SHIFT : 0; + + // Which row/column are we over? + int R, C; // row/column being worked on + ResizeFlag resizeflag; // which resizing area are we over? (0=none) + TableContext context = cursor2rowcol(R, C, resizeflag); + switch ( event ) { + case FL_PUSH: + if ( Fl::event_button() == 1 ) { + _last_push_x = Fl::event_x(); // save regardless of context + _last_push_y = Fl::event_y(); // " " + + // Handle selection in table. + // Select cell under cursor, and enable drag selection mode. + // + if ( context == CONTEXT_CELL ) { + // Ctrl key? Toggle selection state + switch ( shiftstate ) { + case FL_CTRL: + select_row(R, 2); // toggle + break; + + case FL_SHIFT: + { + select_row(R, 1); + if ( _last_row > -1 ) { + int srow = R, erow = _last_row; + if ( srow > erow ) { + srow = _last_row; + erow = R; + } + for ( int row = srow; row <= erow; row++ ) { + select_row(row, 1); + } + } + break; + } + + default: + select_all_rows(0); // clear all previous selections + select_row(R, 1); + break; + } + + _last_row = R; + _dragging_select = 1; + ret = 1; // FL_PUSH handled (ensures FL_DRAG will be sent) + // redraw(); // redraw() handled by select_row() + } + } + break; + + case FL_DRAG: + { + if ( _dragging_select ) { + // Dragged off table edges? Handle scrolling + int offtop = toy - _last_y; // >0 if off top of table + int offbot = _last_y - (toy + toh); // >0 if off bottom of table + + if ( offtop > 0 && row_position() > 0 ) { + // Only scroll in upward direction + int diff = _last_y - Fl::event_y(); + if ( diff < 1 ) { + ret = 1; + break; + } + row_position(row_position() - diff); + context = CONTEXT_CELL; C = 0; R = row_position(); // HACK: fake it + if ( R < 0 || R > rows() ) { ret = 1; break; } // HACK: ugly + } + else if ( offbot > 0 && botrow < rows() ) { + // Only scroll in downward direction + int diff = Fl::event_y() - _last_y; + if ( diff < 1 ) { + ret = 1; + break; + } + row_position(row_position() + diff); + context = CONTEXT_CELL; C = 0; R = botrow; // HACK: fake it + if ( R < 0 || R > rows() ) { ret = 1; break; } // HACK: ugly + } + if ( context == CONTEXT_CELL ) { + switch ( shiftstate ) { + case FL_CTRL: + if ( R != _last_row ) { // toggle if dragged to new row + select_row(R, 2); // 2=toggle + } + break; + + case FL_SHIFT: + default: + select_row(R, 1); + if ( _last_row > -1 ) { + int srow = R, erow = _last_row; + if ( srow > erow ) { + srow = _last_row; + erow = R; + } + for ( int row = srow; row <= erow; row++ ) { + select_row(row, 1); + } + } + break; + } + ret = 1; // drag handled + _last_row = R; + } + } + break; + } + + case FL_RELEASE: + if ( Fl::event_button() == 1 ) { + _dragging_select = 0; + ret = 1; // release handled + // Clicked off edges of data table? + // A way for user to clear the current selection. + // + int databot = tiy + table_h, + dataright = tix + table_w; + if ( + ( _last_push_x > dataright && Fl::event_x() > dataright ) || + ( _last_push_y > databot && Fl::event_y() > databot ) + ) { + select_all_rows(0); // clear previous selections + } + } + break; + + default: + break; + } + _last_y = Fl::event_y(); + return(ret); +} diff --git a/src/Fl_Tabs.o b/src/Fl_Tabs.o Binary files differnew file mode 100644 index 000000000..c029f300a --- /dev/null +++ b/src/Fl_Tabs.o diff --git a/src/Makefile b/src/Makefile index 2e050e14f..dce506936 100644 --- a/src/Makefile +++ b/src/Makefile @@ -78,6 +78,8 @@ CPPFILES = \ Fl_Shared_Image.cxx \ Fl_Single_Window.cxx \ Fl_Slider.cxx \ + Fl_Table.cxx \ + Fl_Table_Row.cxx \ Fl_Tabs.cxx \ Fl_Text_Buffer.cxx \ Fl_Text_Display.cxx \ |
