|
fltk 1.3.0rc3
About: FLTK (Fast Light Tool Kit) is a cross-platform C++ GUI toolkit for UNIX/Linux (X11), Microsoft Windows, and MacOS X. Release candidate.
SfR Fresh Dox: fltk-1.3.0rc3-source.tar.gz ("inofficial" and yet experimental doxygen-generated source code documentation) ![]() |
00001 // 00002 // "$Id: Fl_Table.cxx 7950 2010-12-05 01:22:53Z greg.ercolano $" 00003 // 00004 // Fl_Table -- A table widget 00005 // 00006 // Copyright 2002 by Greg Ercolano. 00007 // Copyright (c) 2004 O'ksi'D 00008 // 00009 // This library is free software; you can redistribute it and/or 00010 // modify it under the terms of the GNU Library General Public 00011 // License as published by the Free Software Foundation; either 00012 // version 2 of the License, or (at your option) any later version. 00013 // 00014 // This library is distributed in the hope that it will be useful, 00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 // Library General Public License for more details. 00018 // 00019 // You should have received a copy of the GNU Library General Public 00020 // License along with this library; if not, write to the Free Software 00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 00022 // USA. 00023 // 00024 00025 #include <stdio.h> // fprintf 00026 #include <FL/fl_draw.H> 00027 #include <FL/Fl_Table.H> 00028 00029 #if defined(USE_UTF8) && ( defined(MICROSOFT) || defined(LINUX) ) 00030 #include <FL/fl_utf8.H> // currently only Windows and Linux 00031 #endif 00032 00033 #define SCROLLBAR_SIZE 16 00034 00035 // Scroll display so 'row' is at top 00036 void Fl_Table::row_position(int row) { 00037 if ( _row_position == row ) return; // OPTIMIZATION: no change? avoid redraw 00038 if ( row < 0 ) row = 0; 00039 else if ( row >= rows() ) row = rows() - 1; 00040 if ( table_h <= tih ) return; // don't scroll if table smaller than window 00041 double newtop = row_scroll_position(row); 00042 if ( newtop > vscrollbar->maximum() ) { 00043 newtop = vscrollbar->maximum(); 00044 } 00045 vscrollbar->Fl_Slider::value(newtop); 00046 table_scrolled(); 00047 redraw(); 00048 _row_position = row; // HACK: override what table_scrolled() came up with 00049 } 00050 00051 // Scroll display so 'col' is at left 00052 void Fl_Table::col_position(int col) { 00053 if ( _col_position == col ) return; // OPTIMIZATION: no change? avoid redraw 00054 if ( col < 0 ) col = 0; 00055 else if ( col >= cols() ) col = cols() - 1; 00056 if ( table_w <= tiw ) return; // don't scroll if table smaller than window 00057 double newleft = col_scroll_position(col); 00058 if ( newleft > hscrollbar->maximum() ) { 00059 newleft = hscrollbar->maximum(); 00060 } 00061 hscrollbar->Fl_Slider::value(newleft); 00062 table_scrolled(); 00063 redraw(); 00064 _col_position = col; // HACK: override what table_scrolled() came up with 00065 } 00066 00067 // Find scroll position of a row (in pixels) 00068 long Fl_Table::row_scroll_position(int row) { 00069 int startrow = 0; 00070 long scroll = 0; 00071 // OPTIMIZATION: 00072 // Attempt to use precomputed row scroll position 00073 // 00074 if ( toprow_scrollpos != -1 && row >= toprow ) { 00075 scroll = toprow_scrollpos; 00076 startrow = toprow; 00077 } 00078 for ( int t=startrow; t<row; t++ ) { 00079 scroll += row_height(t); 00080 } 00081 return(scroll); 00082 } 00083 00084 // Find scroll position of a column (in pixels) 00085 long Fl_Table::col_scroll_position(int col) { 00086 int startcol = 0; 00087 long scroll = 0; 00088 // OPTIMIZATION: 00089 // Attempt to use precomputed row scroll position 00090 // 00091 if ( leftcol_scrollpos != -1 && col >= leftcol ) { 00092 scroll = leftcol_scrollpos; 00093 startcol = leftcol; 00094 } 00095 for ( int t=startcol; t<col; t++ ) { 00096 scroll += col_width(t); 00097 } 00098 return(scroll); 00099 } 00100 00101 // Ctor 00102 Fl_Table::Fl_Table(int X, int Y, int W, int H, const char *l) : Fl_Group(X,Y,W,H,l) { 00103 _rows = 0; 00104 _cols = 0; 00105 _row_header_w = 40; 00106 _col_header_h = 18; 00107 _row_header = 0; 00108 _col_header = 0; 00109 _row_header_color = color(); 00110 _col_header_color = color(); 00111 _row_resize = 0; 00112 _col_resize = 0; 00113 _row_resize_min = 1; 00114 _col_resize_min = 1; 00115 _redraw_toprow = -1; 00116 _redraw_botrow = -1; 00117 _redraw_leftcol = -1; 00118 _redraw_rightcol = -1; 00119 table_w = 0; 00120 table_h = 0; 00121 toprow = 0; 00122 botrow = 0; 00123 leftcol = 0; 00124 rightcol = 0; 00125 toprow_scrollpos = -1; 00126 leftcol_scrollpos = -1; 00127 _last_cursor = FL_CURSOR_DEFAULT; 00128 _resizing_col = -1; 00129 _resizing_row = -1; 00130 _dragging_x = -1; 00131 _dragging_y = -1; 00132 _last_row = -1; 00133 _auto_drag = 0; 00134 current_col = -1; 00135 current_row = -1; 00136 select_row = -1; 00137 select_col = -1; 00138 00139 box(FL_THIN_DOWN_FRAME); 00140 00141 vscrollbar = new Fl_Scrollbar(x()+w()-SCROLLBAR_SIZE, y(), 00142 SCROLLBAR_SIZE, h()-SCROLLBAR_SIZE); 00143 vscrollbar->type(FL_VERTICAL); 00144 vscrollbar->callback(scroll_cb, (void*)this); 00145 00146 hscrollbar = new Fl_Scrollbar(x(), y()+h()-SCROLLBAR_SIZE, 00147 w(), SCROLLBAR_SIZE); 00148 hscrollbar->type(FL_HORIZONTAL); 00149 hscrollbar->callback(scroll_cb, (void*)this); 00150 00151 table = new Fl_Scroll(x(), y(), w(), h()); 00152 table->box(FL_NO_BOX); 00153 table->type(0); // don't show Fl_Scroll's scrollbars -- use our own 00154 table->hide(); // hide unless children are present 00155 table->end(); 00156 00157 table_resized(); 00158 redraw(); 00159 00160 Fl_Group::end(); // end the group's begin() 00161 00162 table->begin(); // leave with fltk children getting added to the scroll 00163 } 00164 00165 // Dtor 00166 Fl_Table::~Fl_Table() { 00167 // The parent Fl_Group takes care of destroying scrollbars 00168 } 00169 00170 // Set height of a row 00171 void Fl_Table::row_height(int row, int height) { 00172 if ( row < 0 ) return; 00173 if ( row < (int)_rowheights.size() && _rowheights[row] == height ) { 00174 return; // OPTIMIZATION: no change? avoid redraw 00175 } 00176 // Add row heights, even if none yet 00177 int now_size = (int)_rowheights.size(); 00178 if ( row >= now_size ) { 00179 _rowheights.size(row); 00180 while (now_size < row) 00181 _rowheights[now_size++] = height; 00182 } 00183 _rowheights[row] = height; 00184 table_resized(); 00185 if ( row <= botrow ) { // OPTIMIZATION: only redraw if onscreen or above screen 00186 redraw(); 00187 } 00188 // ROW RESIZE CALLBACK 00189 if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED ) { 00190 do_callback(CONTEXT_RC_RESIZE, row, 0); 00191 } 00192 } 00193 00194 // Set width of a column 00195 void Fl_Table::col_width(int col, int width) 00196 { 00197 if ( col < 0 ) return; 00198 if ( col < (int)_colwidths.size() && _colwidths[col] == width ) { 00199 return; // OPTIMIZATION: no change? avoid redraw 00200 } 00201 // Add column widths, even if none yet 00202 int now_size = (int)_colwidths.size(); 00203 if ( col >= now_size ) { 00204 _colwidths.size(col); 00205 while (now_size < col) { 00206 _colwidths[now_size++] = width; 00207 } 00208 } 00209 _colwidths[col] = width; 00210 table_resized(); 00211 if ( col <= rightcol ) { // OPTIMIZATION: only redraw if onscreen or to the left 00212 redraw(); 00213 } 00214 // COLUMN RESIZE CALLBACK 00215 if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED ) { 00216 do_callback(CONTEXT_RC_RESIZE, 0, col); 00217 } 00218 } 00219 00220 // Return row/col clamped to reality 00221 int Fl_Table::row_col_clamp(TableContext context, int &R, int &C) { 00222 int clamped = 0; 00223 if ( R < 0 ) { R = 0; clamped = 1; } 00224 if ( C < 0 ) { C = 0; clamped = 1; } 00225 switch ( context ) { 00226 case CONTEXT_COL_HEADER: 00227 // Allow col headers to draw even if no rows 00228 if ( R >= _rows && R != 0 ) { R = _rows - 1; clamped = 1; } 00229 break; 00230 00231 case CONTEXT_ROW_HEADER: 00232 // Allow row headers to draw even if no columns 00233 if ( C >= _cols && C != 0 ) { C = _cols - 1; clamped = 1; } 00234 break; 00235 00236 case CONTEXT_CELL: 00237 default: 00238 // CLAMP R/C TO _rows/_cols 00239 if ( R >= _rows ) { R = _rows - 1; clamped = 1; } 00240 if ( C >= _cols ) { C = _cols - 1; clamped = 1; } 00241 break; 00242 } 00243 return(clamped); 00244 } 00245 00246 // Return bounding region for given context 00247 void Fl_Table::get_bounds(TableContext context, int &X, int &Y, int &W, int &H) { 00248 switch ( context ) { 00249 case CONTEXT_COL_HEADER: 00250 // Column header clipping. 00251 X = tox; 00252 Y = wiy; 00253 W = tow; 00254 H = col_header_height(); 00255 return; 00256 00257 case CONTEXT_ROW_HEADER: 00258 // Row header clipping. 00259 X = wix; 00260 Y = toy; 00261 W = row_header_width(); 00262 H = toh; 00263 return; 00264 00265 case CONTEXT_TABLE: 00266 // Table inner dimensions 00267 X = tix; Y = tiy; W = tiw; H = tih; 00268 return; 00269 00270 // TODO: Add other contexts.. 00271 default: 00272 fprintf(stderr, "Fl_Table::get_bounds(): context %d unimplemented\n", (int)context); 00273 return; 00274 } 00275 //NOTREACHED 00276 } 00277 00278 // Find row/col beneath cursor 00279 // 00280 // Returns R/C and context. 00281 // Also returns resizeflag, if mouse is hovered over a resize boundary. 00282 // 00283 Fl_Table::TableContext Fl_Table::cursor2rowcol(int &R, int &C, ResizeFlag &resizeflag) { 00284 // return values 00285 R = C = 0; 00286 resizeflag = RESIZE_NONE; 00287 // Row header? 00288 int X, Y, W, H; 00289 if ( row_header() ) { 00290 // Inside a row heading? 00291 get_bounds(CONTEXT_ROW_HEADER, X, Y, W, H); 00292 if ( Fl::event_inside(X, Y, W, H) ) { 00293 // Scan visible rows until found 00294 for ( R = toprow; R <= botrow; R++ ) { 00295 find_cell(CONTEXT_ROW_HEADER, R, 0, X, Y, W, H); 00296 if ( Fl::event_y() >= Y && Fl::event_y() < (Y+H) ) { 00297 // Found row? 00298 // If cursor over resize boundary, and resize enabled, 00299 // enable the appropriate resize flag. 00300 // 00301 if ( row_resize() ) { 00302 if ( Fl::event_y() <= (Y+3-0) ) { resizeflag = RESIZE_ROW_ABOVE; } 00303 if ( Fl::event_y() >= (Y+H-3) ) { resizeflag = RESIZE_ROW_BELOW; } 00304 } 00305 return(CONTEXT_ROW_HEADER); 00306 } 00307 } 00308 // Must be in row header dead zone 00309 return(CONTEXT_NONE); 00310 } 00311 } 00312 // Column header? 00313 if ( col_header() ) { 00314 // Inside a column heading? 00315 get_bounds(CONTEXT_COL_HEADER, X, Y, W, H); 00316 if ( Fl::event_inside(X, Y, W, H) ) { 00317 // Scan visible columns until found 00318 for ( C = leftcol; C <= rightcol; C++ ) { 00319 find_cell(CONTEXT_COL_HEADER, 0, C, X, Y, W, H); 00320 if ( Fl::event_x() >= X && Fl::event_x() < (X+W) ) { 00321 // Found column? 00322 // If cursor over resize boundary, and resize enabled, 00323 // enable the appropriate resize flag. 00324 // 00325 if ( col_resize() ) { 00326 if ( Fl::event_x() <= (X+3-0) ) { resizeflag = RESIZE_COL_LEFT; } 00327 if ( Fl::event_x() >= (X+W-3) ) { resizeflag = RESIZE_COL_RIGHT; } 00328 } 00329 return(CONTEXT_COL_HEADER); 00330 } 00331 } 00332 // Must be in column header dead zone 00333 return(CONTEXT_NONE); 00334 } 00335 } 00336 // Mouse somewhere in table? 00337 // Scan visible r/c's until we find it. 00338 // 00339 if ( Fl::event_inside(tox, toy, tow, toh) ) { 00340 for ( R = toprow; R <= botrow; R++ ) { 00341 find_cell(CONTEXT_CELL, R, C, X, Y, W, H); 00342 if ( Fl::event_y() < Y ) break; // OPT: thanks lars 00343 if ( Fl::event_y() >= (Y+H) ) continue; // OPT: " " 00344 for ( C = leftcol; C <= rightcol; C++ ) { 00345 find_cell(CONTEXT_CELL, R, C, X, Y, W, H); 00346 if ( Fl::event_inside(X, Y, W, H) ) { 00347 return(CONTEXT_CELL); // found it 00348 } 00349 } 00350 } 00351 // Must be in a dead zone of the table 00352 R = C = 0; 00353 return(CONTEXT_TABLE); 00354 } 00355 // Somewhere else 00356 return(CONTEXT_NONE); 00357 } 00358 00359 // Find X/Y/W/H for cell at R/C 00360 // If R or C are out of range, returns -1 00361 // with X/Y/W/H set to zero. 00362 // 00363 int Fl_Table::find_cell(TableContext context, int R, int C, int &X, int &Y, int &W, int &H) { 00364 if ( row_col_clamp(context, R, C) ) { // row or col out of range? error 00365 X=Y=W=H=0; 00366 return(-1); 00367 } 00368 X = col_scroll_position(C) - hscrollbar->value() + tix; 00369 Y = row_scroll_position(R) - vscrollbar->value() + tiy; 00370 W = col_width(C); 00371 H = row_height(R); 00372 00373 switch ( context ) { 00374 case CONTEXT_COL_HEADER: 00375 Y = wiy; 00376 H = col_header_height(); 00377 return(0); 00378 00379 case CONTEXT_ROW_HEADER: 00380 X = wix; 00381 W = row_header_width(); 00382 return(0); 00383 00384 case CONTEXT_CELL: 00385 return(0); 00386 00387 case CONTEXT_TABLE: 00388 return(0); 00389 00390 // TODO -- HANDLE OTHER CONTEXTS 00391 default: 00392 fprintf(stderr, "Fl_Table::find_cell: unknown context %d\n", (int)context); 00393 return(-1); 00394 } 00395 //NOTREACHED 00396 } 00397 00398 // Enable automatic scroll-selection 00399 void Fl_Table::_start_auto_drag() { 00400 if (_auto_drag) return; 00401 _auto_drag = 1; 00402 Fl::add_timeout(0.3, _auto_drag_cb2, this); 00403 } 00404 00405 // Disable automatic scroll-selection 00406 void Fl_Table::_stop_auto_drag() { 00407 if (!_auto_drag) return; 00408 Fl::remove_timeout(_auto_drag_cb2, this); 00409 _auto_drag = 0; 00410 } 00411 00412 void Fl_Table::_auto_drag_cb2(void *d) { 00413 ((Fl_Table*)d)->_auto_drag_cb(); 00414 } 00415 00416 // Handle automatic scroll-selection if mouse selection dragged off table edge 00417 void Fl_Table::_auto_drag_cb() { 00418 int lx = Fl::e_x; 00419 int ly = Fl::e_y; 00420 if (_selecting == CONTEXT_COL_HEADER) 00421 { ly = y() + col_header_height(); } 00422 else if (_selecting == CONTEXT_ROW_HEADER) 00423 { lx = x() + row_header_width(); } 00424 if (lx > x() + w() - 20) { 00425 Fl::e_x = x() + w() - 20; 00426 if (hscrollbar->visible()) 00427 ((Fl_Slider*)hscrollbar)->value(hscrollbar->clamp(hscrollbar->value() + 30)); 00428 hscrollbar->do_callback(); 00429 _dragging_x = Fl::e_x - 30; 00430 } 00431 else if (lx < (x() + row_header_width())) { 00432 Fl::e_x = x() + row_header_width() + 1; 00433 if (hscrollbar->visible()) { 00434 ((Fl_Slider*)hscrollbar)->value(hscrollbar->clamp(hscrollbar->value() - 30)); 00435 } 00436 hscrollbar->do_callback(); 00437 _dragging_x = Fl::e_x + 30; 00438 } 00439 if (ly > y() + h() - 20) { 00440 Fl::e_y = y() + h() - 20; 00441 if (vscrollbar->visible()) { 00442 ((Fl_Slider*)vscrollbar)->value(vscrollbar->clamp(vscrollbar->value() + 30)); 00443 } 00444 vscrollbar->do_callback(); 00445 _dragging_y = Fl::e_y - 30; 00446 } 00447 else if (ly < (y() + col_header_height())) { 00448 Fl::e_y = y() + col_header_height() + 1; 00449 if (vscrollbar->visible()) { 00450 ((Fl_Slider*)vscrollbar)->value(vscrollbar->clamp(vscrollbar->value() - 30)); 00451 } 00452 vscrollbar->do_callback(); 00453 _dragging_y = Fl::e_y + 30; 00454 } 00455 _auto_drag = 2; 00456 handle(FL_DRAG); 00457 _auto_drag = 1; 00458 Fl::e_x = lx; 00459 Fl::e_y = ly; 00460 Fl::check(); 00461 Fl::flush(); 00462 if (Fl::event_buttons() && _auto_drag) { 00463 Fl::add_timeout(0.05, _auto_drag_cb2, this); 00464 } 00465 } 00466 00467 // Recalculate the window dimensions 00468 void Fl_Table::recalc_dimensions() { 00469 // Recalc to* (Table Outer), ti* (Table Inner), wi* ( Widget Inner) 00470 wix = ( x() + Fl::box_dx(box())); tox = wix; tix = tox + Fl::box_dx(table->box()); 00471 wiy = ( y() + Fl::box_dy(box())); toy = wiy; tiy = toy + Fl::box_dy(table->box()); 00472 wiw = ( w() - Fl::box_dw(box())); tow = wiw; tiw = tow - Fl::box_dw(table->box()); 00473 wih = ( h() - Fl::box_dh(box())); toh = wih; tih = toh - Fl::box_dh(table->box()); 00474 // Trim window if headers enabled 00475 if ( col_header() ) { 00476 tiy += col_header_height(); toy += col_header_height(); 00477 tih -= col_header_height(); toh -= col_header_height(); 00478 } 00479 if ( row_header() ) { 00480 tix += row_header_width(); tox += row_header_width(); 00481 tiw -= row_header_width(); tow -= row_header_width(); 00482 } 00483 // Make scroll bars disappear if window large enough 00484 { 00485 // First pass: can hide via window size? 00486 int hidev = (table_h <= tih); 00487 int hideh = (table_w <= tiw); 00488 // Second pass: Check for interference 00489 if ( !hideh & hidev ) { hidev = (( table_h - tih + SCROLLBAR_SIZE ) <= 0 ); } 00490 if ( !hidev & hideh ) { hideh = (( table_w - tiw + SCROLLBAR_SIZE ) <= 0 ); } 00491 // Determine scrollbar visibility, trim ti[xywh]/to[xywh] 00492 if ( hidev ) { vscrollbar->hide(); } 00493 else { vscrollbar->show(); tiw -= SCROLLBAR_SIZE; tow -= SCROLLBAR_SIZE; } 00494 if ( hideh ) { hscrollbar->hide(); } 00495 else { hscrollbar->show(); tih -= SCROLLBAR_SIZE; toh -= SCROLLBAR_SIZE; } 00496 } 00497 // Resize the child table 00498 table->resize(tox, toy, tow, toh); 00499 table->init_sizes(); 00500 } 00501 00502 // Recalculate internals after a scroll. 00503 // 00504 // Call this if table has been scrolled or resized. 00505 // Does not handle redraw(). 00506 // TODO: Assumes ti[xywh] has already been recalculated. 00507 // 00508 void Fl_Table::table_scrolled() { 00509 // Find top row 00510 int y, row, voff = vscrollbar->value(); 00511 for ( row=y=0; row < _rows; row++ ) { 00512 y += row_height(row); 00513 if ( y > voff ) { y -= row_height(row); break; } 00514 } 00515 _row_position = toprow = ( row >= _rows ) ? (row - 1) : row; 00516 toprow_scrollpos = y; // OPTIMIZATION: save for later use 00517 // Find bottom row 00518 voff = vscrollbar->value() + tih; 00519 for ( ; row < _rows; row++ ) { 00520 y += row_height(row); 00521 if ( y >= voff ) { break; } 00522 } 00523 botrow = ( row >= _rows ) ? (row - 1) : row; 00524 // Left column 00525 int x, col, hoff = hscrollbar->value(); 00526 for ( col=x=0; col < _cols; col++ ) { 00527 x += col_width(col); 00528 if ( x > hoff ) { x -= col_width(col); break; } 00529 } 00530 _col_position = leftcol = ( col >= _cols ) ? (col - 1) : col; 00531 leftcol_scrollpos = x; // OPTIMIZATION: save for later use 00532 // Right column 00533 // Work with data left over from leftcol calculation 00534 // 00535 hoff = hscrollbar->value() + tiw; 00536 for ( ; col < _cols; col++ ) { 00537 x += col_width(col); 00538 if ( x >= hoff ) { break; } 00539 } 00540 rightcol = ( col >= _cols ) ? (col - 1) : col; 00541 // First tell children to scroll 00542 draw_cell(CONTEXT_RC_RESIZE, 0,0,0,0,0,0); 00543 } 00544 00545 // Table resized: recalc internal data 00546 // Call this whenever the window is resized. 00547 // Recalculates the scrollbar sizes. 00548 // Makes no assumptions about any pre-initialized data. 00549 // 00550 void Fl_Table::table_resized() { 00551 table_h = row_scroll_position(rows()); 00552 table_w = col_scroll_position(cols()); 00553 recalc_dimensions(); 00554 // Recalc scrollbar sizes 00555 // Clamp scrollbar value() after a resize. 00556 // Resize scrollbars to enforce a constant trough width after a window resize. 00557 // 00558 { 00559 // Vertical scrollbar 00560 float vscrolltab = ( table_h == 0 || tih > table_h ) ? 1 : (float)tih / table_h; 00561 float hscrolltab = ( table_w == 0 || tiw > table_w ) ? 1 : (float)tiw / table_w; 00562 vscrollbar->bounds(0, table_h-tih); 00563 vscrollbar->precision(10); 00564 vscrollbar->slider_size(vscrolltab); 00565 vscrollbar->resize(wix+wiw-SCROLLBAR_SIZE, wiy, 00566 SCROLLBAR_SIZE, 00567 wih - ((hscrollbar->visible())?SCROLLBAR_SIZE:0)); 00568 vscrollbar->Fl_Valuator::value(vscrollbar->clamp(vscrollbar->value())); 00569 // Horizontal scrollbar 00570 hscrollbar->bounds(0, table_w-tiw); 00571 hscrollbar->precision(10); 00572 hscrollbar->slider_size(hscrolltab); 00573 hscrollbar->resize(wix, wiy+wih-SCROLLBAR_SIZE, 00574 wiw - ((vscrollbar->visible())?SCROLLBAR_SIZE:0), 00575 SCROLLBAR_SIZE); 00576 hscrollbar->Fl_Valuator::value(hscrollbar->clamp(hscrollbar->value())); 00577 } 00578 00579 // Tell FLTK child widgets were resized 00580 Fl_Group::init_sizes(); 00581 00582 // Recalc top/bot/left/right 00583 table_scrolled(); 00584 00585 // DO *NOT* REDRAW -- LEAVE THIS UP TO THE CALLER 00586 // redraw(); 00587 } 00588 00589 // Someone moved a scrollbar 00590 void Fl_Table::scroll_cb(Fl_Widget*w, void *data) { 00591 Fl_Table *o = (Fl_Table*)data; 00592 o->recalc_dimensions(); // recalc tix, tiy, etc. 00593 o->table_scrolled(); 00594 o->redraw(); 00595 } 00596 00597 // Set number of rows 00598 void Fl_Table::rows(int val) { 00599 int oldrows = _rows; 00600 _rows = val; 00601 { 00602 int default_h = ( _rowheights.size() > 0 ) ? _rowheights.back() : 25; 00603 int now_size = _rowheights.size(); 00604 _rowheights.size(val); // enlarge or shrink as needed 00605 while ( now_size < val ) { 00606 _rowheights[now_size++] = default_h; // fill new 00607 } 00608 } 00609 table_resized(); 00610 00611 // OPTIMIZATION: redraw only if change is visible. 00612 if ( val >= oldrows && oldrows > botrow ) { 00613 // NO REDRAW 00614 } else { 00615 redraw(); 00616 } 00617 } 00618 00619 // Set number of cols 00620 void Fl_Table::cols(int val) { 00621 _cols = val; 00622 { 00623 int default_w = ( _colwidths.size() > 0 ) ? _colwidths[_colwidths.size()-1] : 80; 00624 int now_size = _colwidths.size(); 00625 _colwidths.size(val); // enlarge or shrink as needed 00626 while ( now_size < val ) { 00627 _colwidths[now_size++] = default_w; // fill new 00628 } 00629 } 00630 table_resized(); 00631 redraw(); 00632 } 00633 00634 // Change mouse cursor to different type 00635 void Fl_Table::change_cursor(Fl_Cursor newcursor) { 00636 if ( newcursor != _last_cursor ) { 00637 fl_cursor(newcursor, FL_BLACK, FL_WHITE); 00638 _last_cursor = newcursor; 00639 } 00640 } 00641 00642 void Fl_Table::damage_zone(int r1, int c1, int r2, int c2, int r3, int c3) { 00643 int R1 = r1, C1 = c1; 00644 int R2 = r2, C2 = c2; 00645 if (r1 > R2) R2 = r1; 00646 if (r2 < R1) R1 = r2; 00647 if (r3 > R2) R2 = r3; 00648 if (r3 < R1) R1 = r3; 00649 if (c1 > C2) C2 = c1; 00650 if (c2 < C1) C1 = c2; 00651 if (c3 > C2) C2 = c3; 00652 if (c3 < C1) C1 = c3; 00653 if (R1 < 0) { 00654 if (R2 < 0) return; 00655 R1 = 0; 00656 } 00657 if (C1 < 0) { 00658 if (C2 < 0) return; 00659 C1 = 0; 00660 } 00661 if (R1 < toprow) R1 = toprow; 00662 if (R2 > botrow) R2 = botrow; 00663 if (C1 < leftcol) C1 = leftcol; 00664 if (C2 > rightcol) C2 = rightcol; 00665 redraw_range(R1, R2, C1, C2); 00666 } 00667 00668 int Fl_Table::move_cursor(int R, int C) { 00669 if (select_row == -1) R++; 00670 if (select_col == -1) C++; 00671 R += select_row; 00672 C += select_col; 00673 if (R < 0) R = 0; 00674 if (R >= rows()) R = rows() - 1; 00675 if (C < 0) C = 0; 00676 if (C >= cols()) C = cols() - 1; 00677 if (R == select_row && C == select_col) return 0; 00678 damage_zone(current_row, current_col, select_row, select_col, R, C); 00679 select_row = R; 00680 select_col = C; 00681 if (!Fl::event_state(FL_SHIFT)) { 00682 current_row = R; 00683 current_col = C; 00684 } 00685 if (R < toprow + 1 || R > botrow - 1) row_position(R); 00686 if (C < leftcol + 1 || C > rightcol - 1) col_position(C); 00687 return 1; 00688 } 00689 00690 // #define DEBUG 1 00691 #ifdef DEBUG 00692 #include "eventnames.h" 00693 #define PRINTEVENT \ 00694 fprintf(stderr,"Table %s: ** Event: %s --\n", (label()?label():"none"), eventnames[event]); 00695 #else 00696 #define PRINTEVENT 00697 #endif 00698 00699 // Handle FLTK events 00700 int Fl_Table::handle(int event) { 00701 PRINTEVENT; 00702 int ret = Fl_Group::handle(event); // let FLTK group handle events first 00703 if (ret) { 00704 if (Fl::event_inside(hscrollbar) || Fl::event_inside(vscrollbar)) return 1; 00705 if (Fl::focus() != this && contains(Fl::focus())) return 1; 00706 } 00707 // Which row/column are we over? 00708 int R, C; // row/column being worked on 00709 ResizeFlag resizeflag; // which resizing area are we over? (0=none) 00710 TableContext context = cursor2rowcol(R, C, resizeflag); 00711 switch ( event ) { 00712 case FL_PUSH: 00713 if (Fl::event_button() == 1 && !Fl::event_clicks()) { 00714 if (Fl::focus() != this) { 00715 take_focus(); 00716 do_callback(CONTEXT_TABLE, -1, -1); 00717 ret = 1; 00718 } 00719 damage_zone(current_row, current_col, select_row, select_col, R, C); 00720 if (context == CONTEXT_CELL) { 00721 current_row = select_row = R; 00722 current_col = select_col = C; 00723 _selecting = CONTEXT_CELL; 00724 } else { 00725 current_row = select_row = -1; 00726 current_col = select_col = -1; 00727 } 00728 } 00729 // Need this for eg. right click to pop up a menu 00730 if ( Fl_Widget::callback() && // callback defined? 00731 resizeflag == RESIZE_NONE ) { // not resizing? 00732 do_callback(context, R, C); // do callback 00733 } 00734 switch ( context ) { 00735 case CONTEXT_CELL: 00736 // FL_PUSH on a cell? 00737 ret = 1; // express interest in FL_RELEASE 00738 break; 00739 00740 case CONTEXT_NONE: 00741 // FL_PUSH on table corner? 00742 if ( Fl::event_button() == 1 && 00743 Fl::event_x() < x() + row_header_width()) { 00744 current_col = 0; 00745 select_col = cols() - 1; 00746 current_row = 0; 00747 select_row = rows() - 1; 00748 damage_zone(current_row, current_col, select_row, select_col); 00749 ret = 1; 00750 } 00751 break; 00752 00753 case CONTEXT_COL_HEADER: 00754 // FL_PUSH on a column header? 00755 if ( Fl::event_button() == 1) { 00756 // Resizing? Handle it 00757 if ( resizeflag ) { 00758 // Start resize if left click on column border. 00759 // "ret=1" ensures we get drag events from now on. 00760 // (C-1) is used if mouse is over the left hand side 00761 // of cell, so we resize the next column on the left. 00762 // 00763 _resizing_col = ( resizeflag & RESIZE_COL_LEFT ) ? C-1 : C; 00764 _resizing_row = -1; 00765 _dragging_x = Fl::event_x(); 00766 ret = 1; 00767 } else { 00768 // Not resizing? Select the column 00769 current_col = select_col = C; 00770 current_row = 0; 00771 select_row = rows() - 1; 00772 _selecting = CONTEXT_COL_HEADER; 00773 damage_zone(current_row, current_col, select_row, select_col); 00774 ret = 1; 00775 } 00776 } 00777 break; 00778 00779 case CONTEXT_ROW_HEADER: 00780 // FL_PUSH on a row header? 00781 if ( Fl::event_button() == 1 ) { 00782 // Resizing? Handle it 00783 if ( resizeflag ) { 00784 // Start resize if left mouse clicked on row border. 00785 // "ret = 1" ensures we get drag events from now on. 00786 // (R-1) is used if mouse is over the top of the cell, 00787 // so that we resize the row above. 00788 // 00789 _resizing_row = ( resizeflag & RESIZE_ROW_ABOVE ) ? R-1 : R; 00790 _resizing_col = -1; 00791 _dragging_y = Fl::event_y(); 00792 ret = 1; 00793 } else { 00794 // Not resizing? Select the row 00795 current_row = select_row = R; 00796 current_col = 0; 00797 select_col = cols() - 1; 00798 _selecting = CONTEXT_ROW_HEADER; 00799 damage_zone(current_row, current_col, select_row, select_col); 00800 ret = 1; 00801 } 00802 } 00803 break; 00804 00805 default: 00806 ret = 0; // express disinterest 00807 break; 00808 } 00809 _last_row = R; 00810 break; 00811 00812 case FL_DRAG: 00813 if (_auto_drag == 1) { 00814 ret = 1; 00815 break; 00816 } 00817 if ( _resizing_col > -1 ) { 00818 // Dragging column? 00819 // 00820 // Let user drag even /outside/ the row/col widget. 00821 // Don't allow column width smaller than 1. 00822 // Continue to show FL_CURSOR_WE at all times during drag. 00823 // 00824 int offset = _dragging_x - Fl::event_x(); 00825 int new_w = col_width(_resizing_col) - offset; 00826 if ( new_w < _col_resize_min ) new_w = _col_resize_min; 00827 col_width(_resizing_col, new_w); 00828 _dragging_x = Fl::event_x(); 00829 table_resized(); 00830 redraw(); 00831 change_cursor(FL_CURSOR_WE); 00832 ret = 1; 00833 if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED ) { 00834 do_callback(CONTEXT_RC_RESIZE, R, C); 00835 } 00836 } 00837 else if ( _resizing_row > -1 ) { 00838 // Dragging row? 00839 // 00840 // Let user drag even /outside/ the row/col widget. 00841 // Don't allow row width smaller than 1. 00842 // Continue to show FL_CURSOR_NS at all times during drag. 00843 // 00844 int offset = _dragging_y - Fl::event_y(); 00845 int new_h = row_height(_resizing_row) - offset; 00846 if ( new_h < _row_resize_min ) new_h = _row_resize_min; 00847 row_height(_resizing_row, new_h); 00848 _dragging_y = Fl::event_y(); 00849 table_resized(); 00850 redraw(); 00851 change_cursor(FL_CURSOR_NS); 00852 ret = 1; 00853 if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED ) { 00854 do_callback(CONTEXT_RC_RESIZE, R, C); 00855 } 00856 } else { 00857 if (Fl::event_button() == 1 && 00858 _selecting == CONTEXT_CELL && 00859 context == CONTEXT_CELL) { 00860 if (select_row != R || select_col != C) { 00861 damage_zone(current_row, current_col, select_row, select_col, R, C); 00862 } 00863 select_row = R; 00864 select_col = C; 00865 ret = 1; 00866 } 00867 else if (Fl::event_button() == 1 && 00868 _selecting == CONTEXT_ROW_HEADER && 00869 context & (CONTEXT_ROW_HEADER|CONTEXT_COL_HEADER|CONTEXT_CELL)) { 00870 if (select_row != R) { 00871 damage_zone(current_row, current_col, select_row, select_col, R, C); 00872 } 00873 select_row = R; 00874 ret = 1; 00875 } 00876 else if (Fl::event_button() == 1 && 00877 _selecting == CONTEXT_COL_HEADER 00878 && context & (CONTEXT_ROW_HEADER|CONTEXT_COL_HEADER|CONTEXT_CELL)) { 00879 if (select_col != C) { 00880 damage_zone(current_row, current_col, select_row, select_col, R, C); 00881 } 00882 select_col = C; 00883 ret = 1; 00884 } 00885 } 00886 // Enable autodrag if not resizing, and mouse has moved off table edge 00887 if ( _resizing_row < 0 && _resizing_col < 0 && _auto_drag == 0 && 00888 ( Fl::event_x() > x() + w() - 20 || 00889 Fl::event_x() < x() + row_header_width() || 00890 Fl::event_y() > y() + h() - 20 || 00891 Fl::event_y() < y() + col_header_height() 00892 ) ) { 00893 _start_auto_drag(); 00894 } 00895 break; 00896 00897 case FL_RELEASE: 00898 _stop_auto_drag(); 00899 switch ( context ) { 00900 case CONTEXT_ROW_HEADER: // release on row header 00901 case CONTEXT_COL_HEADER: // release on col header 00902 case CONTEXT_CELL: // release on a cell 00903 case CONTEXT_TABLE: // release on dead zone 00904 if ( _resizing_col == -1 && // not resizing a column 00905 _resizing_row == -1 && // not resizing a row 00906 Fl_Widget::callback() && // callback defined 00907 when() & FL_WHEN_RELEASE && // on button release 00908 _last_row == R ) { // release on same row PUSHed? 00909 // Need this for eg. left clicking on a cell to select it 00910 do_callback(context, R, C); 00911 } 00912 break; 00913 00914 default: 00915 break; 00916 } 00917 if ( Fl::event_button() == 1 ) { 00918 change_cursor(FL_CURSOR_DEFAULT); 00919 _resizing_col = -1; 00920 _resizing_row = -1; 00921 ret = 1; 00922 } 00923 break; 00924 00925 case FL_MOVE: 00926 if ( context == CONTEXT_COL_HEADER && // in column header? 00927 resizeflag ) { // resize + near boundary? 00928 change_cursor(FL_CURSOR_WE); // show resize cursor 00929 } 00930 else if ( context == CONTEXT_ROW_HEADER && // in row header? 00931 resizeflag ) { // resize + near boundary? 00932 change_cursor(FL_CURSOR_NS); // show resize cursor 00933 } else { 00934 change_cursor(FL_CURSOR_DEFAULT); // normal cursor 00935 } 00936 ret = 1; 00937 break; 00938 00939 case FL_ENTER: // See FLTK event docs on the FL_ENTER widget 00940 if (!ret) take_focus(); 00941 ret = 1; 00942 //FALLTHROUGH 00943 00944 case FL_LEAVE: // We want to track the mouse if resizing is allowed. 00945 if ( resizeflag ) { 00946 ret = 1; 00947 } 00948 if ( event == FL_LEAVE ) { 00949 _stop_auto_drag(); 00950 change_cursor(FL_CURSOR_DEFAULT); 00951 } 00952 break; 00953 00954 case FL_FOCUS: 00955 Fl::focus(this); 00956 //FALLTHROUGH 00957 00958 case FL_UNFOCUS: 00959 _stop_auto_drag(); 00960 ret = 1; 00961 break; 00962 00963 case FL_KEYBOARD: { 00964 ret = 0; 00965 int is_row = select_row; 00966 int is_col = select_col; 00967 switch(Fl::event_key()) { 00968 case FL_Home: 00969 ret = move_cursor(0, -1000000); 00970 break; 00971 case FL_End: 00972 ret = move_cursor(0, 1000000); 00973 break; 00974 case FL_Page_Up: 00975 ret = move_cursor(-(botrow - toprow - 1), 0); 00976 break; 00977 case FL_Page_Down: 00978 ret = move_cursor(botrow - toprow - 1 , 0); 00979 break; 00980 case FL_Left: 00981 ret = move_cursor(0, -1); 00982 break; 00983 case FL_Right: 00984 ret = move_cursor(0, 1); 00985 break; 00986 case FL_Up: 00987 ret = move_cursor(-1, 0); 00988 break; 00989 case FL_Down: 00990 ret = move_cursor(1, 0); 00991 break; 00992 case FL_Tab: 00993 if ( Fl::event_state() & FL_SHIFT ) { 00994 ret = move_cursor(0, -1); // shift-tab -> left 00995 } else { 00996 ret = move_cursor(0, 1); // tab -> right 00997 } 00998 break; 00999 } 01000 if (ret && Fl::focus() != this) { 01001 do_callback(CONTEXT_TABLE, -1, -1); 01002 take_focus(); 01003 } 01004 //if (!ret && Fl_Widget::callback() && when() & FL_WHEN_NOT_CHANGED ) 01005 if ( Fl_Widget::callback() && 01006 ( 01007 ( !ret && when() & FL_WHEN_NOT_CHANGED ) || 01008 ( is_row!= select_row || is_col!= select_col ) 01009 ) 01010 ) { 01011 do_callback(CONTEXT_CELL, select_row, select_col); 01012 //damage_zone(current_row, current_col, select_row, select_col); 01013 ret = 1; 01014 } 01015 break; 01016 } 01017 01018 default: 01019 change_cursor(FL_CURSOR_DEFAULT); 01020 break; 01021 } 01022 return(ret); 01023 } 01024 01025 // Resize FLTK override 01026 // Handle resize events if user resizes parent window. 01027 // 01028 void Fl_Table::resize(int X, int Y, int W, int H) { 01029 // Tell group to resize, and recalc our own widget as well 01030 Fl_Group::resize(X, Y, W, H); 01031 table_resized(); 01032 redraw(); 01033 } 01034 01035 // Draw a cell 01036 void Fl_Table::_redraw_cell(TableContext context, int r, int c) { 01037 if ( r < 0 || c < 0 ) return; 01038 int X,Y,W,H; 01039 find_cell(context, r, c, X, Y, W, H); // find positions of cell 01040 draw_cell(context, r, c, X, Y, W, H); // call users' function to draw it 01041 } 01042 01047 int Fl_Table::is_selected(int r, int c) { 01048 int s_left, s_right, s_top, s_bottom; 01049 01050 if (select_col > current_col) { 01051 s_left = current_col; 01052 s_right = select_col; 01053 } else { 01054 s_right = current_col; 01055 s_left = select_col; 01056 } 01057 if (select_row > current_row) { 01058 s_top = current_row; 01059 s_bottom = select_row; 01060 } else { 01061 s_bottom = current_row; 01062 s_top = select_row; 01063 } 01064 if (r >= s_top && r <= s_bottom && c >= s_left && c <= s_right) { 01065 return 1; 01066 } 01067 return 0; 01068 } 01069 01078 void Fl_Table::get_selection(int& row_top, int& col_left, int& row_bot, int& col_right) { 01079 if (select_col > current_col) { 01080 col_left = current_col; 01081 col_right = select_col; 01082 } else { 01083 col_right = current_col; 01084 col_left = select_col; 01085 } 01086 if (select_row > current_row) { 01087 row_top = current_row; 01088 row_bot = select_row; 01089 } else { 01090 row_bot = current_row; 01091 row_top = select_row; 01092 } 01093 } 01094 01106 void Fl_Table::set_selection(int row_top, int col_left, int row_bot, int col_right) { 01107 damage_zone(current_row, current_col, select_row, select_col); 01108 current_col = col_left; 01109 current_row = row_top; 01110 select_col = col_right; 01111 select_row = row_bot; 01112 damage_zone(current_row, current_col, select_row, select_col); 01113 } 01114 01115 // Draw the entire Fl_Table 01116 // Override the draw() routine to draw the table. 01117 // Then tell the group to draw over us. 01118 // 01119 void Fl_Table::draw() { 01120 draw_cell(CONTEXT_STARTPAGE, 0, 0, // let user's drawing routine 01121 tix, tiy, tiw, tih); // prep new page 01122 01123 // Let fltk widgets draw themselves first. Do this after 01124 // draw_cell(CONTEXT_STARTPAGE) in case user moves widgets around. 01125 // Use window 'inner' clip to prevent drawing into table border. 01126 // (unfortunately this clips FLTK's border, so we must draw it explicity below) 01127 // 01128 fl_push_clip(wix, wiy, wiw, wih); 01129 { 01130 Fl_Group::draw(); 01131 } 01132 fl_pop_clip(); 01133 01134 // Explicitly draw border around widget, if any 01135 draw_box(box(), x(), y(), w(), h(), color()); 01136 01137 // If Fl_Scroll 'table' is hidden, draw its box 01138 // Do this after Fl_Group::draw() so we draw over scrollbars 01139 // that leak around the border. 01140 // 01141 if ( ! table->visible() ) { 01142 if ( damage() & FL_DAMAGE_ALL || damage() & FL_DAMAGE_CHILD ) { 01143 draw_box(table->box(), tox, toy, tow, toh, table->color()); 01144 } 01145 } 01146 // Clip all further drawing to the inner widget dimensions 01147 fl_push_clip(wix, wiy, wiw, wih); 01148 { 01149 // Only redraw a few cells? 01150 if ( ! ( damage() & FL_DAMAGE_ALL ) && _redraw_leftcol != -1 ) { 01151 fl_push_clip(tix, tiy, tiw, tih); 01152 for ( int c = _redraw_leftcol; c <= _redraw_rightcol; c++ ) { 01153 for ( int r = _redraw_toprow; r <= _redraw_botrow; r++ ) { 01154 _redraw_cell(CONTEXT_CELL, r, c); 01155 } 01156 } 01157 fl_pop_clip(); 01158 } 01159 if ( damage() & FL_DAMAGE_ALL ) { 01160 int X,Y,W,H; 01161 // Draw row headers, if any 01162 if ( row_header() ) { 01163 get_bounds(CONTEXT_ROW_HEADER, X, Y, W, H); 01164 fl_push_clip(X,Y,W,H); 01165 for ( int r = toprow; r <= botrow; r++ ) { 01166 _redraw_cell(CONTEXT_ROW_HEADER, r, 0); 01167 } 01168 fl_pop_clip(); 01169 } 01170 // Draw column headers, if any 01171 if ( col_header() ) { 01172 get_bounds(CONTEXT_COL_HEADER, X, Y, W, H); 01173 fl_push_clip(X,Y,W,H); 01174 for ( int c = leftcol; c <= rightcol; c++ ) { 01175 _redraw_cell(CONTEXT_COL_HEADER, 0, c); 01176 } 01177 fl_pop_clip(); 01178 } 01179 // Draw all cells. 01180 // This includes cells partially obscured off edges of table. 01181 // No longer do this last; you might think it would be nice 01182 // to draw over dead zones, but on redraws it flickers. Avoid 01183 // drawing over deadzones; prevent deadzones by sizing columns. 01184 // 01185 fl_push_clip(tix, tiy, tiw, tih); { 01186 for ( int r = toprow; r <= botrow; r++ ) { 01187 for ( int c = leftcol; c <= rightcol; c++ ) { 01188 _redraw_cell(CONTEXT_CELL, r, c); 01189 } 01190 } 01191 } 01192 fl_pop_clip(); 01193 // Draw little rectangle in corner of headers 01194 if ( row_header() && col_header() ) { 01195 fl_rectf(wix, wiy, row_header_width(), col_header_height(), color()); 01196 } 01197 01198 // Table has a boxtype? Close those few dead pixels 01199 if ( table->box() ) { 01200 if ( col_header() ) { 01201 fl_rectf(tox, wiy, Fl::box_dx(table->box()), col_header_height(), color()); 01202 } 01203 if ( row_header() ) { 01204 fl_rectf(wix, toy, row_header_width(), Fl::box_dx(table->box()), color()); 01205 } 01206 } 01207 01208 // Table width smaller than window? Fill remainder with rectangle 01209 if ( table_w < tiw ) { 01210 fl_rectf(tix + table_w, tiy, tiw - table_w, tih, color()); 01211 // Col header? fill that too 01212 if ( col_header() ) { 01213 fl_rectf(tix + table_w, 01214 wiy, 01215 // get that corner just right.. 01216 (tiw - table_w + Fl::box_dw(table->box()) - 01217 Fl::box_dx(table->box())), 01218 col_header_height(), 01219 color()); 01220 } 01221 } 01222 // Table height smaller than window? Fill remainder with rectangle 01223 if ( table_h < tih ) { 01224 fl_rectf(tix, tiy + table_h, tiw, tih - table_h, color()); 01225 if ( row_header() ) { 01226 // NOTE: 01227 // Careful with that lower corner; don't use tih; when eg. 01228 // table->box(FL_THIN_UPFRAME) and hscrollbar hidden, 01229 // leaves a row of dead pixels. 01230 // 01231 fl_rectf(wix, tiy + table_h, row_header_width(), 01232 (wiy+wih) - (tiy+table_h) - 01233 ( hscrollbar->visible() ? SCROLLBAR_SIZE : 0), 01234 color()); 01235 } 01236 } 01237 } 01238 // Both scrollbars? Draw little box in lower right 01239 if ( vscrollbar->visible() && hscrollbar->visible() ) { 01240 fl_rectf(vscrollbar->x(), hscrollbar->y(), 01241 vscrollbar->w(), hscrollbar->h(), color()); 01242 } 01243 draw_cell(CONTEXT_ENDPAGE, 0, 0, // let user's drawing 01244 tix, tiy, tiw, tih); // routines cleanup 01245 01246 _redraw_leftcol = _redraw_rightcol = _redraw_toprow = _redraw_botrow = -1; 01247 } 01248 fl_pop_clip(); 01249 } 01250 01251 // 01252 // End of "$Id: Fl_Table.cxx 7950 2010-12-05 01:22:53Z greg.ercolano $". 01253 //