Fl_Table.cpp
上传用户:yhdzpy8989
上传日期:2007-06-13
资源大小:13604k
文件大小:31k
源码类别:

生物技术

开发平台:

C/C++

  1. /*
  2.  * ===========================================================================
  3.  * PRODUCTION $Log: Fl_Table.cpp,v $
  4.  * PRODUCTION Revision 1000.1  2004/06/01 21:06:18  gouriano
  5.  * PRODUCTION PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.4
  6.  * PRODUCTION
  7.  * ===========================================================================
  8.  */
  9. /*
  10.  * This is a third-party file, and is maintained and copyrighted as below.
  11.  * Please see LICENSE at the root of the NCBI C++ toolkit for details on its
  12.  * redistribution
  13.  *
  14.  * ===========================================================================
  15.  * $Log: Fl_Table.cpp,v $
  16.  * Revision 1000.1  2004/06/01 21:06:18  gouriano
  17.  * PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.4
  18.  *
  19.  * Revision 1.4  2004/05/21 22:27:51  gorelenk
  20.  * Added PCH ncbi_pch.hpp
  21.  *
  22.  * Revision 1.3  2003/09/24 18:29:57  dicuccio
  23.  * Rearranged #include statements to avoid compiler warning on MSVC
  24.  *
  25.  * Revision 1.2  2003/08/01 19:02:21  dicuccio
  26.  * Retabified / reindented.  Changed calling of callback in Fl_Table::handle() -
  27.  * was called only if the release happened in the same row as the initial push,
  28.  * which prevenets a callback on click-drag-release for selection
  29.  *
  30.  * Revision 1.1  2003/07/25 13:37:45  dicuccio
  31.  * Initial revision
  32.  *
  33.  * ===========================================================================
  34.  */
  35. //
  36. // Fl_Table -- A table widget
  37. //
  38. // Copyright 2002 by Greg Ercolano.
  39. //
  40. // This library is free software; you can redistribute it and/or
  41. // modify it under the terms of the GNU Library General Public
  42. // License as published by the Free Software Foundation; either
  43. // version 2 of the License, or (at your option) any later version.
  44. //
  45. // This library is distributed in the hope that it will be useful,
  46. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  47. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  48. // Library General Public License for more details.
  49. //
  50. // You should have received a copy of the GNU Library General Public
  51. // License along with this library; if not, write to the Free Software
  52. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  53. // USA.
  54. //
  55. // Please report all bugs and problems to "erco at seriss dot com".
  56. //
  57. #include <ncbi_pch.hpp>
  58. #include <gui/widgets/Fl_Table/Fl_Table.H>
  59. #include <stdio.h>      // fprintf
  60. #include <FL/fl_draw.H>
  61. #define SCROLLBAR_SIZE  16
  62. // Scroll display so 'row' is at top
  63. void Fl_Table::row_position(int row)
  64. {
  65.     if ( _row_position == row ) return; // OPTIMIZATION: no change? avoid redraw
  66.     if ( row < 0 ) row = 0;
  67.     else if ( row >= rows() ) row = rows() - 1;
  68.     if ( table_h <= tih ) return;   // don't scroll if table smaller than window
  69.     double newtop = row_scroll_position(row);
  70.     if ( newtop > vscrollbar->maximum() ) 
  71.     { newtop = vscrollbar->maximum(); }
  72.     vscrollbar->Fl_Slider::value(newtop);
  73.     table_scrolled();
  74.     redraw();
  75.     _row_position = row;        // HACK: override what table_scrolled() came up with
  76. }
  77. // Scroll display so 'col' is at left
  78. void Fl_Table::col_position(int col)
  79. {
  80.     if ( _col_position == col ) return; // OPTIMIZATION: no change? avoid redraw
  81.     if ( col < 0 ) col = 0;
  82.     else if ( col >= cols() ) col = cols() - 1;
  83.     if ( table_w <= tiw ) return;   // don't scroll if table smaller than window
  84.     double newleft = col_scroll_position(col);
  85.     if ( newleft > hscrollbar->maximum() ) 
  86.     { newleft = hscrollbar->maximum(); }
  87.     hscrollbar->Fl_Slider::value(newleft);
  88.     table_scrolled();
  89.     redraw();
  90.     _col_position = col;        // HACK: override what table_scrolled() came up with
  91. }
  92. // Find scroll position of a row (in pixels)
  93. long Fl_Table::row_scroll_position(int row)
  94. {
  95.     int startrow = 0;
  96.     long scroll = 0;
  97.     // OPTIMIZATION: 
  98.     //     Attempt to use precomputed row scroll position
  99.     if ( toprow_scrollpos != -1 && row >= toprow )
  100.     { scroll = toprow_scrollpos; startrow = toprow; }
  101.     for ( int t=startrow; t<row; t++ )
  102.         scroll += row_height(t);
  103.     return(scroll);
  104. }
  105. // Find scroll position of a column (in pixels)
  106. long Fl_Table::col_scroll_position(int col)
  107. {
  108.     int startcol = 0;
  109.     long scroll = 0;
  110.     // OPTIMIZATION: 
  111.     //     Attempt to use precomputed row scroll position
  112.     if ( leftcol_scrollpos != -1 && col >= leftcol )
  113.     { scroll = leftcol_scrollpos; startcol = leftcol; }
  114.     for ( int t=startcol; t<col; t++ )
  115.         scroll += col_width(t);
  116.     return(scroll);
  117. }
  118. // Ctor
  119. Fl_Table::Fl_Table(int X, int Y, int W, int H, const char *l) : Fl_Group(X,Y,W,H,l)
  120. {
  121.     _rows             = 0;
  122.     _cols             = 0;
  123.     _row_header_w     = 40;
  124.     _col_header_h     = 18;
  125.     _row_header       = 0;
  126.     _col_header       = 0;
  127.     _row_header_color = color();
  128.     _col_header_color = color();
  129.     _row_resize       = 0;
  130.     _col_resize       = 0;
  131.     _row_resize_min   = 1;
  132.     _col_resize_min   = 1;
  133.     _redraw_toprow    = -1;
  134.     _redraw_botrow    = -1;
  135.     _redraw_leftcol   = -1;
  136.     _redraw_rightcol  = -1;
  137.     table_w           = 0;
  138.     table_h           = 0;
  139.     toprow            = 0;
  140.     botrow            = 0;
  141.     leftcol           = 0;
  142.     rightcol          = 0;
  143.     toprow_scrollpos  = -1;
  144.     leftcol_scrollpos = -1;
  145.     _last_cursor      = FL_CURSOR_DEFAULT;
  146.     _resizing_col     = -1;
  147.     _resizing_row     = -1;
  148.     _dragging_x       = -1;
  149.     _dragging_y       = -1;
  150.     _last_row         = -1;
  151.     box(FL_THIN_DOWN_FRAME);
  152.     vscrollbar = new Fl_Scrollbar(x()+w()-SCROLLBAR_SIZE, y(), SCROLLBAR_SIZE, h()-SCROLLBAR_SIZE);
  153.     vscrollbar->type(FL_VERTICAL);
  154.     vscrollbar->callback(scroll_cb, (void*)this);
  155.     hscrollbar = new Fl_Scrollbar(x(), y()+h()-SCROLLBAR_SIZE, w(), SCROLLBAR_SIZE);
  156.     hscrollbar->type(FL_HORIZONTAL);
  157.     hscrollbar->callback(scroll_cb, (void*)this);
  158.     table = new Fl_Scroll(x(), y(), w(), h());
  159.     table->box(FL_NO_BOX);
  160.     table->type(0);     // don't show Fl_Scroll's scrollbars -- use our own
  161.     table->hide();      // hide unless children are present
  162.     table->end();
  163.     table_resized();
  164.     redraw();
  165.     Fl_Group::end();        // end the group's begin()
  166.     table->begin();     // leave with fltk children getting added to the scroll
  167. }
  168. // Dtor
  169. Fl_Table::~Fl_Table()
  170. {
  171.     // The parent Fl_Group takes care of destroying scrollbars
  172. }
  173. // Set height of a row
  174. void Fl_Table::row_height(int row, int height)
  175. {
  176.     if ( row < 0 ) return;
  177.     if ( row < (int)_rowheights.size() && _rowheights[row] == height ) 
  178.     { return; }     // OPTIMIZATION: no change? avoid redraw
  179.     // Add row heights, even if none yet
  180.     while ( row >= (int)_rowheights.size() )
  181.     { _rowheights.push_back(height); }
  182.     _rowheights[row] = height;
  183.     table_resized();
  184.     if ( row <= botrow )    // OPTIMIZATION: only redraw if onscreen or above screen
  185.     { redraw(); }
  186. }
  187. // Set width of a column
  188. void Fl_Table::col_width(int col, int width)
  189. {
  190.     if ( col < 0 ) return;
  191.     if ( col < (int)_colwidths.size() && _colwidths[col] == width ) 
  192.     { return; }     // OPTIMIZATION: no change? avoid redraw
  193.     // Add column widths, even if none yet
  194.     while ( col >= (int)_colwidths.size() )
  195.     { _colwidths.push_back(width); }
  196.     _colwidths[col] = width;
  197.     table_resized();
  198.     if ( col <= rightcol )  // OPTIMIZATION: only redraw if onscreen or to the left
  199.     { redraw(); }
  200. }
  201. // Return row/col clamped to reality
  202. int Fl_Table::row_col_clamp(TableContext context, int &R, int &C)
  203. {
  204.     int clamped = 0;
  205.     if ( R < 0 ) { R = 0; clamped = 1; }
  206.     if ( C < 0 ) { C = 0; clamped = 1; }
  207.     switch ( context )
  208.     {
  209.     case CONTEXT_COL_HEADER:
  210.         // Allow col headers to draw even if no rows
  211.         if ( R >= _rows && R != 0 ) { R = _rows - 1; clamped = 1; }
  212.         break;
  213.     case CONTEXT_ROW_HEADER:
  214.         // Allow row headers to draw even if no columns
  215.         if ( C >= _cols && C != 0 ) { C = _cols - 1; clamped = 1; }
  216.         break;
  217.     case CONTEXT_CELL:
  218.     default:
  219.         // CLAMP R/C TO _rows/_cols
  220.         if ( R >= _rows ) { R = _rows - 1; clamped = 1; }
  221.         if ( C >= _cols ) { C = _cols - 1; clamped = 1; }
  222.         break;
  223.     }
  224.     return(clamped);
  225. }
  226. // Return bounding region for given context
  227. void Fl_Table::get_bounds(TableContext context, int &X, int &Y, int &W, int &H)
  228. {
  229.     switch ( context )
  230.     {
  231.     case CONTEXT_COL_HEADER:
  232.         // Column header clipping.
  233.         X = tox;
  234.         Y = wiy;
  235.         W = tow;
  236.         H = col_header_height();
  237.         return;
  238.     case CONTEXT_ROW_HEADER:
  239.         // Row header clipping.
  240.         X = wix;
  241.         Y = toy;
  242.         W = row_header_width();
  243.         H = toh;
  244.         return;
  245.     case CONTEXT_TABLE:
  246.         // Table inner dimensions
  247.         X = tix; Y = tiy; W = tiw; H = tih;
  248.         return;
  249.         // TODO: Add other contexts..
  250.     default:
  251.         fprintf(stderr, "Fl_Table::get_bounds(): context %d unimplementedn", (int)context);
  252.         return;
  253.     }
  254.     //NOTREACHED
  255. }
  256. // Find row/col beneath cursor
  257. //
  258. //    Returns R/C and context.
  259. //    Also returns resizeflag, if mouse is hovered over a resize boundary.
  260. //
  261. Fl_Table::TableContext Fl_Table::cursor2rowcol(int &R, int &C, ResizeFlag &resizeflag)
  262. {
  263.     // return values
  264.     R = C = 0;
  265.     resizeflag = RESIZE_NONE;
  266.     int X, Y, W, H;
  267.     // Row header?
  268.     if ( row_header() )
  269.     {
  270.         // Inside a row heading?
  271.         get_bounds(CONTEXT_ROW_HEADER, X, Y, W, H);
  272.         if ( Fl::event_inside(X, Y, W, H) )
  273.         {
  274.             // Scan visible rows until found
  275.             for ( R = toprow; R <= botrow; R++ )
  276.             {
  277.                 find_cell(CONTEXT_ROW_HEADER, R, 0, X, Y, W, H);
  278.                 if ( Fl::event_y() >= Y && Fl::event_y() < (Y+H) )
  279.                 {
  280.                     // Found row?
  281.                     //     If cursor over resize boundary, and resize enabled,
  282.                     //     enable the appropriate resize flag.
  283.                     //
  284.                     if ( row_resize() )
  285.                     {
  286.                         if ( Fl::event_y() <= (Y+3-0) ) { resizeflag = RESIZE_ROW_ABOVE; }
  287.                         if ( Fl::event_y() >= (Y+H-3) ) { resizeflag = RESIZE_ROW_BELOW; }
  288.                     }
  289.                     return(CONTEXT_ROW_HEADER);
  290.                 }
  291.             }
  292.             // Must be in row header dead zone
  293.             return(CONTEXT_NONE);
  294.         }
  295.     }
  296.     // Column header?
  297.     if ( col_header() )
  298.     {
  299.         // Inside a column heading?
  300.         get_bounds(CONTEXT_COL_HEADER, X, Y, W, H);
  301.         if ( Fl::event_inside(X, Y, W, H) )
  302.         {
  303.             // Scan visible columns until found
  304.             for ( C = leftcol; C <= rightcol; C++ )
  305.             {
  306.                 find_cell(CONTEXT_COL_HEADER, 0, C, X, Y, W, H);
  307.                 if ( Fl::event_x() >= X && Fl::event_x() < (X+W) )
  308.                 {
  309.                     // Found column?
  310.                     //     If cursor over resize boundary, and resize enabled,
  311.                     //     enable the appropriate resize flag.
  312.                     //
  313.                     if ( col_resize() )
  314.                     {
  315.                         if ( Fl::event_x() <= (X+3-0) ) { resizeflag = RESIZE_COL_LEFT; }
  316.                         if ( Fl::event_x() >= (X+W-3) ) { resizeflag = RESIZE_COL_RIGHT; }
  317.                     }
  318.                     return(CONTEXT_COL_HEADER);
  319.                 }
  320.             }
  321.             // Must be in column header dead zone
  322.             return(CONTEXT_NONE);
  323.         }
  324.     }
  325.     // Mouse somewhere in table?
  326.     //     Scan visible r/c's until we find it.
  327.     //
  328.     if ( Fl::event_inside(tox, toy, tow, toh) )
  329.     {
  330.         for ( R = toprow; R <= botrow; R++ )
  331.         {
  332.             for ( C = leftcol; C <= rightcol; C++ )
  333.             {
  334.                 find_cell(CONTEXT_CELL, R, C, X, Y, W, H);
  335.                 if ( Fl::event_inside(X, Y, W, H) )
  336.                 { return(CONTEXT_CELL); }   // found it
  337.             }
  338.         }
  339.         // Must be in a dead zone of the table
  340.         R = C = 0;
  341.         return(CONTEXT_TABLE);
  342.     }
  343.     // Somewhere else
  344.     return(CONTEXT_NONE);
  345. }
  346. // Find X/Y/W/H for cell at R/C
  347. //     If R or C are out of range, returns -1 
  348. //     with X/Y/W/H set to zero.
  349. //
  350. int Fl_Table::find_cell(TableContext context, int R, int C, int &X, int &Y, int &W, int &H)
  351. {
  352.     if ( row_col_clamp(context, R, C) )     // row or col out of range? error
  353.     { X=Y=W=H=0; return(-1); }
  354.     X = col_scroll_position(C) - hscrollbar->value() + tix;
  355.     Y = row_scroll_position(R) - vscrollbar->value() + tiy;
  356.     W = col_width(C);
  357.     H = row_height(R);
  358.     switch ( context )
  359.     {
  360.     case CONTEXT_COL_HEADER:
  361.         Y = wiy;
  362.         H = col_header_height();
  363.         return(0);
  364.     case CONTEXT_ROW_HEADER:
  365.         X = wix;
  366.         W = row_header_width();
  367.         return(0);
  368.     case CONTEXT_CELL:
  369.         return(0);
  370.     case CONTEXT_TABLE:
  371.         return(0);
  372.         // TODO -- HANDLE OTHER CONTEXTS
  373.     default:
  374.         fprintf(stderr, "Fl_Table::find_cell: unknown context %dn", (int)context);
  375.         return(-1);
  376.     }
  377.     //NOTREACHED
  378. }
  379. // Recalculate the window dimensions
  380. void Fl_Table::recalc_dimensions()
  381. {
  382.     // Recal to* (Table Outer), ti* (Table Inner), wi* ( Widget Inner)
  383.     wix = ( x() + Fl::box_dx(box())); tox = wix; tix = tox + Fl::box_dx(table->box());
  384.     wiy = ( y() + Fl::box_dy(box())); toy = wiy; tiy = toy + Fl::box_dy(table->box());
  385.     wiw = ( w() - Fl::box_dw(box())); tow = wiw; tiw = tow - Fl::box_dw(table->box());
  386.     wih = ( h() - Fl::box_dh(box())); toh = wih; tih = toh - Fl::box_dh(table->box());
  387.     // Trim window if headers enabled
  388.     if ( col_header() )
  389.     {
  390.         tiy += col_header_height(); toy += col_header_height();
  391.         tih -= col_header_height(); toh -= col_header_height();
  392.     }
  393.     if ( row_header() )
  394.     {
  395.         tix += row_header_width(); tox += row_header_width();
  396.         tiw -= row_header_width(); tow -= row_header_width();
  397.     }
  398.     // Make scroll bars disappear if window large enough
  399.     {
  400.         // First pass: can hide via window size?
  401.         int hidev = (table_h <= tih),
  402.         hideh = (table_w <= tiw);
  403.         // Second pass: Check for interference
  404.         if ( !hideh & hidev ) { hidev = (( table_h - tih + SCROLLBAR_SIZE ) <= 0 ); } 
  405.         if ( !hidev & hideh ) { hideh = (( table_w - tiw + SCROLLBAR_SIZE ) <= 0 ); }
  406.         // Determine scrollbar visibility, trim ti[xywh]/to[xywh]
  407.         if ( hidev ) { vscrollbar->hide(); } 
  408.         else { vscrollbar->show(); tiw -= SCROLLBAR_SIZE; tow -= SCROLLBAR_SIZE; }
  409.         if ( hideh ) { hscrollbar->hide(); } 
  410.         else { hscrollbar->show(); tih -= SCROLLBAR_SIZE; toh -= SCROLLBAR_SIZE;}
  411.     }
  412.     // Resize the child table
  413.     table->resize(tox, toy, tow, toh);
  414.     table->init_sizes();
  415. }
  416. // Recalculate internals after a scroll.
  417. //
  418. //    Call this if table has been scrolled or resized.
  419. //    Does not handle redraw().
  420. //    TODO: Assumes ti[xywh] has already been recalculated.
  421. //
  422. void Fl_Table::table_scrolled()
  423. {
  424.     // Top row
  425.     int y, row, voff = vscrollbar->value();
  426.     for ( row=y=0; row < _rows; row++ )
  427.     {
  428.         y += row_height(row);
  429.         if ( y >= voff ) { y -= row_height(row); break; }
  430.     }
  431.     _row_position = toprow = ( row >= _rows ) ? (row - 1) : row;
  432.     toprow_scrollpos = y;   // OPTIMIZATION: save for later use
  433.     // Bottom row
  434.     voff = vscrollbar->value() + tih;
  435.     for ( ; row < _rows; row++ )
  436.     {
  437.         y += row_height(row);
  438.         if ( y >= voff ) { break; }
  439.     }
  440.     botrow = ( row >= _rows ) ? (row - 1) : row;
  441.     // Left column
  442.     int x, col, hoff = hscrollbar->value();
  443.     for ( col=x=0; col < _cols; col++ )
  444.     {
  445.         x += col_width(col);
  446.         if ( x >= hoff ) { x -= col_width(col); break; }
  447.     }
  448.     _col_position = leftcol = ( col >= _cols ) ? (col - 1) : col;
  449.     leftcol_scrollpos = x;  // OPTIMIZATION: save for later use
  450.     // Right column
  451.     //    Work with data left over from leftcol calculation
  452.     //
  453.     hoff = hscrollbar->value() + tiw;
  454.     for ( ; col < _cols; col++ )
  455.     {
  456.         x += col_width(col);
  457.         if ( x >= hoff ) { break; }
  458.     }
  459.     rightcol = ( col >= _cols ) ? (col - 1) : col;
  460.     // First tell children to scroll
  461.     draw_cell(CONTEXT_RC_RESIZE, 0,0,0,0,0,0);
  462. }
  463. // Table resized: recalc internal data
  464. //    Call this whenever the window is resized.
  465. //    Recalculates the scrollbar sizes.
  466. //    Makes no assumptions about any pre-initialized data.
  467. //
  468. void Fl_Table::table_resized()
  469. {
  470.     table_h = row_scroll_position(rows());
  471.     table_w = col_scroll_position(cols());
  472.     recalc_dimensions();
  473.     // Recalc scrollbar sizes
  474.     //    Clamp scrollbar value() after a resize.
  475.     //    Resize scrollbars to enforce a constant trough width after a window resize.
  476.     //
  477.     {
  478.         float vscrolltab = ( table_h == 0 || tih > table_h ) ? 1 : (float)tih / table_h;
  479.         float hscrolltab = ( table_w == 0 || tiw > table_w ) ? 1 : (float)tiw / table_w;
  480.         vscrollbar->bounds(0, table_h-tih);
  481.         vscrollbar->precision(10);
  482.         vscrollbar->slider_size(vscrolltab);
  483.         vscrollbar->resize(wix+wiw-SCROLLBAR_SIZE, wiy,
  484.                            SCROLLBAR_SIZE, wih - ((hscrollbar->visible())?SCROLLBAR_SIZE:0));
  485.         vscrollbar->Fl_Valuator::value(vscrollbar->clamp(vscrollbar->value())); 
  486.         hscrollbar->bounds(0, table_w-tiw);
  487.         hscrollbar->precision(10);
  488.         hscrollbar->slider_size(hscrolltab);
  489.         hscrollbar->resize(wix, wiy+wih-SCROLLBAR_SIZE,
  490.                            wiw - ((vscrollbar->visible())?SCROLLBAR_SIZE:0), SCROLLBAR_SIZE);
  491.         hscrollbar->Fl_Valuator::value(hscrollbar->clamp(hscrollbar->value()));
  492.     }
  493.     // Tell FLTK child widgets were resized
  494.     Fl_Group::init_sizes();
  495.     // Recalc top/bot/left/right
  496.     table_scrolled();
  497.     // DO *NOT* REDRAW -- LEAVE THIS UP TO THE CALLER
  498.     // redraw();
  499. }
  500. // Someone moved a scrollbar
  501. void Fl_Table::scroll_cb(Fl_Widget*w, void *data)
  502. {
  503.     Fl_Table *o = (Fl_Table*)data;
  504.     o->recalc_dimensions(); // recalc tix, tiy, etc.
  505.     o->table_scrolled();
  506.     o->redraw();
  507. }
  508. // Set number of rows
  509. void Fl_Table::rows(int val)
  510. {
  511.     int oldrows = _rows;
  512.     _rows = val;
  513.     {
  514.         int default_h = ( _rowheights.size() > 0 ) ? _rowheights.back() : 25;
  515.         while ( val > (int)_rowheights.size() ) { _rowheights.push_back(default_h); }   // enlarge
  516.         while ( val < (int)_rowheights.size() ) { _rowheights.pop_back(); }     // shrink
  517.     }
  518.     table_resized();
  519.     // OPTIMIZATION: redraw only if change is visible.
  520.     if ( val >= oldrows && oldrows > botrow )
  521.     { /* NO REDRAW */ }
  522.     else
  523.     { redraw(); }
  524. }
  525. // Set number of cols
  526. void Fl_Table::cols(int val)
  527. {
  528.     _cols = val;
  529.     {
  530.         int default_w = ( _colwidths.size() > 0 ) ? _colwidths[_colwidths.size()-1] : 80;
  531.         while ( val > (int)_colwidths.size() ) { _colwidths.push_back(default_w); } // enlarge
  532.         while ( val < (int)_colwidths.size() ) { _colwidths.pop_back(); }       // shrink
  533.     }
  534.     table_resized();
  535.     redraw();
  536. }
  537. // Change mouse cursor to different type
  538. void Fl_Table::change_cursor(Fl_Cursor newcursor)
  539. {
  540.     if ( newcursor != _last_cursor )
  541.     {
  542.         fl_cursor(newcursor, FL_BLACK, FL_WHITE);
  543.         _last_cursor = newcursor;
  544.     }
  545. }
  546. // #define DEBUG 1
  547. #ifdef DEBUG
  548. #include "eventnames.h"
  549. #define PRINTEVENT 
  550. fprintf(stderr,"Table %s: ** Event: %s --n", (label()?label():"none"), eventnames[event]);
  551. #else
  552. #define PRINTEVENT
  553. #endif
  554. // Handle FLTK events
  555. int Fl_Table::handle(int event)
  556. {
  557.     PRINTEVENT;
  558.     int ret = Fl_Group::handle(event);  // let FLTK group handle events first
  559.     // Which row/column are we over?
  560.     int R, C;               // row/column being worked on
  561.     ResizeFlag resizeflag;      // which resizing area are we over? (0=none)
  562.     TableContext context = cursor2rowcol(R, C, resizeflag);
  563.     switch ( event )
  564.     {
  565.     case FL_PUSH:
  566.         // Need this for eg. right click to pop up a menu
  567.         if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED )
  568.         { do_callback(context, R, C); }
  569.         switch ( context )
  570.         {
  571.         case CONTEXT_CELL:
  572.             // FL_PUSH on a cell?
  573.             ret = 1;            // express interest in FL_RELEASE
  574.             break;
  575.         case CONTEXT_COL_HEADER:
  576.             // FL_PUSH on a column header?
  577.             if ( Fl::event_button() == 1 && resizeflag )
  578.             {
  579.                 // Start resize if left click on column border.
  580.                 //    "ret=1" ensures we get drag events from now on.
  581.                 //    (C-1) is used if mouse is over the left hand side of the cell,
  582.                 //    so that we resize the next column over to the left.
  583.                 //
  584.                 _resizing_col = ( resizeflag & RESIZE_COL_LEFT ) ? C-1 : C; 
  585.                 _resizing_row = -1;
  586.                 _dragging_x = Fl::event_x(); 
  587.                 ret = 1;
  588.             }
  589.             break;
  590.         case CONTEXT_ROW_HEADER:
  591.             // FL_PUSH on a row header?
  592.             if ( Fl::event_button() == 1 && resizeflag )
  593.             {
  594.                 // Start resize if left mouse clicked on row border.
  595.                 //    "ret = 1" ensures we get drag events from now on.
  596.                 //    (R-1) is used if mouse is over the top of the cell,
  597.                 //    so that we resize the row above.
  598.                 //
  599.                 _resizing_row = ( resizeflag & RESIZE_ROW_ABOVE ) ? R-1 : R; 
  600.                 _resizing_col = -1;
  601.                 _dragging_y = Fl::event_y(); 
  602.                 ret = 1;
  603.             }
  604.             break;
  605.         default:
  606.             ret = 0;        // express disinterest
  607.             break;
  608.         }
  609.         _last_row = R;
  610.         break;
  611.     case FL_DRAG:
  612.         if ( _resizing_col > -1 )
  613.         {
  614.             // Dragging column?
  615.             //
  616.             //    Let user drag even /outside/ the row/col widget.
  617.             //    Don't allow column width smaller than 1.
  618.             //    Continue to show FL_CURSOR_WE at all times during drag.
  619.             //
  620.             int offset = _dragging_x - Fl::event_x();
  621.             int new_w = col_width(_resizing_col) - offset;
  622.             if ( new_w < _col_resize_min ) new_w = _col_resize_min;
  623.             col_width(_resizing_col, new_w);
  624.             _dragging_x = Fl::event_x();
  625.             table_resized();
  626.             redraw();
  627.             change_cursor(FL_CURSOR_WE);
  628.             ret = 1;
  629.             if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED )
  630.             { do_callback(CONTEXT_RC_RESIZE, R, C); }
  631.         }
  632.         else if ( _resizing_row > -1 )
  633.         {
  634.             // Dragging row?
  635.             //
  636.             //    Let user drag even /outside/ the row/col widget.
  637.             //    Don't allow row width smaller than 1.
  638.             //    Continue to show FL_CURSOR_NS at all times during drag.
  639.             //
  640.             int offset = _dragging_y - Fl::event_y();
  641.             int new_h = row_height(_resizing_row) - offset;
  642.             if ( new_h < _row_resize_min ) new_h = _row_resize_min;
  643.             row_height(_resizing_row, new_h);
  644.             _dragging_y = Fl::event_y();
  645.             table_resized();
  646.             redraw();
  647.             change_cursor(FL_CURSOR_NS);
  648.             ret = 1;
  649.             if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED )
  650.             { do_callback(CONTEXT_RC_RESIZE, R, C); }
  651.         }
  652.         break;
  653.     case FL_RELEASE:
  654.         switch ( context )
  655.         {
  656.         case CONTEXT_ROW_HEADER:    // release on row header
  657.         case CONTEXT_COL_HEADER:    // release on col header
  658.         case CONTEXT_CELL:      // release on a cell
  659.         case CONTEXT_TABLE:     // release on dead zone
  660.             if ( _resizing_col == -1 &&             // not resizing a column
  661.                  _resizing_row == -1 &&             // not resizing a row
  662.                  Fl_Widget::callback() &&           // callback defined
  663.                  when() & FL_WHEN_RELEASE)          // on button release
  664.             {
  665.                 // Need this for eg. left clicking on a cell to select it
  666.                 do_callback(context, R, C);
  667.             }
  668.             break;
  669.         default:
  670.             break;
  671.         }
  672.         if ( Fl::event_button() == 1 )
  673.         {
  674.             change_cursor(FL_CURSOR_DEFAULT);
  675.             _resizing_col = -1;
  676.             _resizing_row = -1;
  677.             ret = 1;
  678.         }
  679.         break;
  680.     case FL_MOVE:
  681.         if ( context == CONTEXT_COL_HEADER &&   // in column header?
  682.              resizeflag )               // resize enabled + near boundary?
  683.         { change_cursor(FL_CURSOR_WE); }    // show resize cursor
  684.         else if ( context == CONTEXT_ROW_HEADER &&  // in row header?
  685.                   resizeflag )               // resize enabled + near boundary?
  686.         { change_cursor(FL_CURSOR_NS); }    // show resize cursor
  687.         else
  688.         { change_cursor(FL_CURSOR_DEFAULT); }   // normal cursor
  689.         ret = 1;
  690.         break;
  691.     case FL_ENTER:      // See FLTK event docs on the FL_ENTER widget
  692.     case FL_LEAVE:      // We want to track the mouse if resizing is allowed.
  693.         if ( resizeflag )
  694.         { ret = 1; }
  695.         if ( event == FL_LEAVE )
  696.         { change_cursor(FL_CURSOR_DEFAULT); }   // normal cursor
  697.         break;
  698.     case FL_FOCUS:
  699.     case FL_UNFOCUS:
  700.         // Currently no interest in keyboard focus. 
  701.         //     This will likely change when we implement keyboard navigation of cells.
  702.         //
  703.         // if (Fl::visible_focus()) 
  704.         //     { ret = 1; }
  705.         break;
  706.     default:
  707.         change_cursor(FL_CURSOR_DEFAULT);
  708.         break;
  709.     }
  710.     return(ret);
  711. }
  712. // Resize FLTK override
  713. //     Handle resize events if user resizes parent window.
  714. //
  715. void Fl_Table::resize(int X, int Y, int W, int H)
  716. {
  717.     // Tell group to resize, and recalc our own widget as well
  718.     Fl_Group::resize(X, Y, W, H);
  719.     table_resized();
  720.     redraw();
  721. }
  722. // Draw a cell
  723. void Fl_Table::_redraw_cell(TableContext context, int r, int c)
  724. {
  725.     if ( r < 0 || c < 0 ) return;
  726.     int X,Y,W,H;
  727.     find_cell(context, r, c, X, Y, W, H);   // find positions of cell
  728.     draw_cell(context, r, c, X, Y, W, H);   // call users' function to draw it
  729. }
  730. // Draw the entire Fl_Table
  731. //    Override the draw() routine to draw the table.
  732. //    Then tell the group to draw over us.
  733. //
  734. void Fl_Table::draw()
  735. {
  736.     draw_cell(CONTEXT_STARTPAGE, 0, 0,      // let user's drawing routine
  737.               tix, tiy, tiw, tih);      // prep new page
  738.     // Let fltk widgets draw themselves first. Do this after
  739.     // draw_cell(CONTEXT_STARTPAGE) in case user moves widgets around.
  740.     // Use window 'inner' clip to prevent drawing into table border.
  741.     // (unfortunately this clips FLTK's border, so we must draw it explicity below)
  742.     //
  743.     fl_push_clip(wix, wiy, wiw, wih);
  744.     {
  745.         Fl_Group::draw();
  746.     }
  747.     fl_pop_clip();
  748.     // Explicitly draw border around widget, if any
  749.     draw_box(box(), x(), y(), w(), h(), color());
  750.     // If Fl_Scroll 'table' is hidden, draw its box
  751.     //    Do this after Fl_Group::draw() so we draw over scrollbars
  752.     //    that leak around the border.
  753.     //
  754.     if ( ! table->visible() )
  755.     {
  756.         if ( damage() & FL_DAMAGE_ALL || damage() & FL_DAMAGE_CHILD )
  757.         { draw_box(table->box(), tox, toy, tow, toh, table->color()); }
  758.     }
  759.     // Clip all further drawing to the inner widget dimensions
  760.     fl_push_clip(wix, wiy, wiw, wih);
  761.     {
  762.         // Only redraw a few cells?
  763.         if ( ! ( damage() & FL_DAMAGE_ALL ) && _redraw_leftcol != -1 )
  764.         {
  765.             fl_push_clip(tix, tiy, tiw, tih);
  766.             for ( int c = _redraw_leftcol; c <= _redraw_rightcol; c++ )
  767.                 for ( int r = _redraw_toprow; r <= _redraw_botrow; r++ )
  768.                 { _redraw_cell(CONTEXT_CELL, r, c); }
  769.             fl_pop_clip();
  770.         }
  771.         if ( damage() & FL_DAMAGE_ALL )
  772.         {
  773.             int X,Y,W,H;
  774.             // Draw row headers, if any
  775.             if ( row_header() )
  776.             {
  777.                 get_bounds(CONTEXT_ROW_HEADER, X, Y, W, H);
  778.                 fl_push_clip(X,Y,W,H);
  779.                 for ( int r = toprow; r <= botrow; r++ )
  780.                 {
  781.                     _redraw_cell(CONTEXT_ROW_HEADER, r, 0);
  782.                 }
  783.                 fl_pop_clip();
  784.             }
  785.             // Draw column headers, if any
  786.             if ( col_header() )
  787.             {
  788.                 get_bounds(CONTEXT_COL_HEADER, X, Y, W, H);
  789.                 fl_push_clip(X,Y,W,H);
  790.                 for ( int c = leftcol; c <= rightcol; c++ )
  791.                 {
  792.                     _redraw_cell(CONTEXT_COL_HEADER, 0, c);
  793.                 }
  794.                 fl_pop_clip();
  795.             }
  796.             // Draw all cells.
  797.             //    This includes cells partially obscured off edges of table.
  798.             //    No longer do this last; you might think it would be nice
  799.             //    to draw over dead zones, but on redraws it flickers. Avoid
  800.             //    drawing over deadzones; prevent deadzones by sizing columns.
  801.             //
  802.             fl_push_clip(tix, tiy, tiw, tih);
  803.             {
  804.                 for ( int r = toprow; r <= botrow; r++ )
  805.                 {
  806.                     for ( int c = leftcol; c <= rightcol; c++ )
  807.                     {
  808.                         _redraw_cell(CONTEXT_CELL, r, c);
  809.                     }
  810.                 }
  811.             }
  812.             fl_pop_clip();
  813.             // Draw little rectangle in corner of headers
  814.             if ( row_header() && col_header() )
  815.             { fl_rectf(wix, wiy, row_header_width(), col_header_height(), color()); }
  816.             // Table has a boxtype? Close those few dead pixels
  817.             if ( table->box() )
  818.             {
  819.                 if ( col_header() )
  820.                 { fl_rectf(tox, wiy, Fl::box_dx(table->box()), 
  821.                            col_header_height(), color()); }
  822.                 if ( row_header() )
  823.                 { fl_rectf(wix, toy, row_header_width(), 
  824.                            Fl::box_dx(table->box()), color()); }
  825.             }
  826.             // Table width smaller than window? Fill remainder with rectangle
  827.             if ( table_w < tiw )
  828.             {
  829.                 fl_rectf(tix + table_w, tiy, tiw - table_w, tih, color());
  830.                 // Col header? fill that too
  831.                 if ( col_header() )
  832.                 {
  833.                     fl_rectf(tix + table_w, 
  834.                              wiy, 
  835.                              // get that corner just right..
  836.                              (tiw - table_w + Fl::box_dw(table->box()) - 
  837.                               Fl::box_dx(table->box())),
  838.                              col_header_height(),
  839.                              color());
  840.                 }
  841.             }
  842.             // Table height smaller than window? Fill remainder with rectangle
  843.             if ( table_h < tih )
  844.             {
  845.                 fl_rectf(tix, tiy + table_h, tiw, tih - table_h, color());
  846.                 if ( row_header() )
  847.                 {
  848.                     // NOTE:
  849.                     //     Careful with that lower corner; don't use tih; when eg. 
  850.                     //     table->box(FL_THIN_UPFRAME) and hscrollbar hidden, 
  851.                     //     leaves a row of dead pixels.
  852.                     //
  853.                     fl_rectf(wix, tiy + table_h, row_header_width(), 
  854.                              (wiy+wih)-(tiy+table_h)-(hscrollbar->visible()?
  855.                                                       SCROLLBAR_SIZE:0),
  856.                              color());
  857.                 }
  858.             }
  859.         }
  860.         // Both scrollbars? Draw little box in lower right
  861.         if ( vscrollbar->visible() && hscrollbar->visible() )
  862.         {
  863.             fl_rectf(vscrollbar->x(), hscrollbar->y(), 
  864.                      vscrollbar->w(), hscrollbar->h(), color());
  865.         }
  866.         draw_cell(CONTEXT_ENDPAGE, 0, 0,        // let user's drawing
  867.                   tix, tiy, tiw, tih);          // routines cleanup
  868.         _redraw_leftcol = _redraw_rightcol = _redraw_toprow = _redraw_botrow = -1;
  869.     }
  870.     fl_pop_clip();
  871. }