2 // "$Id: Fl_Table.cxx 7950 2010-12-05 01:22:53Z greg.ercolano $"
4 // Fl_Table -- A table widget
6 // Copyright 2002 by Greg Ercolano.
7 // Copyright (c) 2004 O'ksi'D
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Library General Public
11 // License as published by the Free Software Foundation; either
12 // version 2 of the License, or (at your option) any later version.
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Library General Public License for more details.
19 // You should have received a copy of the GNU Library General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
25 #include <stdio.h> // fprintf
26 #include <FL/fl_draw.H>
27 #include <FL/Fl_Table.H>
29 #if defined(USE_UTF8) && ( defined(MICROSOFT) || defined(LINUX) )
30 #include <FL/fl_utf8.H> // currently only Windows and Linux
33 #define SCROLLBAR_SIZE 16
35 // Scroll display so 'row' is at top
36 void Fl_Table::row_position(int row
) {
37 if ( _row_position
== row
) return; // OPTIMIZATION: no change? avoid redraw
38 if ( row
< 0 ) row
= 0;
39 else if ( row
>= rows() ) row
= rows() - 1;
40 if ( table_h
<= tih
) return; // don't scroll if table smaller than window
41 double newtop
= row_scroll_position(row
);
42 if ( newtop
> vscrollbar
->maximum() ) {
43 newtop
= vscrollbar
->maximum();
45 vscrollbar
->Fl_Slider::value(newtop
);
48 _row_position
= row
; // HACK: override what table_scrolled() came up with
51 // Scroll display so 'col' is at left
52 void Fl_Table::col_position(int col
) {
53 if ( _col_position
== col
) return; // OPTIMIZATION: no change? avoid redraw
54 if ( col
< 0 ) col
= 0;
55 else if ( col
>= cols() ) col
= cols() - 1;
56 if ( table_w
<= tiw
) return; // don't scroll if table smaller than window
57 double newleft
= col_scroll_position(col
);
58 if ( newleft
> hscrollbar
->maximum() ) {
59 newleft
= hscrollbar
->maximum();
61 hscrollbar
->Fl_Slider::value(newleft
);
64 _col_position
= col
; // HACK: override what table_scrolled() came up with
67 // Find scroll position of a row (in pixels)
68 long Fl_Table::row_scroll_position(int row
) {
72 // Attempt to use precomputed row scroll position
74 if ( toprow_scrollpos
!= -1 && row
>= toprow
) {
75 scroll
= toprow_scrollpos
;
78 for ( int t
=startrow
; t
<row
; t
++ ) {
79 scroll
+= row_height(t
);
84 // Find scroll position of a column (in pixels)
85 long Fl_Table::col_scroll_position(int col
) {
89 // Attempt to use precomputed row scroll position
91 if ( leftcol_scrollpos
!= -1 && col
>= leftcol
) {
92 scroll
= leftcol_scrollpos
;
95 for ( int t
=startcol
; t
<col
; t
++ ) {
96 scroll
+= col_width(t
);
102 Fl_Table::Fl_Table(int X
, int Y
, int W
, int H
, const char *l
) : Fl_Group(X
,Y
,W
,H
,l
) {
109 _row_header_color
= color();
110 _col_header_color
= color();
117 _redraw_leftcol
= -1;
118 _redraw_rightcol
= -1;
125 toprow_scrollpos
= -1;
126 leftcol_scrollpos
= -1;
127 _last_cursor
= FL_CURSOR_DEFAULT
;
139 box(FL_THIN_DOWN_FRAME
);
141 vscrollbar
= new Fl_Scrollbar(x()+w()-SCROLLBAR_SIZE
, y(),
142 SCROLLBAR_SIZE
, h()-SCROLLBAR_SIZE
);
143 vscrollbar
->type(FL_VERTICAL
);
144 vscrollbar
->callback(scroll_cb
, (void*)this);
146 hscrollbar
= new Fl_Scrollbar(x(), y()+h()-SCROLLBAR_SIZE
,
147 w(), SCROLLBAR_SIZE
);
148 hscrollbar
->type(FL_HORIZONTAL
);
149 hscrollbar
->callback(scroll_cb
, (void*)this);
151 table
= new Fl_Scroll(x(), y(), w(), h());
152 table
->box(FL_NO_BOX
);
153 table
->type(0); // don't show Fl_Scroll's scrollbars -- use our own
154 table
->hide(); // hide unless children are present
160 Fl_Group::end(); // end the group's begin()
162 table
->begin(); // leave with fltk children getting added to the scroll
166 Fl_Table::~Fl_Table() {
167 // The parent Fl_Group takes care of destroying scrollbars
170 // Set height of a row
171 void Fl_Table::row_height(int row
, int height
) {
172 if ( row
< 0 ) return;
173 if ( row
< (int)_rowheights
.size() && _rowheights
[row
] == height
) {
174 return; // OPTIMIZATION: no change? avoid redraw
176 // Add row heights, even if none yet
177 int now_size
= (int)_rowheights
.size();
178 if ( row
>= now_size
) {
179 _rowheights
.size(row
);
180 while (now_size
< row
)
181 _rowheights
[now_size
++] = height
;
183 _rowheights
[row
] = height
;
185 if ( row
<= botrow
) { // OPTIMIZATION: only redraw if onscreen or above screen
188 // ROW RESIZE CALLBACK
189 if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED
) {
190 do_callback(CONTEXT_RC_RESIZE
, row
, 0);
194 // Set width of a column
195 void Fl_Table::col_width(int col
, int width
)
197 if ( col
< 0 ) return;
198 if ( col
< (int)_colwidths
.size() && _colwidths
[col
] == width
) {
199 return; // OPTIMIZATION: no change? avoid redraw
201 // Add column widths, even if none yet
202 int now_size
= (int)_colwidths
.size();
203 if ( col
>= now_size
) {
204 _colwidths
.size(col
);
205 while (now_size
< col
) {
206 _colwidths
[now_size
++] = width
;
209 _colwidths
[col
] = width
;
211 if ( col
<= rightcol
) { // OPTIMIZATION: only redraw if onscreen or to the left
214 // COLUMN RESIZE CALLBACK
215 if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED
) {
216 do_callback(CONTEXT_RC_RESIZE
, 0, col
);
220 // Return row/col clamped to reality
221 int Fl_Table::row_col_clamp(TableContext context
, int &R
, int &C
) {
223 if ( R
< 0 ) { R
= 0; clamped
= 1; }
224 if ( C
< 0 ) { C
= 0; clamped
= 1; }
226 case CONTEXT_COL_HEADER
:
227 // Allow col headers to draw even if no rows
228 if ( R
>= _rows
&& R
!= 0 ) { R
= _rows
- 1; clamped
= 1; }
231 case CONTEXT_ROW_HEADER
:
232 // Allow row headers to draw even if no columns
233 if ( C
>= _cols
&& C
!= 0 ) { C
= _cols
- 1; clamped
= 1; }
238 // CLAMP R/C TO _rows/_cols
239 if ( R
>= _rows
) { R
= _rows
- 1; clamped
= 1; }
240 if ( C
>= _cols
) { C
= _cols
- 1; clamped
= 1; }
246 // Return bounding region for given context
247 void Fl_Table::get_bounds(TableContext context
, int &X
, int &Y
, int &W
, int &H
) {
249 case CONTEXT_COL_HEADER
:
250 // Column header clipping.
254 H
= col_header_height();
257 case CONTEXT_ROW_HEADER
:
258 // Row header clipping.
261 W
= row_header_width();
266 // Table inner dimensions
267 X
= tix
; Y
= tiy
; W
= tiw
; H
= tih
;
270 // TODO: Add other contexts..
272 fprintf(stderr
, "Fl_Table::get_bounds(): context %d unimplemented\n", (int)context
);
278 // Find row/col beneath cursor
280 // Returns R/C and context.
281 // Also returns resizeflag, if mouse is hovered over a resize boundary.
283 Fl_Table::TableContext
Fl_Table::cursor2rowcol(int &R
, int &C
, ResizeFlag
&resizeflag
) {
286 resizeflag
= RESIZE_NONE
;
289 if ( row_header() ) {
290 // Inside a row heading?
291 get_bounds(CONTEXT_ROW_HEADER
, X
, Y
, W
, H
);
292 if ( Fl::event_inside(X
, Y
, W
, H
) ) {
293 // Scan visible rows until found
294 for ( R
= toprow
; R
<= botrow
; R
++ ) {
295 find_cell(CONTEXT_ROW_HEADER
, R
, 0, X
, Y
, W
, H
);
296 if ( Fl::event_y() >= Y
&& Fl::event_y() < (Y
+H
) ) {
298 // If cursor over resize boundary, and resize enabled,
299 // enable the appropriate resize flag.
301 if ( row_resize() ) {
302 if ( Fl::event_y() <= (Y
+3-0) ) { resizeflag
= RESIZE_ROW_ABOVE
; }
303 if ( Fl::event_y() >= (Y
+H
-3) ) { resizeflag
= RESIZE_ROW_BELOW
; }
305 return(CONTEXT_ROW_HEADER
);
308 // Must be in row header dead zone
309 return(CONTEXT_NONE
);
313 if ( col_header() ) {
314 // Inside a column heading?
315 get_bounds(CONTEXT_COL_HEADER
, X
, Y
, W
, H
);
316 if ( Fl::event_inside(X
, Y
, W
, H
) ) {
317 // Scan visible columns until found
318 for ( C
= leftcol
; C
<= rightcol
; C
++ ) {
319 find_cell(CONTEXT_COL_HEADER
, 0, C
, X
, Y
, W
, H
);
320 if ( Fl::event_x() >= X
&& Fl::event_x() < (X
+W
) ) {
322 // If cursor over resize boundary, and resize enabled,
323 // enable the appropriate resize flag.
325 if ( col_resize() ) {
326 if ( Fl::event_x() <= (X
+3-0) ) { resizeflag
= RESIZE_COL_LEFT
; }
327 if ( Fl::event_x() >= (X
+W
-3) ) { resizeflag
= RESIZE_COL_RIGHT
; }
329 return(CONTEXT_COL_HEADER
);
332 // Must be in column header dead zone
333 return(CONTEXT_NONE
);
336 // Mouse somewhere in table?
337 // Scan visible r/c's until we find it.
339 if ( Fl::event_inside(tox
, toy
, tow
, toh
) ) {
340 for ( R
= toprow
; R
<= botrow
; R
++ ) {
341 find_cell(CONTEXT_CELL
, R
, C
, X
, Y
, W
, H
);
342 if ( Fl::event_y() < Y
) break; // OPT: thanks lars
343 if ( Fl::event_y() >= (Y
+H
) ) continue; // OPT: " "
344 for ( C
= leftcol
; C
<= rightcol
; C
++ ) {
345 find_cell(CONTEXT_CELL
, R
, C
, X
, Y
, W
, H
);
346 if ( Fl::event_inside(X
, Y
, W
, H
) ) {
347 return(CONTEXT_CELL
); // found it
351 // Must be in a dead zone of the table
353 return(CONTEXT_TABLE
);
356 return(CONTEXT_NONE
);
359 // Find X/Y/W/H for cell at R/C
360 // If R or C are out of range, returns -1
361 // with X/Y/W/H set to zero.
363 int Fl_Table::find_cell(TableContext context
, int R
, int C
, int &X
, int &Y
, int &W
, int &H
) {
364 if ( row_col_clamp(context
, R
, C
) ) { // row or col out of range? error
368 X
= col_scroll_position(C
) - hscrollbar
->value() + tix
;
369 Y
= row_scroll_position(R
) - vscrollbar
->value() + tiy
;
374 case CONTEXT_COL_HEADER
:
376 H
= col_header_height();
379 case CONTEXT_ROW_HEADER
:
381 W
= row_header_width();
390 // TODO -- HANDLE OTHER CONTEXTS
392 fprintf(stderr
, "Fl_Table::find_cell: unknown context %d\n", (int)context
);
398 // Enable automatic scroll-selection
399 void Fl_Table::_start_auto_drag() {
400 if (_auto_drag
) return;
402 Fl::add_timeout(0.3, _auto_drag_cb2
, this);
405 // Disable automatic scroll-selection
406 void Fl_Table::_stop_auto_drag() {
407 if (!_auto_drag
) return;
408 Fl::remove_timeout(_auto_drag_cb2
, this);
412 void Fl_Table::_auto_drag_cb2(void *d
) {
413 ((Fl_Table
*)d
)->_auto_drag_cb();
416 // Handle automatic scroll-selection if mouse selection dragged off table edge
417 void Fl_Table::_auto_drag_cb() {
420 if (_selecting
== CONTEXT_COL_HEADER
)
421 { ly
= y() + col_header_height(); }
422 else if (_selecting
== CONTEXT_ROW_HEADER
)
423 { lx
= x() + row_header_width(); }
424 if (lx
> x() + w() - 20) {
425 Fl::e_x
= x() + w() - 20;
426 if (hscrollbar
->visible())
427 ((Fl_Slider
*)hscrollbar
)->value(hscrollbar
->clamp(hscrollbar
->value() + 30));
428 hscrollbar
->do_callback();
429 _dragging_x
= Fl::e_x
- 30;
431 else if (lx
< (x() + row_header_width())) {
432 Fl::e_x
= x() + row_header_width() + 1;
433 if (hscrollbar
->visible()) {
434 ((Fl_Slider
*)hscrollbar
)->value(hscrollbar
->clamp(hscrollbar
->value() - 30));
436 hscrollbar
->do_callback();
437 _dragging_x
= Fl::e_x
+ 30;
439 if (ly
> y() + h() - 20) {
440 Fl::e_y
= y() + h() - 20;
441 if (vscrollbar
->visible()) {
442 ((Fl_Slider
*)vscrollbar
)->value(vscrollbar
->clamp(vscrollbar
->value() + 30));
444 vscrollbar
->do_callback();
445 _dragging_y
= Fl::e_y
- 30;
447 else if (ly
< (y() + col_header_height())) {
448 Fl::e_y
= y() + col_header_height() + 1;
449 if (vscrollbar
->visible()) {
450 ((Fl_Slider
*)vscrollbar
)->value(vscrollbar
->clamp(vscrollbar
->value() - 30));
452 vscrollbar
->do_callback();
453 _dragging_y
= Fl::e_y
+ 30;
462 if (Fl::event_buttons() && _auto_drag
) {
463 Fl::add_timeout(0.05, _auto_drag_cb2
, this);
467 // Recalculate the window dimensions
468 void Fl_Table::recalc_dimensions() {
469 // Recalc to* (Table Outer), ti* (Table Inner), wi* ( Widget Inner)
470 wix
= ( x() + Fl::box_dx(box())); tox
= wix
; tix
= tox
+ Fl::box_dx(table
->box());
471 wiy
= ( y() + Fl::box_dy(box())); toy
= wiy
; tiy
= toy
+ Fl::box_dy(table
->box());
472 wiw
= ( w() - Fl::box_dw(box())); tow
= wiw
; tiw
= tow
- Fl::box_dw(table
->box());
473 wih
= ( h() - Fl::box_dh(box())); toh
= wih
; tih
= toh
- Fl::box_dh(table
->box());
474 // Trim window if headers enabled
475 if ( col_header() ) {
476 tiy
+= col_header_height(); toy
+= col_header_height();
477 tih
-= col_header_height(); toh
-= col_header_height();
479 if ( row_header() ) {
480 tix
+= row_header_width(); tox
+= row_header_width();
481 tiw
-= row_header_width(); tow
-= row_header_width();
483 // Make scroll bars disappear if window large enough
485 // First pass: can hide via window size?
486 int hidev
= (table_h
<= tih
);
487 int hideh
= (table_w
<= tiw
);
488 // Second pass: Check for interference
489 if ( !hideh
& hidev
) { hidev
= (( table_h
- tih
+ SCROLLBAR_SIZE
) <= 0 ); }
490 if ( !hidev
& hideh
) { hideh
= (( table_w
- tiw
+ SCROLLBAR_SIZE
) <= 0 ); }
491 // Determine scrollbar visibility, trim ti[xywh]/to[xywh]
492 if ( hidev
) { vscrollbar
->hide(); }
493 else { vscrollbar
->show(); tiw
-= SCROLLBAR_SIZE
; tow
-= SCROLLBAR_SIZE
; }
494 if ( hideh
) { hscrollbar
->hide(); }
495 else { hscrollbar
->show(); tih
-= SCROLLBAR_SIZE
; toh
-= SCROLLBAR_SIZE
; }
497 // Resize the child table
498 table
->resize(tox
, toy
, tow
, toh
);
502 // Recalculate internals after a scroll.
504 // Call this if table has been scrolled or resized.
505 // Does not handle redraw().
506 // TODO: Assumes ti[xywh] has already been recalculated.
508 void Fl_Table::table_scrolled() {
510 int y
, row
, voff
= vscrollbar
->value();
511 for ( row
=y
=0; row
< _rows
; row
++ ) {
512 y
+= row_height(row
);
513 if ( y
> voff
) { y
-= row_height(row
); break; }
515 _row_position
= toprow
= ( row
>= _rows
) ? (row
- 1) : row
;
516 toprow_scrollpos
= y
; // OPTIMIZATION: save for later use
518 voff
= vscrollbar
->value() + tih
;
519 for ( ; row
< _rows
; row
++ ) {
520 y
+= row_height(row
);
521 if ( y
>= voff
) { break; }
523 botrow
= ( row
>= _rows
) ? (row
- 1) : row
;
525 int x
, col
, hoff
= hscrollbar
->value();
526 for ( col
=x
=0; col
< _cols
; col
++ ) {
528 if ( x
> hoff
) { x
-= col_width(col
); break; }
530 _col_position
= leftcol
= ( col
>= _cols
) ? (col
- 1) : col
;
531 leftcol_scrollpos
= x
; // OPTIMIZATION: save for later use
533 // Work with data left over from leftcol calculation
535 hoff
= hscrollbar
->value() + tiw
;
536 for ( ; col
< _cols
; col
++ ) {
538 if ( x
>= hoff
) { break; }
540 rightcol
= ( col
>= _cols
) ? (col
- 1) : col
;
541 // First tell children to scroll
542 draw_cell(CONTEXT_RC_RESIZE
, 0,0,0,0,0,0);
545 // Table resized: recalc internal data
546 // Call this whenever the window is resized.
547 // Recalculates the scrollbar sizes.
548 // Makes no assumptions about any pre-initialized data.
550 void Fl_Table::table_resized() {
551 table_h
= row_scroll_position(rows());
552 table_w
= col_scroll_position(cols());
554 // Recalc scrollbar sizes
555 // Clamp scrollbar value() after a resize.
556 // Resize scrollbars to enforce a constant trough width after a window resize.
559 // Vertical scrollbar
560 float vscrolltab
= ( table_h
== 0 || tih
> table_h
) ? 1 : (float)tih
/ table_h
;
561 float hscrolltab
= ( table_w
== 0 || tiw
> table_w
) ? 1 : (float)tiw
/ table_w
;
562 vscrollbar
->bounds(0, table_h
-tih
);
563 vscrollbar
->precision(10);
564 vscrollbar
->slider_size(vscrolltab
);
565 vscrollbar
->resize(wix
+wiw
-SCROLLBAR_SIZE
, wiy
,
567 wih
- ((hscrollbar
->visible())?SCROLLBAR_SIZE
:0));
568 vscrollbar
->Fl_Valuator::value(vscrollbar
->clamp(vscrollbar
->value()));
569 // Horizontal scrollbar
570 hscrollbar
->bounds(0, table_w
-tiw
);
571 hscrollbar
->precision(10);
572 hscrollbar
->slider_size(hscrolltab
);
573 hscrollbar
->resize(wix
, wiy
+wih
-SCROLLBAR_SIZE
,
574 wiw
- ((vscrollbar
->visible())?SCROLLBAR_SIZE
:0),
576 hscrollbar
->Fl_Valuator::value(hscrollbar
->clamp(hscrollbar
->value()));
579 // Tell FLTK child widgets were resized
580 Fl_Group::init_sizes();
582 // Recalc top/bot/left/right
585 // DO *NOT* REDRAW -- LEAVE THIS UP TO THE CALLER
589 // Someone moved a scrollbar
590 void Fl_Table::scroll_cb(Fl_Widget
*w
, void *data
) {
591 Fl_Table
*o
= (Fl_Table
*)data
;
592 o
->recalc_dimensions(); // recalc tix, tiy, etc.
597 // Set number of rows
598 void Fl_Table::rows(int val
) {
602 int default_h
= ( _rowheights
.size() > 0 ) ? _rowheights
.back() : 25;
603 int now_size
= _rowheights
.size();
604 _rowheights
.size(val
); // enlarge or shrink as needed
605 while ( now_size
< val
) {
606 _rowheights
[now_size
++] = default_h
; // fill new
611 // OPTIMIZATION: redraw only if change is visible.
612 if ( val
>= oldrows
&& oldrows
> botrow
) {
619 // Set number of cols
620 void Fl_Table::cols(int val
) {
623 int default_w
= ( _colwidths
.size() > 0 ) ? _colwidths
[_colwidths
.size()-1] : 80;
624 int now_size
= _colwidths
.size();
625 _colwidths
.size(val
); // enlarge or shrink as needed
626 while ( now_size
< val
) {
627 _colwidths
[now_size
++] = default_w
; // fill new
634 // Change mouse cursor to different type
635 void Fl_Table::change_cursor(Fl_Cursor newcursor
) {
636 if ( newcursor
!= _last_cursor
) {
637 fl_cursor(newcursor
, FL_BLACK
, FL_WHITE
);
638 _last_cursor
= newcursor
;
642 void Fl_Table::damage_zone(int r1
, int c1
, int r2
, int c2
, int r3
, int c3
) {
643 int R1
= r1
, C1
= c1
;
644 int R2
= r2
, C2
= c2
;
645 if (r1
> R2
) R2
= r1
;
646 if (r2
< R1
) R1
= r2
;
647 if (r3
> R2
) R2
= r3
;
648 if (r3
< R1
) R1
= r3
;
649 if (c1
> C2
) C2
= c1
;
650 if (c2
< C1
) C1
= c2
;
651 if (c3
> C2
) C2
= c3
;
652 if (c3
< C1
) C1
= c3
;
661 if (R1
< toprow
) R1
= toprow
;
662 if (R2
> botrow
) R2
= botrow
;
663 if (C1
< leftcol
) C1
= leftcol
;
664 if (C2
> rightcol
) C2
= rightcol
;
665 redraw_range(R1
, R2
, C1
, C2
);
668 int Fl_Table::move_cursor(int R
, int C
) {
669 if (select_row
== -1) R
++;
670 if (select_col
== -1) C
++;
674 if (R
>= rows()) R
= rows() - 1;
676 if (C
>= cols()) C
= cols() - 1;
677 if (R
== select_row
&& C
== select_col
) return 0;
678 damage_zone(current_row
, current_col
, select_row
, select_col
, R
, C
);
681 if (!Fl::event_state(FL_SHIFT
)) {
685 if (R
< toprow
+ 1 || R
> botrow
- 1) row_position(R
);
686 if (C
< leftcol
+ 1 || C
> rightcol
- 1) col_position(C
);
692 #include "eventnames.h"
694 fprintf(stderr,"Table %s: ** Event: %s --\n", (label()?label():"none"), eventnames[event]);
699 // Handle FLTK events
700 int Fl_Table::handle(int event
) {
702 int ret
= Fl_Group::handle(event
); // let FLTK group handle events first
704 if (Fl::event_inside(hscrollbar
) || Fl::event_inside(vscrollbar
)) return 1;
705 if (Fl::focus() != this && contains(Fl::focus())) return 1;
707 // Which row/column are we over?
708 int R
, C
; // row/column being worked on
709 ResizeFlag resizeflag
; // which resizing area are we over? (0=none)
710 TableContext context
= cursor2rowcol(R
, C
, resizeflag
);
713 if (Fl::event_button() == 1 && !Fl::event_clicks()) {
714 if (Fl::focus() != this) {
716 do_callback(CONTEXT_TABLE
, -1, -1);
719 damage_zone(current_row
, current_col
, select_row
, select_col
, R
, C
);
720 if (context
== CONTEXT_CELL
) {
721 current_row
= select_row
= R
;
722 current_col
= select_col
= C
;
723 _selecting
= CONTEXT_CELL
;
725 current_row
= select_row
= -1;
726 current_col
= select_col
= -1;
729 // Need this for eg. right click to pop up a menu
730 if ( Fl_Widget::callback() && // callback defined?
731 resizeflag
== RESIZE_NONE
) { // not resizing?
732 do_callback(context
, R
, C
); // do callback
736 // FL_PUSH on a cell?
737 ret
= 1; // express interest in FL_RELEASE
741 // FL_PUSH on table corner?
742 if ( Fl::event_button() == 1 &&
743 Fl::event_x() < x() + row_header_width()) {
745 select_col
= cols() - 1;
747 select_row
= rows() - 1;
748 damage_zone(current_row
, current_col
, select_row
, select_col
);
753 case CONTEXT_COL_HEADER
:
754 // FL_PUSH on a column header?
755 if ( Fl::event_button() == 1) {
756 // Resizing? Handle it
758 // Start resize if left click on column border.
759 // "ret=1" ensures we get drag events from now on.
760 // (C-1) is used if mouse is over the left hand side
761 // of cell, so we resize the next column on the left.
763 _resizing_col
= ( resizeflag
& RESIZE_COL_LEFT
) ? C
-1 : C
;
765 _dragging_x
= Fl::event_x();
768 // Not resizing? Select the column
769 current_col
= select_col
= C
;
771 select_row
= rows() - 1;
772 _selecting
= CONTEXT_COL_HEADER
;
773 damage_zone(current_row
, current_col
, select_row
, select_col
);
779 case CONTEXT_ROW_HEADER
:
780 // FL_PUSH on a row header?
781 if ( Fl::event_button() == 1 ) {
782 // Resizing? Handle it
784 // Start resize if left mouse clicked on row border.
785 // "ret = 1" ensures we get drag events from now on.
786 // (R-1) is used if mouse is over the top of the cell,
787 // so that we resize the row above.
789 _resizing_row
= ( resizeflag
& RESIZE_ROW_ABOVE
) ? R
-1 : R
;
791 _dragging_y
= Fl::event_y();
794 // Not resizing? Select the row
795 current_row
= select_row
= R
;
797 select_col
= cols() - 1;
798 _selecting
= CONTEXT_ROW_HEADER
;
799 damage_zone(current_row
, current_col
, select_row
, select_col
);
806 ret
= 0; // express disinterest
813 if (_auto_drag
== 1) {
817 if ( _resizing_col
> -1 ) {
820 // Let user drag even /outside/ the row/col widget.
821 // Don't allow column width smaller than 1.
822 // Continue to show FL_CURSOR_WE at all times during drag.
824 int offset
= _dragging_x
- Fl::event_x();
825 int new_w
= col_width(_resizing_col
) - offset
;
826 if ( new_w
< _col_resize_min
) new_w
= _col_resize_min
;
827 col_width(_resizing_col
, new_w
);
828 _dragging_x
= Fl::event_x();
831 change_cursor(FL_CURSOR_WE
);
833 if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED
) {
834 do_callback(CONTEXT_RC_RESIZE
, R
, C
);
837 else if ( _resizing_row
> -1 ) {
840 // Let user drag even /outside/ the row/col widget.
841 // Don't allow row width smaller than 1.
842 // Continue to show FL_CURSOR_NS at all times during drag.
844 int offset
= _dragging_y
- Fl::event_y();
845 int new_h
= row_height(_resizing_row
) - offset
;
846 if ( new_h
< _row_resize_min
) new_h
= _row_resize_min
;
847 row_height(_resizing_row
, new_h
);
848 _dragging_y
= Fl::event_y();
851 change_cursor(FL_CURSOR_NS
);
853 if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED
) {
854 do_callback(CONTEXT_RC_RESIZE
, R
, C
);
857 if (Fl::event_button() == 1 &&
858 _selecting
== CONTEXT_CELL
&&
859 context
== CONTEXT_CELL
) {
860 if (select_row
!= R
|| select_col
!= C
) {
861 damage_zone(current_row
, current_col
, select_row
, select_col
, R
, C
);
867 else if (Fl::event_button() == 1 &&
868 _selecting
== CONTEXT_ROW_HEADER
&&
869 context
& (CONTEXT_ROW_HEADER
|CONTEXT_COL_HEADER
|CONTEXT_CELL
)) {
870 if (select_row
!= R
) {
871 damage_zone(current_row
, current_col
, select_row
, select_col
, R
, C
);
876 else if (Fl::event_button() == 1 &&
877 _selecting
== CONTEXT_COL_HEADER
878 && context
& (CONTEXT_ROW_HEADER
|CONTEXT_COL_HEADER
|CONTEXT_CELL
)) {
879 if (select_col
!= C
) {
880 damage_zone(current_row
, current_col
, select_row
, select_col
, R
, C
);
886 // Enable autodrag if not resizing, and mouse has moved off table edge
887 if ( _resizing_row
< 0 && _resizing_col
< 0 && _auto_drag
== 0 &&
888 ( Fl::event_x() > x() + w() - 20 ||
889 Fl::event_x() < x() + row_header_width() ||
890 Fl::event_y() > y() + h() - 20 ||
891 Fl::event_y() < y() + col_header_height()
900 case CONTEXT_ROW_HEADER
: // release on row header
901 case CONTEXT_COL_HEADER
: // release on col header
902 case CONTEXT_CELL
: // release on a cell
903 case CONTEXT_TABLE
: // release on dead zone
904 if ( _resizing_col
== -1 && // not resizing a column
905 _resizing_row
== -1 && // not resizing a row
906 Fl_Widget::callback() && // callback defined
907 when() & FL_WHEN_RELEASE
&& // on button release
908 _last_row
== R
) { // release on same row PUSHed?
909 // Need this for eg. left clicking on a cell to select it
910 do_callback(context
, R
, C
);
917 if ( Fl::event_button() == 1 ) {
918 change_cursor(FL_CURSOR_DEFAULT
);
926 if ( context
== CONTEXT_COL_HEADER
&& // in column header?
927 resizeflag
) { // resize + near boundary?
928 change_cursor(FL_CURSOR_WE
); // show resize cursor
930 else if ( context
== CONTEXT_ROW_HEADER
&& // in row header?
931 resizeflag
) { // resize + near boundary?
932 change_cursor(FL_CURSOR_NS
); // show resize cursor
934 change_cursor(FL_CURSOR_DEFAULT
); // normal cursor
939 case FL_ENTER
: // See FLTK event docs on the FL_ENTER widget
940 if (!ret
) take_focus();
944 case FL_LEAVE
: // We want to track the mouse if resizing is allowed.
948 if ( event
== FL_LEAVE
) {
950 change_cursor(FL_CURSOR_DEFAULT
);
965 int is_row
= select_row
;
966 int is_col
= select_col
;
967 switch(Fl::event_key()) {
969 ret
= move_cursor(0, -1000000);
972 ret
= move_cursor(0, 1000000);
975 ret
= move_cursor(-(botrow
- toprow
- 1), 0);
978 ret
= move_cursor(botrow
- toprow
- 1 , 0);
981 ret
= move_cursor(0, -1);
984 ret
= move_cursor(0, 1);
987 ret
= move_cursor(-1, 0);
990 ret
= move_cursor(1, 0);
993 if ( Fl::event_state() & FL_SHIFT
) {
994 ret
= move_cursor(0, -1); // shift-tab -> left
996 ret
= move_cursor(0, 1); // tab -> right
1000 if (ret
&& Fl::focus() != this) {
1001 do_callback(CONTEXT_TABLE
, -1, -1);
1004 //if (!ret && Fl_Widget::callback() && when() & FL_WHEN_NOT_CHANGED )
1005 if ( Fl_Widget::callback() &&
1007 ( !ret
&& when() & FL_WHEN_NOT_CHANGED
) ||
1008 ( is_row
!= select_row
|| is_col
!= select_col
)
1011 do_callback(CONTEXT_CELL
, select_row
, select_col
);
1012 //damage_zone(current_row, current_col, select_row, select_col);
1019 change_cursor(FL_CURSOR_DEFAULT
);
1025 // Resize FLTK override
1026 // Handle resize events if user resizes parent window.
1028 void Fl_Table::resize(int X
, int Y
, int W
, int H
) {
1029 // Tell group to resize, and recalc our own widget as well
1030 Fl_Group::resize(X
, Y
, W
, H
);
1036 void Fl_Table::_redraw_cell(TableContext context
, int r
, int c
) {
1037 if ( r
< 0 || c
< 0 ) return;
1039 find_cell(context
, r
, c
, X
, Y
, W
, H
); // find positions of cell
1040 draw_cell(context
, r
, c
, X
, Y
, W
, H
); // call users' function to draw it
1044 See if the cell at row \p r and column \p c is selected.
1045 \returns 1 if the cell is selected, 0 if not.
1047 int Fl_Table::is_selected(int r
, int c
) {
1048 int s_left
, s_right
, s_top
, s_bottom
;
1050 if (select_col
> current_col
) {
1051 s_left
= current_col
;
1052 s_right
= select_col
;
1054 s_right
= current_col
;
1055 s_left
= select_col
;
1057 if (select_row
> current_row
) {
1058 s_top
= current_row
;
1059 s_bottom
= select_row
;
1061 s_bottom
= current_row
;
1064 if (r
>= s_top
&& r
<= s_bottom
&& c
>= s_left
&& c
<= s_right
) {
1071 Gets the region of cells selected (highlighted).
1073 \param[in] row_top Returns the top row of selection area
1074 \param[in] col_left Returns the left column of selection area
1075 \param[in] row_bot Returns the bottom row of selection area
1076 \param[in] col_right Returns the right column of selection area
1078 void Fl_Table::get_selection(int& row_top
, int& col_left
, int& row_bot
, int& col_right
) {
1079 if (select_col
> current_col
) {
1080 col_left
= current_col
;
1081 col_right
= select_col
;
1083 col_right
= current_col
;
1084 col_left
= select_col
;
1086 if (select_row
> current_row
) {
1087 row_top
= current_row
;
1088 row_bot
= select_row
;
1090 row_bot
= current_row
;
1091 row_top
= select_row
;
1096 Sets the region of cells to be selected (highlighted).
1098 So for instance, set_selection(0,0,0,0) selects the top/left cell in the table.
1099 And set_selection(0,0,1,1) selects the four cells in rows 0 and 1, column 0 and 1.
1101 \param[in] row_top Top row of selection area
1102 \param[in] col_left Left column of selection area
1103 \param[in] row_bot Bottom row of selection area
1104 \param[in] col_right Right column of selection area
1106 void Fl_Table::set_selection(int row_top
, int col_left
, int row_bot
, int col_right
) {
1107 damage_zone(current_row
, current_col
, select_row
, select_col
);
1108 current_col
= col_left
;
1109 current_row
= row_top
;
1110 select_col
= col_right
;
1111 select_row
= row_bot
;
1112 damage_zone(current_row
, current_col
, select_row
, select_col
);
1115 // Draw the entire Fl_Table
1116 // Override the draw() routine to draw the table.
1117 // Then tell the group to draw over us.
1119 void Fl_Table::draw() {
1120 draw_cell(CONTEXT_STARTPAGE
, 0, 0, // let user's drawing routine
1121 tix
, tiy
, tiw
, tih
); // prep new page
1123 // Let fltk widgets draw themselves first. Do this after
1124 // draw_cell(CONTEXT_STARTPAGE) in case user moves widgets around.
1125 // Use window 'inner' clip to prevent drawing into table border.
1126 // (unfortunately this clips FLTK's border, so we must draw it explicity below)
1128 fl_push_clip(wix
, wiy
, wiw
, wih
);
1134 // Explicitly draw border around widget, if any
1135 draw_box(box(), x(), y(), w(), h(), color());
1137 // If Fl_Scroll 'table' is hidden, draw its box
1138 // Do this after Fl_Group::draw() so we draw over scrollbars
1139 // that leak around the border.
1141 if ( ! table
->visible() ) {
1142 if ( damage() & FL_DAMAGE_ALL
|| damage() & FL_DAMAGE_CHILD
) {
1143 draw_box(table
->box(), tox
, toy
, tow
, toh
, table
->color());
1146 // Clip all further drawing to the inner widget dimensions
1147 fl_push_clip(wix
, wiy
, wiw
, wih
);
1149 // Only redraw a few cells?
1150 if ( ! ( damage() & FL_DAMAGE_ALL
) && _redraw_leftcol
!= -1 ) {
1151 fl_push_clip(tix
, tiy
, tiw
, tih
);
1152 for ( int c
= _redraw_leftcol
; c
<= _redraw_rightcol
; c
++ ) {
1153 for ( int r
= _redraw_toprow
; r
<= _redraw_botrow
; r
++ ) {
1154 _redraw_cell(CONTEXT_CELL
, r
, c
);
1159 if ( damage() & FL_DAMAGE_ALL
) {
1161 // Draw row headers, if any
1162 if ( row_header() ) {
1163 get_bounds(CONTEXT_ROW_HEADER
, X
, Y
, W
, H
);
1164 fl_push_clip(X
,Y
,W
,H
);
1165 for ( int r
= toprow
; r
<= botrow
; r
++ ) {
1166 _redraw_cell(CONTEXT_ROW_HEADER
, r
, 0);
1170 // Draw column headers, if any
1171 if ( col_header() ) {
1172 get_bounds(CONTEXT_COL_HEADER
, X
, Y
, W
, H
);
1173 fl_push_clip(X
,Y
,W
,H
);
1174 for ( int c
= leftcol
; c
<= rightcol
; c
++ ) {
1175 _redraw_cell(CONTEXT_COL_HEADER
, 0, c
);
1180 // This includes cells partially obscured off edges of table.
1181 // No longer do this last; you might think it would be nice
1182 // to draw over dead zones, but on redraws it flickers. Avoid
1183 // drawing over deadzones; prevent deadzones by sizing columns.
1185 fl_push_clip(tix
, tiy
, tiw
, tih
); {
1186 for ( int r
= toprow
; r
<= botrow
; r
++ ) {
1187 for ( int c
= leftcol
; c
<= rightcol
; c
++ ) {
1188 _redraw_cell(CONTEXT_CELL
, r
, c
);
1193 // Draw little rectangle in corner of headers
1194 if ( row_header() && col_header() ) {
1195 fl_rectf(wix
, wiy
, row_header_width(), col_header_height(), color());
1198 // Table has a boxtype? Close those few dead pixels
1199 if ( table
->box() ) {
1200 if ( col_header() ) {
1201 fl_rectf(tox
, wiy
, Fl::box_dx(table
->box()), col_header_height(), color());
1203 if ( row_header() ) {
1204 fl_rectf(wix
, toy
, row_header_width(), Fl::box_dx(table
->box()), color());
1208 // Table width smaller than window? Fill remainder with rectangle
1209 if ( table_w
< tiw
) {
1210 fl_rectf(tix
+ table_w
, tiy
, tiw
- table_w
, tih
, color());
1211 // Col header? fill that too
1212 if ( col_header() ) {
1213 fl_rectf(tix
+ table_w
,
1215 // get that corner just right..
1216 (tiw
- table_w
+ Fl::box_dw(table
->box()) -
1217 Fl::box_dx(table
->box())),
1218 col_header_height(),
1222 // Table height smaller than window? Fill remainder with rectangle
1223 if ( table_h
< tih
) {
1224 fl_rectf(tix
, tiy
+ table_h
, tiw
, tih
- table_h
, color());
1225 if ( row_header() ) {
1227 // Careful with that lower corner; don't use tih; when eg.
1228 // table->box(FL_THIN_UPFRAME) and hscrollbar hidden,
1229 // leaves a row of dead pixels.
1231 fl_rectf(wix
, tiy
+ table_h
, row_header_width(),
1232 (wiy
+wih
) - (tiy
+table_h
) -
1233 ( hscrollbar
->visible() ? SCROLLBAR_SIZE
: 0),
1238 // Both scrollbars? Draw little box in lower right
1239 if ( vscrollbar
->visible() && hscrollbar
->visible() ) {
1240 fl_rectf(vscrollbar
->x(), hscrollbar
->y(),
1241 vscrollbar
->w(), hscrollbar
->h(), color());
1243 draw_cell(CONTEXT_ENDPAGE
, 0, 0, // let user's drawing
1244 tix
, tiy
, tiw
, tih
); // routines cleanup
1246 _redraw_leftcol
= _redraw_rightcol
= _redraw_toprow
= _redraw_botrow
= -1;
1252 // End of "$Id: Fl_Table.cxx 7950 2010-12-05 01:22:53Z greg.ercolano $".