Fix for problem with posting 'popup menus' during user callback

causing change in row/col selection of Fl_Table_Row.
As reported by David Lopez in fltk.general on 11/09/2011, Subject: Popup menu over Fl_Table.



git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@9172 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
Greg Ercolano 2011-11-13 02:46:10 +00:00
parent 3086c07eec
commit baca25f902
2 changed files with 76 additions and 43 deletions

View File

@ -708,10 +708,20 @@ int Fl_Table::handle(int event) {
return 1; return 1;
} }
} }
// Make snapshots of realtime event states *before* we service user's cb,
// which may do things like post popup menus that return with unexpected button states.
int _event_button = Fl::event_button();
int _event_clicks = Fl::event_clicks();
int _event_x = Fl::event_x();
int _event_y = Fl::event_y();
int _event_key = Fl::event_key();
int _event_state = Fl::event_state();
Fl_Widget *_focus = Fl::focus();
switch ( event ) { switch ( event ) {
case FL_PUSH: case FL_PUSH:
if (Fl::event_button() == 1 && !Fl::event_clicks()) { // Single left-click on table? do user's callback with CONTEXT_TABLE
if (Fl::focus() != this) { if (_event_button == 1 && !_event_clicks) {
if (_focus == this) {
take_focus(); take_focus();
do_callback(CONTEXT_TABLE, -1, -1); do_callback(CONTEXT_TABLE, -1, -1);
ret = 1; ret = 1;
@ -726,21 +736,27 @@ int Fl_Table::handle(int event) {
current_col = select_col = -1; current_col = select_col = -1;
} }
} }
// Need this for eg. right click to pop up a menu // A click on table with user's callback defined?
// Need this for eg. right click to pop up a menu
//
if ( Fl_Widget::callback() && // callback defined? if ( Fl_Widget::callback() && // callback defined?
resizeflag == RESIZE_NONE ) { // not resizing? resizeflag == RESIZE_NONE ) { // not resizing?
do_callback(context, R, C); // do callback do_callback(context, R, C); // do callback with context (cell, header, etc)
} }
// Handle selection if handling a left-click
// Use snapshot of _event_button we made before servicing user's cb's
// to avoid checking realtime state of buttons which may have changed
// during the user's callbacks.
//
switch ( context ) { switch ( context ) {
case CONTEXT_CELL: case CONTEXT_CELL:
// FL_PUSH on a cell? // FL_PUSH on a cell?
ret = 1; // express interest in FL_RELEASE ret = 1; // express interest in FL_RELEASE
break; break;
case CONTEXT_NONE: case CONTEXT_NONE:
// FL_PUSH on table corner? // FL_PUSH on table corner?
if ( Fl::event_button() == 1 && if ( _event_button == 1 && _event_x < x() + row_header_width()) {
Fl::event_x() < x() + row_header_width()) {
current_col = 0; current_col = 0;
select_col = cols() - 1; select_col = cols() - 1;
current_row = 0; current_row = 0;
@ -752,7 +768,7 @@ int Fl_Table::handle(int event) {
case CONTEXT_COL_HEADER: case CONTEXT_COL_HEADER:
// FL_PUSH on a column header? // FL_PUSH on a column header?
if ( Fl::event_button() == 1) { if ( _event_button == 1) {
// Resizing? Handle it // Resizing? Handle it
if ( resizeflag ) { if ( resizeflag ) {
// Start resize if left click on column border. // Start resize if left click on column border.
@ -762,7 +778,7 @@ int Fl_Table::handle(int event) {
// //
_resizing_col = ( resizeflag & RESIZE_COL_LEFT ) ? C-1 : C; _resizing_col = ( resizeflag & RESIZE_COL_LEFT ) ? C-1 : C;
_resizing_row = -1; _resizing_row = -1;
_dragging_x = Fl::event_x(); _dragging_x = _event_x;
ret = 1; ret = 1;
} else { } else {
// Not resizing? Select the column // Not resizing? Select the column
@ -778,7 +794,7 @@ int Fl_Table::handle(int event) {
case CONTEXT_ROW_HEADER: case CONTEXT_ROW_HEADER:
// FL_PUSH on a row header? // FL_PUSH on a row header?
if ( Fl::event_button() == 1 ) { if ( _event_button == 1 ) {
// Resizing? Handle it // Resizing? Handle it
if ( resizeflag ) { if ( resizeflag ) {
// Start resize if left mouse clicked on row border. // Start resize if left mouse clicked on row border.
@ -788,7 +804,7 @@ int Fl_Table::handle(int event) {
// //
_resizing_row = ( resizeflag & RESIZE_ROW_ABOVE ) ? R-1 : R; _resizing_row = ( resizeflag & RESIZE_ROW_ABOVE ) ? R-1 : R;
_resizing_col = -1; _resizing_col = -1;
_dragging_y = Fl::event_y(); _dragging_y = _event_y;
ret = 1; ret = 1;
} else { } else {
// Not resizing? Select the row // Not resizing? Select the row
@ -821,11 +837,11 @@ int Fl_Table::handle(int event) {
// Don't allow column width smaller than 1. // Don't allow column width smaller than 1.
// Continue to show FL_CURSOR_WE at all times during drag. // Continue to show FL_CURSOR_WE at all times during drag.
// //
int offset = _dragging_x - Fl::event_x(); int offset = _dragging_x - _event_x;
int new_w = col_width(_resizing_col) - offset; int new_w = col_width(_resizing_col) - offset;
if ( new_w < _col_resize_min ) new_w = _col_resize_min; if ( new_w < _col_resize_min ) new_w = _col_resize_min;
col_width(_resizing_col, new_w); col_width(_resizing_col, new_w);
_dragging_x = Fl::event_x(); _dragging_x = _event_x;
table_resized(); table_resized();
redraw(); redraw();
change_cursor(FL_CURSOR_WE); change_cursor(FL_CURSOR_WE);
@ -841,11 +857,11 @@ int Fl_Table::handle(int event) {
// Don't allow row width smaller than 1. // Don't allow row width smaller than 1.
// Continue to show FL_CURSOR_NS at all times during drag. // Continue to show FL_CURSOR_NS at all times during drag.
// //
int offset = _dragging_y - Fl::event_y(); int offset = _dragging_y - _event_y;
int new_h = row_height(_resizing_row) - offset; int new_h = row_height(_resizing_row) - offset;
if ( new_h < _row_resize_min ) new_h = _row_resize_min; if ( new_h < _row_resize_min ) new_h = _row_resize_min;
row_height(_resizing_row, new_h); row_height(_resizing_row, new_h);
_dragging_y = Fl::event_y(); _dragging_y = _event_y;
table_resized(); table_resized();
redraw(); redraw();
change_cursor(FL_CURSOR_NS); change_cursor(FL_CURSOR_NS);
@ -854,7 +870,7 @@ int Fl_Table::handle(int event) {
do_callback(CONTEXT_RC_RESIZE, R, C); do_callback(CONTEXT_RC_RESIZE, R, C);
} }
} else { } else {
if (Fl::event_button() == 1 && if (_event_button == 1 &&
_selecting == CONTEXT_CELL && _selecting == CONTEXT_CELL &&
context == CONTEXT_CELL) { context == CONTEXT_CELL) {
if (select_row != R || select_col != C) { if (select_row != R || select_col != C) {
@ -864,7 +880,7 @@ int Fl_Table::handle(int event) {
select_col = C; select_col = C;
ret = 1; ret = 1;
} }
else if (Fl::event_button() == 1 && else if (_event_button == 1 &&
_selecting == CONTEXT_ROW_HEADER && _selecting == CONTEXT_ROW_HEADER &&
context & (CONTEXT_ROW_HEADER|CONTEXT_COL_HEADER|CONTEXT_CELL)) { context & (CONTEXT_ROW_HEADER|CONTEXT_COL_HEADER|CONTEXT_CELL)) {
if (select_row != R) { if (select_row != R) {
@ -873,7 +889,7 @@ int Fl_Table::handle(int event) {
select_row = R; select_row = R;
ret = 1; ret = 1;
} }
else if (Fl::event_button() == 1 && else if (_event_button == 1 &&
_selecting == CONTEXT_COL_HEADER _selecting == CONTEXT_COL_HEADER
&& context & (CONTEXT_ROW_HEADER|CONTEXT_COL_HEADER|CONTEXT_CELL)) { && context & (CONTEXT_ROW_HEADER|CONTEXT_COL_HEADER|CONTEXT_CELL)) {
if (select_col != C) { if (select_col != C) {
@ -885,10 +901,10 @@ int Fl_Table::handle(int event) {
} }
// Enable autodrag if not resizing, and mouse has moved off table edge // Enable autodrag if not resizing, and mouse has moved off table edge
if ( _resizing_row < 0 && _resizing_col < 0 && _auto_drag == 0 && if ( _resizing_row < 0 && _resizing_col < 0 && _auto_drag == 0 &&
( Fl::event_x() > x() + w() - 20 || ( _event_x > x() + w() - 20 ||
Fl::event_x() < x() + row_header_width() || _event_x < x() + row_header_width() ||
Fl::event_y() > y() + h() - 20 || _event_y > y() + h() - 20 ||
Fl::event_y() < y() + col_header_height() _event_y < y() + col_header_height()
) ) { ) ) {
_start_auto_drag(); _start_auto_drag();
} }
@ -903,7 +919,7 @@ int Fl_Table::handle(int event) {
case CONTEXT_TABLE: // release on dead zone case CONTEXT_TABLE: // release on dead zone
if ( _resizing_col == -1 && // not resizing a column if ( _resizing_col == -1 && // not resizing a column
_resizing_row == -1 && // not resizing a row _resizing_row == -1 && // not resizing a row
Fl_Widget::callback() && // callback defined Fl_Widget::callback() && // callback defined
when() & FL_WHEN_RELEASE && // on button release when() & FL_WHEN_RELEASE && // on button release
_last_row == R ) { // release on same row PUSHed? _last_row == R ) { // release on same row PUSHed?
// Need this for eg. left clicking on a cell to select it // Need this for eg. left clicking on a cell to select it
@ -914,7 +930,7 @@ int Fl_Table::handle(int event) {
default: default:
break; break;
} }
if ( Fl::event_button() == 1 ) { if ( _event_button == 1 ) {
change_cursor(FL_CURSOR_DEFAULT); change_cursor(FL_CURSOR_DEFAULT);
_resizing_col = -1; _resizing_col = -1;
_resizing_row = -1; _resizing_row = -1;
@ -964,7 +980,7 @@ int Fl_Table::handle(int event) {
ret = 0; ret = 0;
int is_row = select_row; int is_row = select_row;
int is_col = select_col; int is_col = select_col;
switch(Fl::event_key()) { switch(_event_key) {
case FL_Home: case FL_Home:
ret = move_cursor(0, -1000000); ret = move_cursor(0, -1000000);
break; break;
@ -990,7 +1006,7 @@ int Fl_Table::handle(int event) {
ret = move_cursor(1, 0); ret = move_cursor(1, 0);
break; break;
case FL_Tab: case FL_Tab:
if ( Fl::event_state() & FL_SHIFT ) { if ( _event_state & FL_SHIFT ) {
ret = move_cursor(0, -1); // shift-tab -> left ret = move_cursor(0, -1); // shift-tab -> left
} else { } else {
ret = move_cursor(0, 1); // tab -> right ret = move_cursor(0, 1); // tab -> right

View File

@ -155,23 +155,40 @@ void Fl_Table_Row::rows(int val) {
while ( val < (int)_rowselect.size() ) { _rowselect.pop_back(); } // shrink while ( val < (int)_rowselect.size() ) { _rowselect.pop_back(); } // shrink
} }
// #include "eventnames.h" // debugging //#define DEBUG 1
// #include <stdio.h> #ifdef DEBUG
#include <FL/names.h>
#define PRINTEVENT \
fprintf(stderr,"TableRow %s: ** Event: %s --\n", (label()?label():"none"), fl_eventnames[event]);
#else
#define PRINTEVENT
#endif
// Handle events // Handle events
int Fl_Table_Row::handle(int event) { int Fl_Table_Row::handle(int event) {
PRINTEVENT;
// fprintf(stderr, "** EVENT: %s: EVENT XY=%d,%d\n",
// eventnames[event], Fl::event_x(), Fl::event_y()); // debugging // Make snapshots of realtime event states *before* we service user's cb,
// which may do things like post popup menus that return with unexpected button states.
int _event_button = Fl::event_button();
//int _event_clicks = Fl::event_clicks(); // uncomment if needed
int _event_x = Fl::event_x();
int _event_y = Fl::event_y();
//int _event_key = Fl::event_key(); // uncomment if needed
int _event_state = Fl::event_state();
//Fl_Widget *_focus = Fl::focus(); // uncomment if needed
// Let base class handle event // Let base class handle event
// Note: base class may invoke user callbacks that post menus,
// so from here on use event state snapshots (above).
//
int ret = Fl_Table::handle(event); int ret = Fl_Table::handle(event);
// The following code disables cell selection.. why was it added? -erco 05/18/03 // 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) // 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 : int shiftstate = (_event_state & FL_CTRL) ? FL_CTRL :
(Fl::event_state() & FL_SHIFT) ? FL_SHIFT : 0; (_event_state & FL_SHIFT) ? FL_SHIFT : 0;
// Which row/column are we over? // Which row/column are we over?
int R, C; // row/column being worked on int R, C; // row/column being worked on
@ -179,9 +196,9 @@ int Fl_Table_Row::handle(int event) {
TableContext context = cursor2rowcol(R, C, resizeflag); TableContext context = cursor2rowcol(R, C, resizeflag);
switch ( event ) { switch ( event ) {
case FL_PUSH: case FL_PUSH:
if ( Fl::event_button() == 1 ) { if ( _event_button == 1 ) {
_last_push_x = Fl::event_x(); // save regardless of context _last_push_x = _event_x; // save regardless of context
_last_push_y = Fl::event_y(); // " " _last_push_y = _event_y; // " "
// Handle selection in table. // Handle selection in table.
// Select cell under cursor, and enable drag selection mode. // Select cell under cursor, and enable drag selection mode.
@ -230,7 +247,7 @@ int Fl_Table_Row::handle(int event) {
if ( offtop > 0 && row_position() > 0 ) { if ( offtop > 0 && row_position() > 0 ) {
// Only scroll in upward direction // Only scroll in upward direction
int diff = _last_y - Fl::event_y(); int diff = _last_y - _event_y;
if ( diff < 1 ) { if ( diff < 1 ) {
ret = 1; ret = 1;
break; break;
@ -241,7 +258,7 @@ int Fl_Table_Row::handle(int event) {
} }
else if ( offbot > 0 && botrow < rows() ) { else if ( offbot > 0 && botrow < rows() ) {
// Only scroll in downward direction // Only scroll in downward direction
int diff = Fl::event_y() - _last_y; int diff = _event_y - _last_y;
if ( diff < 1 ) { if ( diff < 1 ) {
ret = 1; ret = 1;
break; break;
@ -281,7 +298,7 @@ int Fl_Table_Row::handle(int event) {
} }
case FL_RELEASE: case FL_RELEASE:
if ( Fl::event_button() == 1 ) { if ( _event_button == 1 ) {
_dragging_select = 0; _dragging_select = 0;
ret = 1; // release handled ret = 1; // release handled
// Clicked off edges of data table? // Clicked off edges of data table?
@ -290,8 +307,8 @@ int Fl_Table_Row::handle(int event) {
int databot = tiy + table_h, int databot = tiy + table_h,
dataright = tix + table_w; dataright = tix + table_w;
if ( if (
( _last_push_x > dataright && Fl::event_x() > dataright ) || ( _last_push_x > dataright && _event_x > dataright ) ||
( _last_push_y > databot && Fl::event_y() > databot ) ( _last_push_y > databot && _event_y > databot )
) { ) {
select_all_rows(0); // clear previous selections select_all_rows(0); // clear previous selections
} }
@ -301,7 +318,7 @@ int Fl_Table_Row::handle(int event) {
default: default:
break; break;
} }
_last_y = Fl::event_y(); _last_y = _event_y;
return(ret); return(ret);
} }