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)  

Fl_Table.cxx

Go to the documentation of this file.
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 //