1
// This is a (fairly heavily) modified version of Jason Bryan's
2
// Flu_Tree_Browser, for inclusion in Gmsh.
4
// The following changes have been made:
6
// * Removed drag-and-drop code
7
// * Added pixmaps in the .cpp file
8
// * Removed IntStack (replaced by std::vector)
9
// * Removed FluSimpleString (replaced by std::string)
10
// * Removed animation code
11
// * Removed the "find" pass when adding a new node (cf. "CG")
12
// to improve speed (WARNING: this fundamentally changes the
13
// way the tree works: calling add("/a/b") followed by add("/a/c")
14
// will create two "a" branches. This is the desired behaviour
15
// for Gmsh; probably not for you)
18
/***************************************************************
19
* FLU - FLTK Utility Widgets
20
* Copyright (C) 2002 Ohio Supercomputer Center, Ohio State University
22
* This file and its content is protected by a software license.
23
* You should have received a copy of this license with this file.
24
* If not, please contact the Ohio Supercomputer Center immediately:
25
* Attn: Jason Bryan Re: FLU 1224 Kinnear Rd, Columbus, Ohio 43212
27
***************************************************************/
30
#include <FL/fl_draw.H>
31
#include "Flu_Tree_Browser.h"
33
static char * plus_xpm[] = {
55
static char * minus_xpm[] = {
77
static char * folder_closed_xpm[] = {
140
static char * folder_open_xpm[] = {
183
" .&&.&!~{]^/%%%.( ",
184
" .&&._:=<$;%%%%.( ",
193
#define MAX( x, y ) ( (x)>(y) ? (x) : (y) )
194
#define MIN( x, y ) ( (x)<(y) ? (x) : (y) )
196
bool Flu_Tree_Browser::USE_FLU_WIDGET_CALLBACK = false;
198
Flu_Tree_Browser :: NodeList :: NodeList()
204
Flu_Tree_Browser :: NodeList :: ~NodeList()
209
typedef Flu_Tree_Browser::Node* NodeP;
211
bool Flu_Tree_Browser :: NodeList :: search( const char *n, int &index )
217
// we know we have at least one node. so use it to get the RData struct to find out what
218
// the insertion mode is
219
int iMode = _nodes[0]->tree->insertion_mode();
221
if( iMode == FLU_INSERT_SORTED || iMode == FLU_INSERT_SORTED_REVERSE )
222
return( binSearch( n, index ) );
224
return( linSearch( n, index ) );
227
bool Flu_Tree_Browser :: NodeList :: search( Node *n, int &index )
233
// we know we have at least one node. so use it to get the RData struct to find out what
234
// the insertion mode is
235
int iMode = _nodes[0]->tree->insertion_mode();
237
if( iMode == FLU_INSERT_SORTED || iMode == FLU_INSERT_SORTED_REVERSE )
238
return( binSearch( n, index ) );
240
return( linSearch( n, index ) );
243
bool Flu_Tree_Browser :: NodeList :: linSearch( const char *n, int &index )
246
for( int i = 0; i < _nNodes; i++ )
248
if( strcmp( n, _nodes[i]->label() ) == 0 )
257
bool Flu_Tree_Browser :: NodeList :: linSearch( Node *n, int &index )
260
for( int i = 0; i < _nNodes; i++ )
271
bool Flu_Tree_Browser :: NodeList :: binSearch( Node *n, int &index )
273
if( binSearch( n->label(), index ) )
275
// the search found the first node with the label. since there are identical entries
276
// allowed, it may not be the actual node we want. therefore search forward until we find it
277
for( ; index < _nNodes; index++ )
278
if( _nodes[index] == n )
286
bool Flu_Tree_Browser :: NodeList :: binSearch( const char *n, int &index )
288
// do a binary search for a child with name == "n"
289
// return true if the child is found, and store its index in "index"
290
// return false if the child is not found, and store the index it would
293
// special case: no nodes
300
// we know we have at least one node. so use it to get the RData struct to find out what
301
// the insertion mode is
302
int iMode = _nodes[0]->tree->insertion_mode();
304
// special case: 1 node
307
int val = strcmp( n, _nodes[0]->label() );
308
if( iMode == FLU_INSERT_SORTED_REVERSE )
322
int first = 0, last = _nNodes - 1;
323
int val1, val2, mVal;
326
// the range is down to 2 nodes
327
if( last == first + 1 )
329
val1 = strcmp( n, _nodes[first]->label() );
330
if( iMode == FLU_INSERT_SORTED_REVERSE )
342
val2 = strcmp( n, _nodes[last]->label() );
343
if( iMode == FLU_INSERT_SORTED_REVERSE )
362
// pick which half of the array to search next
363
int midpoint = first + ((last-first)>>1);
364
mVal = strcmp( n, _nodes[midpoint]->label() );
365
if( iMode == FLU_INSERT_SORTED_REVERSE )
378
// we found *a* node equal to "n", now find the first node equal to "n"
379
// by searching until we hit a node not equal to "n"
380
for( first = index; first > 0; first-- )
381
if( strcmp( n, _nodes[first-1]->label() ) != 0 )
388
int Flu_Tree_Browser :: NodeList :: compareNodes( const void *arg1, const void* arg2 )
390
Flu_Tree_Browser::Node *n1 = *((Flu_Tree_Browser::Node**)arg1), *n2 = *((Flu_Tree_Browser::Node**)arg2);
391
return strcmp( n1->text.c_str(), n2->text.c_str() );
394
int Flu_Tree_Browser :: NodeList :: reverseCompareNodes( const void *arg1, const void* arg2 )
396
Flu_Tree_Browser::Node *n1 = *((Flu_Tree_Browser::Node**)arg1), *n2 = *((Flu_Tree_Browser::Node**)arg2);
397
return( -strcmp( n1->text.c_str(), n2->text.c_str() ) );
400
void Flu_Tree_Browser :: NodeList :: sort()
404
// we know we have at least one node. so use it to get the RData struct to find out what
405
// the insertion mode is
406
int iMode = _nodes[0]->tree->insertion_mode();
407
if( iMode == FLU_INSERT_SORTED )
408
qsort( _nodes, _nNodes, sizeof(Node*), compareNodes );
409
else if( iMode == FLU_INSERT_SORTED_REVERSE )
410
qsort( _nodes, _nNodes, sizeof(Node*), reverseCompareNodes );
414
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: insert( const char* fullpath, int pos )
416
// insert the new node at the back of the tree
417
int imode = tree->insertion_mode();
418
tree->insertion_mode( FLU_INSERT_BACK );
419
Node *n = add( fullpath );
420
tree->insertion_mode( imode );
421
if( !n ) return NULL;
422
// find the node at position "pos" and
423
// move the new node before it, so it takes over position "pos"
424
if( pos < 0 ) pos = 0;
425
if( pos >= children() ) pos = children()-1;
426
move( n, MOVE_BEFORE, child(pos) );
430
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: insert_branch( const char* fullpath, int pos )
432
std::string p( fullpath );
433
if( p.size() && p[p.size()-1] != '/' && p[p.size()-1] != '\\' ) p += "/";
434
return insert( p.c_str(), pos );
437
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: insert_leaf( const char* fullpath, int pos )
439
std::string p( fullpath );
440
if( p.size() && ( p[p.size()-1] == '/' || p[p.size()-1] == '\\' ) ) p[p.size()-1] = '\0';
441
return insert( p.c_str(), pos );
444
bool Flu_Tree_Browser :: Node :: move( int pos )
446
// get this node's position
448
if( i == -1 ) return false;
449
// get the node in our parent at index "pos"
450
if( !parent() ) return false;
451
if( pos < 0 ) pos = 0;
452
if( pos >= parent()->children() ) pos = parent()->children()-1;
453
Node *n = parent()->child( pos );
454
// move this node to be before its sibling, so it takes over position "pos"
455
return move( this, MOVE_BEFORE, n );
458
bool Flu_Tree_Browser :: Node :: swap( Node* n1, Node* n2 )
460
if( n1->tree != n2->tree ) return false;
461
Node *p1 = n1->parent(), *p2 = n2->parent();
462
if( !p1 || !p2 ) return false;
463
int i, index1 = -1, index2 = -1;
464
for( i = 0; i < p1->children(); i++ )
466
if( p1->child(i) == n1 )
472
if( index1 == -1 ) return false;
473
for( i = 0; i < p2->children(); i++ )
475
if( p2->child(i) == n2 )
481
if( index2 == -1 ) return false;
482
p1->_children._nodes[index1] = n2;
483
p2->_children._nodes[index2] = n1;
487
bool Flu_Tree_Browser :: Node :: move( Node* n1, int where, Node* n2 )
489
if( isMoveValid( n1, where, n2 ) )
490
return( NodeList::move( n1, where, n2 ) );
495
void Flu_Tree_Browser :: Node :: sort()
498
for( int i = 0; i < _children.size(); i++ )
499
_children.child(i)->sort();
502
bool Flu_Tree_Browser :: Node :: is_ancestor( Node* n )
515
bool Flu_Tree_Browser :: Node :: is_descendent( Node* n )
517
return n->is_ancestor( this );
520
bool Flu_Tree_Browser :: NodeList :: move( Node* n1, int where, Node* n2 )
530
// try to move n1 to the first child position of n2
531
if( where == MOVE_INSIDE )
533
if( !n2->is_branch() )
535
// get the parent of n1
536
Node* p1 = n1->parent();
538
// remove n1 from its parent's list
539
p1->_children.erase( n1 );
541
int iMode = n1->tree->insertion_mode();
542
if( iMode == FLU_INSERT_SORTED || iMode == FLU_INSERT_SORTED_REVERSE )
543
n2->_children.add( n1 );
545
n2->_children.add( n1, 0 );
546
// update the parent of n1
551
// find the position of n2 in its parent's list
552
Node* p2 = n2->parent();
555
int index = 0, removed = -1;
556
if( p2->_children.search( n2, index ) )
558
// get the parent of n1
559
Node* p1 = n1->parent();
561
// remove n1 from its parent's list. remember the position it was removed from
562
removed = p1->_children.erase( n1 );
564
// if n1 and n2 have the same parent, and if n1 came before the spot where
565
// n2 will be inserted, then our indexing is off by one because n1 has been removed
566
if( p1 == p2 && removed <= index )
569
if( where == MOVE_AFTER )
572
// insert n1 at the proper position
573
p2->_children.add( n1, index );
575
// update the parent of n1
582
void Flu_Tree_Browser :: NodeList :: add( Node* n, int position )
585
int mode = n->tree->insertion_mode();
587
// if the list is out of room, allocate a new one that's bigger
588
if( _nNodes == _size )
590
int newSize = ( _size == 0 ) ? 1 : _size*2; // double the size of the old list (same behavior as STL vector)
591
// allocate the new list
592
Node** newNodes = new NodeP[ newSize ];
593
// copy the old list to the new list
594
memcpy( newNodes, _nodes, _nNodes*sizeof(Node*) );
595
// delete the old list and replace it with the new list
597
//n->tree->rdata.cbNode = NULL;
604
if( position > _nNodes )
609
else if( mode == FLU_INSERT_SORTED || mode == FLU_INSERT_SORTED_REVERSE )
611
// search through the list until we find where to insert the node
612
binSearch( n->label(), index );
614
else if( mode == FLU_INSERT_FRONT )
618
else if( mode == FLU_INSERT_BACK )
625
// shift all entries from the new insertion point down one spot
626
// to make room for the new node
627
for( i = _nNodes - 1; i >= index; i-- )
628
_nodes[i+1] = _nodes[i];
636
int Flu_Tree_Browser :: NodeList :: erase( Node *n )
642
if( search( n, index ) )
644
// move all the others down one spot to remove the node
645
for( int i = index; i < _nNodes-1; i++ )
646
_nodes[i] = _nodes[i+1];
654
int Flu_Tree_Browser :: NodeList :: erase( const char* n )
660
if( search( n, index ) )
662
// move all the others down one spot to remove the node
663
for( int i = index; i < _nNodes-1; i++ )
664
_nodes[i] = _nodes[i+1];
671
void Flu_Tree_Browser :: NodeList :: erase( int n )
673
// make sure n is in range
674
if( ( n < 0 ) || ( n >= _nNodes ) )
677
// move all the others down one spot to remove the node
678
for( int i = n; i < _nNodes-1; i++ )
679
_nodes[i] = _nodes[i+1];
684
void Flu_Tree_Browser :: NodeList :: clear()
690
// _nodes[0]->tree->rdata.cbNode = NULL;
697
int Flu_Tree_Browser :: NodeList :: findNum( const char *n )
699
if( ( _nNodes == 0 ) || ( n == 0 ) )
702
// see if there is a first node equal to "n"
704
if( !search( n, index ) )
707
// now search forward until we hit a node not equal to "n"
708
for( last = index; last < _nNodes-1; last++ )
709
if( strcmp( n, _nodes[last+1]->label() ) != 0 )
712
return last - index + 1;
715
Flu_Tree_Browser::Node* Flu_Tree_Browser :: NodeList :: find( const char* n, int which )
717
if( ( _nNodes == 0 ) || ( n == 0 ) || ( which == 0 ) )
720
// see if there is a first node equal to "n"
722
if( !search( n, first ) )
725
// now search forward and try to find the which'th node named "n"
727
for( index = first; index < _nNodes; index++ )
729
if( strcmp( n, _nodes[index]->label() ) == 0 )
741
return _nodes[index];
744
#define SCROLL_SIZE 15
746
Flu_Tree_Browser :: Flu_Tree_Browser( int x, int y, int w, int h, const char *l )
747
: Fl_Group( x, y, w, h )
750
_box = new Fl_Group( x, y, w-SCROLL_SIZE, h-SCROLL_SIZE );
751
_box->resizable( NULL );
753
//_box->set_output();
754
scrollV = new Fl_Scrollbar( x+w-SCROLL_SIZE, y, SCROLL_SIZE, h-SCROLL_SIZE );
755
scrollV->type( FL_VERTICAL );
756
scrollV->callback( _scrollCB, this );
757
scrollV->value( 0, 1, 0, 0 );
758
scrollH = new Fl_Scrollbar( x, y+h-SCROLL_SIZE, w-SCROLL_SIZE, SCROLL_SIZE );
759
scrollH->type( FL_HORIZONTAL );
760
scrollH->callback( _scrollCB, this );
761
scrollH->value( 0, 1, 0, 0 );
762
scrollBox = new Fl_Group( x+w-SCROLL_SIZE, y+h-SCROLL_SIZE, SCROLL_SIZE, SCROLL_SIZE );
763
scrollBox->box( FL_UP_BOX );
767
// set up the recursive data structure
768
memset( &rdata, 0, sizeof(rdata) );
772
rdata.cbReason = FLU_NOTHING;
774
rdata.dragging = false;
775
rdata.forceResize = true;
776
rdata.shiftSelect = false;
777
rdata.shiftSelectAll = false;
779
rdata.searchIndex = 1;
780
rdata.defaultCollapseIcons[0] = new Fl_Pixmap( (char*const*)plus_xpm );
781
rdata.defaultCollapseIcons[1] = new Fl_Pixmap( (char*const*)minus_xpm );
782
rdata.defaultBranchIcons[0] = new Fl_Pixmap( (char*const*)folder_closed_xpm );
783
rdata.defaultBranchIcons[1] = new Fl_Pixmap( (char*const*)folder_open_xpm );
787
// set the default values for the tree
788
selection_follows_hilight( false );
789
select_under_mouse( false );
790
open_without_children( true );
791
auto_branches( false );
792
double_click_opens( true );
793
move_only_same_group( false );
794
allow_leaf_duplication( true );
795
shaded_entry_colors( FL_WHITE, FL_WHITE );
796
collapse_icons( NULL, NULL );
797
//branch_icons( NULL, NULL );
798
rdata.branchIcons[0] = rdata.defaultBranchIcons[0];
799
rdata.branchIcons[1] = rdata.defaultBranchIcons[1];
801
branch_text( FL_BLACK, FL_HELVETICA_BOLD, 12 );
802
leaf_text( FL_BLACK, FL_HELVETICA, 12 );
804
when( FL_WHEN_CHANGED );
806
selection_color( FL_SELECTION_COLOR );
808
connector_style( FL_DARK2, FL_DOT );
809
selection_mode( FLU_MULTI_SELECT );
810
selection_drag_mode( FLU_DRAG_TO_SELECT );
811
insertion_mode( FLU_INSERT_SORTED );
812
show_connectors( true );
815
show_branches( true );
816
open_on_select( false );
817
//root_always_open( false );
823
resize( x, y, w, h );
826
Flu_Tree_Browser :: ~Flu_Tree_Browser()
828
delete rdata.defaultCollapseIcons[0];
829
delete rdata.defaultCollapseIcons[1];
831
delete rdata.defaultBranchIcons[0];
832
delete rdata.defaultBranchIcons[1];
835
void Flu_Tree_Browser :: auto_branches( bool b )
837
rdata.autoBranches = b;
840
void Flu_Tree_Browser :: collapse_icons( Fl_Image *closed, Fl_Image *open )
843
rdata.collapseIcons[0] = closed;
845
rdata.collapseIcons[0] = rdata.defaultCollapseIcons[0];
848
rdata.collapseIcons[1] = open;
850
rdata.collapseIcons[1] = rdata.defaultCollapseIcons[1];
853
void Flu_Tree_Browser :: branch_icons( Fl_Image *closed, Fl_Image *open )
856
rdata.branchIcons[0] = closed;
858
//rdata.branchIcons[0] = rdata.defaultBranchIcons[0];
861
rdata.branchIcons[1] = open;
863
//rdata.branchIcons[1] = rdata.defaultBranchIcons[1];
866
void Flu_Tree_Browser :: set_default_branch_icons()
868
rdata.branchIcons[0] = rdata.defaultBranchIcons[0];
869
rdata.branchIcons[1] = rdata.defaultBranchIcons[1];
872
void Flu_Tree_Browser :: leaf_icon( Fl_Image *icon )
874
rdata.leafIcon = icon;
877
bool Flu_Tree_Browser :: inside_entry_area( int x, int y )
879
if( scrollH->visible() && scrollV->visible() )
880
return( x > _box->x() && y > _box->y() &&
881
x < (_box->x()+_box->w()-scrollV->w()) &&
882
y < (_box->y()+_box->h()-scrollH->h()) );
883
else if( !scrollH->visible() && !scrollV->visible() )
884
return( x > _box->x() && y > _box->y() &&
885
x < (_box->x()+_box->w()) &&
886
y < (_box->y()+_box->h()) );
887
else if( scrollH->visible() )
888
return( x > _box->x() && y > _box->y() &&
889
x < (_box->x()+_box->w()) &&
890
y < (_box->y()+_box->h()-scrollH->h()) );
892
return( x > _box->x() && y > _box->y() &&
893
x < (_box->x()+_box->w()-scrollV->w()) &&
894
y < (_box->y()+_box->h()) );
897
void Flu_Tree_Browser :: resize( int X, int Y, int W, int H )
899
Fl_Group::resize( X, Y, W, H );
901
int dx = Fl::box_dx(box()), dy = Fl::box_dy(box()), dw = Fl::box_dw(box()), dh = Fl::box_dh(box());
903
rdata.x = X+dx; rdata.y = Y+dy; rdata.totalW = rdata.x;
904
root.recurse( rdata, Node::MEASURE );
905
rdata.totalW -= X-dx;
906
rdata.totalH = rdata.y - Y-dy;
908
// if the size of the tree is bigger than the window, turn on the scrollbars
909
bool hOn = false, vOn = false;
910
if( rdata.totalW > W-dw )
912
if( rdata.totalH > H-dh )
915
// check if turning on one scrollbar actually forces the other to turn on
916
if( hOn && ( rdata.totalH > H-SCROLL_SIZE ) )
918
if( vOn && ( rdata.totalW > W-SCROLL_SIZE ) )
921
// now resize the other kids depending on the state of the scrollbars
923
_box->resize( X, Y, W, H );
924
if( hOn && vOn ) // both scrollbars on
926
scrollH->resize( X+dx, Y+H-SCROLL_SIZE-dy, W-SCROLL_SIZE-dw, SCROLL_SIZE );
928
scrollV->resize( X+W-SCROLL_SIZE-dx, Y+dy, SCROLL_SIZE, H-SCROLL_SIZE-dh );
930
scrollBox->resize( X+W-SCROLL_SIZE-dx, Y+H-SCROLL_SIZE-dy, SCROLL_SIZE, SCROLL_SIZE );
933
// set the scrollbar sizes and values
934
int hDelta = rdata.totalW - W+dw + SCROLL_SIZE, scrollHW = scrollH->w()-SCROLL_SIZE-SCROLL_SIZE;
935
hDelta = MAX( hDelta, 0 );
936
scrollH->value( MIN( scrollH->value(), hDelta ), 1, 0, hDelta );
937
scrollH->slider_size( MAX( (float)SCROLL_SIZE/float(scrollHW), float(scrollHW-hDelta)/float(scrollHW) ) );
939
int vDelta = rdata.totalH - H+dh + SCROLL_SIZE, scrollVH = scrollV->h()-SCROLL_SIZE-SCROLL_SIZE;
940
vDelta = MAX( vDelta, 0 );
941
scrollV->value( MIN( scrollV->value(), vDelta ), 1, 0, vDelta );
942
scrollV->slider_size( MAX( (float)SCROLL_SIZE/float(scrollVH), float(scrollVH-vDelta)/float(scrollVH) ) );
943
_box->resize( X, Y, W-SCROLL_SIZE, H-SCROLL_SIZE );
945
else if( !hOn && !vOn ) // neither on
951
else if( hOn ) // just horizontal on
953
scrollH->resize( X+dx, Y+H-SCROLL_SIZE-dy, W-dw, SCROLL_SIZE );
958
// set the scrollbar size and value
959
int hDelta = rdata.totalW - W+dw, scrollHW = scrollH->w()-SCROLL_SIZE-SCROLL_SIZE;
960
hDelta = MAX( hDelta, 0 );
961
scrollH->value( MIN( scrollH->value(), hDelta ), 1, 0, hDelta );
962
scrollH->slider_size( MAX( (float)SCROLL_SIZE/float(scrollHW), float(scrollHW-hDelta)/float(scrollHW) ) );
963
_box->resize( X, Y, W, H-SCROLL_SIZE );
965
else if( vOn ) // just vertical on
968
scrollV->resize( X+W-SCROLL_SIZE-dx, Y+dy, SCROLL_SIZE, H-dh );
972
// set the scrollbar size and value
973
int vDelta = rdata.totalH - H+dh, scrollVH = scrollV->h()-SCROLL_SIZE-SCROLL_SIZE;
974
vDelta = MAX( vDelta, 0 );
975
scrollV->value( MIN( scrollV->value(), vDelta ), 1, 0, vDelta );
976
scrollV->slider_size( MAX( (float)SCROLL_SIZE/float(scrollVH), float(scrollVH-vDelta)/float(scrollVH) ) );
977
_box->resize( X, Y, W-SCROLL_SIZE, H );
980
rdata.browserX = _box->x() + dx;
981
rdata.browserY = _box->y() + dy;
982
rdata.browserW = _box->w() - dw;
983
rdata.browserH = _box->h() - dh;
987
rdata.forceResize = true; // weird hack to get the scrollbars to turn on right the first time
990
int Flu_Tree_Browser :: handle( int event )
992
if( event == FL_NO_EVENT )//|| event == FL_MOVE )
995
if( event == FL_FOCUS )//&& rdata.lastHilighted )
997
//set_hilighted( rdata.lastHilighted );
999
//Fl_Group::handle( event );
1004
if( event == FL_UNFOCUS )
1006
//if( lastEvent != FL_LEAVE )
1008
//rdata.lastHilighted = rdata.hilighted;
1010
//set_hilighted( NULL );
1011
//lastEvent = event;
1012
Fl_Group::handle( event );
1017
if( !rdata.dragging && !( event == FL_MOVE && rdata.selectUnderMouse ) )
1019
if( ! (event == FL_MOVE || event == FL_ENTER || event == FL_LEAVE ) )
1022
if( Fl_Group::handle( event ) )
1024
//if( event == FL_KEYDOWN || event == FL_KEYUP )
1028
//if (scrollV && Fl::event_inside(scrollV) && scrollV->handle(event)) return 1;
1029
//if (scrollH && Fl::event_inside(scrollH) && scrollH->handle(event)) return 1;
1032
if( event == FL_RELEASE )
1035
rdata.dragging = false;
1041
int dx = Fl::box_dx(box()), dy = Fl::box_dy(box());
1043
// set some initial values for the recursive data structure
1044
// account for the scrollbar positions
1045
rdata.x = x()+dx; rdata.y = y()+dy;
1046
if( scrollH->visible() )
1047
rdata.x -= scrollH->value();
1048
if( scrollV->visible() )
1049
rdata.y -= scrollV->value();
1051
rdata.previous = NULL;
1053
rdata.visibilityChanged = false;
1055
// catch cursor keys for moving the hilighted entry or selecting all entries
1056
if( event == FL_KEYDOWN )
1058
// move hilighted entry up
1059
if( Fl::event_key() == FL_Up )
1066
// move hilighted entry down
1067
else if( Fl::event_key() == FL_Down )
1075
else if( Fl::event_state(FL_CTRL) && Fl::event_key() == 'a' )
1083
// check for the Home key
1084
else if( Fl::event_key() == FL_Home )
1086
// set the hilighted entry to be the first entry
1087
if( rdata.showRoot || ( rdata.root->_children.size() == 0 ) )
1088
set_hilighted( rdata.root );
1089
else if( rdata.root->_children.size() > 0 )
1090
set_hilighted( rdata.root->_children.child(0) );
1094
// check for the End key
1095
else if( Fl::event_key() == FL_End )
1097
// set the hilighted entry to be the last visible entry
1098
if( rdata.showRoot && ( rdata.root->_children.size() == 0 ) )
1099
set_hilighted( rdata.root );
1102
// find the last node by repeatedly looking for the last child until there are no more branches
1104
while( n->_children.size() && n->open() )
1105
n = n->_children.child( n->_children.size()-1 );
1112
// pass the event down the tree
1113
int val = root.recurse( rdata, Node::HANDLE, event );
1117
if( rdata.visibilityChanged )
1118
root.determineVisibility();
1122
// special case: if multi-select or single-select and user clicks on no items, unselect all items
1123
else if( (rdata.selectionMode != FLU_NO_SELECT) && (event == FL_PUSH) && (!Fl::event_state(FL_CTRL)) )
1126
set_hilighted( NULL );
1127
rdata.forceResize = true;
1133
if( event == FL_SHOW || event == FL_HIDE )
1134
root.determineVisibility();
1136
return Fl_Group::handle( event );
1140
void Flu_Tree_Browser :: insertion_mode( int m )
1142
rdata.insertionMode = m;
1146
void Flu_Tree_Browser :: set_hilighted( Flu_Tree_Browser::Node* n )
1148
if( rdata.hilighted == n && when() != FL_WHEN_NOT_CHANGED )
1151
if( rdata.hilighted )
1152
rdata.hilighted->do_callback( FLU_UNHILIGHTED );
1153
rdata.hilighted = n;
1154
if( rdata.hilighted )
1155
rdata.hilighted->do_callback( FLU_HILIGHTED );
1157
if( rdata.hilighted )
1159
if( rdata.selectionFollowsHilight )
1161
if( rdata.selectionMode == FLU_SINGLE_SELECT )
1163
rdata.hilighted->select( true );
1166
int extraH = scrollH->visible() ? scrollH->h() : 0;
1168
// if the hilighted entry is below the visible bounds of the browser, move the vertical scrollbar
1169
// so the hilighted entry is the last visible entry
1170
if( rdata.hilighted->currentY-y()+rdata.hilighted->currentH > scrollV->value()+h()-extraH )
1171
((Fl_Valuator*)scrollV)->value( rdata.hilighted->currentY-y() - h()+extraH + rdata.hilighted->currentH );
1173
// if the hilighted entry is above the visible bounds of the browser, move the vertical scrollbar
1174
// so the hilighted entry is the first visible entry
1175
if( rdata.hilighted->currentY-y() < scrollV->value() )
1176
((Fl_Valuator*)scrollV)->value( rdata.hilighted->currentY-y() );
1181
int Flu_Tree_Browser :: num_selected()
1183
return root.recurse( rdata, Node::COUNT_SELECTED );
1186
int Flu_Tree_Browser :: Node :: num_selected()
1188
return recurse( tree->rdata, COUNT_SELECTED );
1191
Flu_Tree_Browser::Node* Flu_Tree_Browser :: get_selected( int index )
1193
return root.get_selected( index );
1196
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: get_selected( int index )
1198
tree->rdata.counter = 0;
1199
tree->rdata.searchIndex = index;
1200
Node *n = modify( 0, GET_SELECTED, tree->rdata );
1201
tree->rdata.searchIndex = 1;
1205
Flu_Tree_Browser :: Node :: Node( const char *lbl )
1219
currentY = currentH = 0;
1221
CLEAR(EXPAND_TO_WIDTH);
1228
cIcon[0] = cIcon[1] = bIcon[0] = bIcon[1] = lIcon = 0;
1231
Flu_Tree_Browser :: Node :: Node( bool l, const char* n, Node *p, RData &rdata, Fl_Widget *w, bool showLbl )
1244
CLEAR(EXPAND_TO_WIDTH);
1248
currentY = currentH = 0;
1249
cIcon[0] = cIcon[1] = bIcon[0] = bIcon[1] = lIcon = 0;
1250
SET( SHOW_LABEL, showLbl );
1255
_id = rdata.nextId++;
1259
void Flu_Tree_Browser :: Node :: initType()
1263
lIcon = tree->rdata.leafIcon;
1264
textColor = tree->rdata.defLeafColor;
1265
textFont = tree->rdata.defLeafFont;
1266
textSize = tree->rdata.defLeafSize;
1270
cIcon[0] = tree->rdata.collapseIcons[0];
1271
cIcon[1] = tree->rdata.collapseIcons[1];
1272
bIcon[0] = tree->rdata.branchIcons[0];
1273
bIcon[1] = tree->rdata.branchIcons[1];
1274
textColor = tree->rdata.defBranchColor;
1275
textFont = tree->rdata.defBranchFont;
1276
textSize = tree->rdata.defBranchSize;
1280
Flu_Tree_Browser :: Node :: ~Node()
1282
// if this node is in a tree, make sure it isn't holding a reference to us
1285
if( tree->rdata.hilighted == this ) tree->rdata.hilighted = NULL;
1286
//if( tree->rdata.lastHilighted == this ) tree->rdata.lastHilighted = NULL;
1287
if( tree->rdata.grabbed == this ) tree->rdata.grabbed = NULL;
1288
if( tree->rdata.dragNode == this ) tree->rdata.dragNode = NULL;
1293
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: first()
1298
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: first_branch()
1303
if( n->is_branch() )
1311
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: first_leaf()
1324
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: last()
1326
if( children() == 0 )
1329
return( child( children() - 1 )->last() );
1332
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: last_branch()
1337
if( n->is_branch() )
1345
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: last_leaf()
1358
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: next_sibling()
1363
for( index = 0; index < _parent->children(); index++ )
1364
if( _parent->child(index) == this )
1366
// if we are the last child of our parent, then we have no next sibling
1367
if( index == _parent->children()-1 )
1369
// otherwise return our next sibling
1371
return( _parent->child(index+1) );
1374
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: previous_sibling()
1379
for( index = 0; index < _parent->children(); index++ )
1380
if( _parent->child(index) == this )
1382
// if we are the first child of our parent, then we have no previous sibling
1385
// otherwise return our previous sibling
1387
return( _parent->child(index-1) );
1390
int Flu_Tree_Browser :: Node :: index() const
1395
for( index = 0; index < _parent->children(); index++ )
1396
if( _parent->child(index) == this )
1401
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: next()
1403
// take care of the root node as a special case
1412
// if we are a branch, then the next node is our first child, unless we don't have any children
1413
if( is_branch() && _children.size() )
1414
return _children.child(0);
1417
// otherwise, the next node is our next sibling. if there is no next sibling (because we
1418
// are the last child of our parent), then the next node is the next sibling of our parent (and so on...)
1419
Node *p = parent(), *n = next_sibling();
1426
n = p->next_sibling();
1434
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: next_branch()
1439
if( n->is_branch() )
1447
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: next_leaf()
1460
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: previous()
1462
// take care of the root node as a special case
1466
// the previous node is either our parent's
1467
// previous sibling (if that sibling exists and is a leaf or a branch with no children),
1468
// or the last child of our parent's previous sibling (if that sibling exists and is
1469
// a branch with children). if there is no previous sibling, then the previous node
1471
Node *n = previous_sibling();
1476
if( n->is_leaf() ) // is leaf, so that is the previous node
1478
else if( n->children() ) // is branch with some children, so previous node is last child
1479
return( n->last() );
1480
else // is branch with no children, so that is the previous node
1485
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: previous_branch()
1487
Node *n = previous();
1490
if( n->is_branch() )
1498
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: previous_leaf()
1500
Node *n = previous();
1511
void Flu_Tree_Browser :: Node :: determineVisibility( bool parentVisible )
1520
for( int i = 0; i < _children.size(); i++ )
1521
_children.child(i)->determineVisibility( parentVisible && open() );
1524
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: child( int i ) const
1526
if( i < 0 || i >= _children.size() )
1529
return _children.child(i);
1532
void Flu_Tree_Browser :: Node :: clear()
1535
for( int i = 0; i < _children.size(); i++ )
1537
//if( tree->rdata.cbNode == _children.child(i) )
1538
//tree->rdata.cbNode = NULL;
1539
delete _children.child(i);
1544
if( _group->parent() )
1545
_group->parent()->remove( *_group );
1546
while( _group->children() )
1547
_group->remove( *_group->child(0) );
1553
void Flu_Tree_Browser :: Node :: print( int spaces )
1555
for( int s = 0; s < spaces; s++ )
1558
printf( " %s\n", text.c_str() );
1560
printf( "[%s]\n", text.c_str() );
1562
for( int i = 0; i < _children.size(); i++ )
1563
_children.child(i)->print( spaces+2 );
1566
void Flu_Tree_Browser :: draw()
1568
if( rdata.forceResize )
1570
resize( x(), y(), w(), h() );
1571
rdata.forceResize = false;
1574
// draw the background color
1575
//fl_draw_box( _box->box(), _box->x(), _box->y(), _box->w(), _box->h(), _box->color() );
1576
fl_draw_box( box(), x(), y(), w(), h(), color() );
1578
int dx = Fl::box_dx(box()), dy = Fl::box_dy(box()),
1579
dw = Fl::box_dw(box()), dh = Fl::box_dh(box());
1581
// set up the recursive data structure
1582
rdata.x = x()+dx; rdata.y = y()+dy;
1583
// account for the positions of the scrollbars
1584
if( scrollH->visible() )
1585
rdata.x -= scrollH->value();
1586
if( scrollV->visible() )
1587
rdata.y -= scrollV->value();
1590
rdata.bgColor = _box->color();
1591
rdata.shadedIndex = 0;
1593
// pick the connector line and selection colors depending on the active state
1596
rdata.lineColor = rdata.defLineColor;
1597
rdata.selectionColor = rdata.defSelectionColor;
1601
rdata.lineColor = fl_inactive( rdata.defLineColor );
1602
rdata.selectionColor = fl_inactive( rdata.defSelectionColor );
1606
fl_push_clip( x()+dx, y()+dy, w()-dw, h()-dh );
1607
root.recurse( rdata, Node::DRAW );
1612
draw_child( *scrollBox );
1613
draw_child( *scrollH );
1614
draw_child( *scrollV );
1616
// draw the box last so it's on top
1617
//fl_draw_box( _box->box(), _box->x(), _box->y(), _box->w(), _box->h(), _box->color() );
1620
inline void draw_T( int x, int y, int w, int h )
1624
fl_line( x+w2, y, x+w2, y+h );
1625
fl_line( x+w2, y+h2, x+w, y+h2 );
1628
inline void draw_L( int x, int y, int w, int h )
1632
fl_line( x+w2, y, x+w2, y+h2 );
1633
fl_line( x+w2, y+h2, x+w, y+h2 );
1636
inline void draw_Lflip( int x, int y, int w, int h )
1640
fl_line( x+w2, y+h, x+w2, y+h2 );
1641
fl_line( x+w2, y+h2, x, y+h2 );
1644
inline void draw_Lflop( int x, int y, int w, int h )
1648
fl_line( x+w2, y+h, x+w2, y+h2 );
1649
fl_line( x+w2, y+h2, x+w, y+h2 );
1652
inline void draw_Ldash( int x, int y, int w, int h )
1656
fl_line( x, y+h, x+w, y+h );
1659
inline void draw_vert_dash( int x, int y, int w, int h )
1662
fl_line( x+w, y+(h>>1), x+w, y+h );
1665
inline void draw_Rdash( int x, int y, int w, int h )
1668
fl_line( x+w, y+h, x+(w>>1), y+h );
1671
void Flu_Tree_Browser :: Node :: draw( RData &rdata, bool measure )
1673
int which = open(); // i.e. which icon: open or closed?
1674
bool skipCollapser = is_root() && rdata.showRoot && ( CHECK(ALWAYS_OPEN) || rdata.allBranchesAlwaysOpen );
1675
int halfHGap = rdata.hGap >> 1, halfVGap = rdata.vGap >> 1;
1676
bool doDraw = !measure;
1681
Fl_Color bgColor = rdata.shadedColors[rdata.shadedIndex], tColor = textColor, hilightColor = rdata.selectionColor;
1683
// pick the text color depending on the active state
1684
if( !rdata.tree->active() || !CHECK(ACTIVE))
1685
tColor = fl_inactive( tColor );
1689
// draw the background for the entry using the entry background color
1690
fl_draw_box( FL_FLAT_BOX, rdata.browserX, Y, rdata.browserW, currentH, bgColor );
1692
// if dragging to the inside of a branch, hilight that branch
1693
if( CHECK(SELECTED) )
1695
bgColor = rdata.selectionColor;
1696
tColor = fl_contrast( tColor, bgColor );
1697
hilightColor = rdata.bgColor;
1698
fl_draw_box( FL_FLAT_BOX, rdata.browserX, Y, rdata.browserW, currentH, bgColor );
1701
fl_color( rdata.lineColor );
1702
fl_line_style( rdata.lineStyle, rdata.lineWidth );
1705
if( is_leaf() ) // draw leaves one way...
1707
// draw the connectors
1708
if( doDraw && rdata.showConnectors && rdata.showBranches )
1710
if( parent()->is_root() && !rdata.showRoot && rdata.first )
1713
draw_Rdash( X-halfHGap, Y-halfVGap, rdata.branchIconW+rdata.hGap, currentH+rdata.vGap );
1715
draw_Lflop( X-halfHGap, Y-halfVGap, rdata.branchIconW+rdata.hGap, currentH+rdata.vGap );
1717
else if( rdata.last )
1718
draw_L( X-halfHGap, Y-halfVGap, rdata.branchIconW+rdata.hGap, currentH+rdata.vGap );
1720
draw_T( X-halfHGap, Y-halfVGap, rdata.branchIconW+rdata.hGap, currentH+rdata.vGap );
1723
// account for leaf icon spacing
1724
if( rdata.showBranches )
1727
X += rdata.collapseIcons[which]->w() + rdata.hGap;
1729
X += rdata.collapseIcons[which]->w() + rdata.wGap;
1734
// draw some more connectors
1735
if( doDraw && rdata.showConnectors && lIcon && rdata.showBranches )
1736
draw_Ldash( X-halfHGap, Y-halfVGap, lIcon->w()+rdata.hGap, currentH+rdata.vGap );
1738
// draw the leaf icon
1739
if( lIcon && !CHECK(ICON_AT_END) )
1742
lIcon->draw( X, Y+(currentH>>1)-(lIcon->h()>>1) );
1743
X += lIcon->w() + rdata.wGap;
1746
else // ...and branches another
1748
// force the root to the left if it has no visible children
1749
if( is_root() && !CHECK(SOME_VISIBLE_CHILDREN) )
1751
skipCollapser = true;
1755
if( !CHECK(SOME_VISIBLE_CHILDREN) && !rdata.showLeaves )
1758
// draw the connectors
1759
if( doDraw && !skipCollapser && rdata.showConnectors && rdata.showBranches )
1763
if( CHECK(SOME_VISIBLE_CHILDREN) )
1764
draw_Rdash( X-halfHGap, Y-halfVGap, rdata.collapseIcons[which]->w()+4+rdata.hGap, currentH+rdata.vGap );
1766
else if( parent()->is_root() && !rdata.showRoot && rdata.first )
1769
draw_Rdash( X-halfHGap, Y-halfVGap, rdata.branchIconW+rdata.hGap, currentH+rdata.vGap );
1771
draw_Lflop( X-halfHGap, Y-halfVGap, rdata.branchIconW+rdata.hGap, currentH+rdata.vGap );
1773
else if( rdata.last )
1774
draw_L( X-halfHGap, Y-halfVGap, rdata.branchIconW+rdata.hGap, currentH+rdata.vGap );
1776
draw_T( X-halfHGap, Y-halfVGap, rdata.branchIconW+rdata.hGap, currentH+rdata.vGap );
1779
// draw the collapsed icons
1780
if( doDraw && !skipCollapser && !CHECK(ALWAYS_OPEN) && !rdata.allBranchesAlwaysOpen )
1782
if( CHECK(SOME_VISIBLE_CHILDREN) || rdata.showLeaves )
1784
if( !rdata.openWOChildren && !CHECK(SOME_VISIBLE_CHILDREN) )
1786
if( rdata.openWOChildren || CHECK(SOME_VISIBLE_CHILDREN) )
1789
cIcon[which]->draw( X, Y+(currentH>>1)-(cIcon[which]->h()>>1) );
1791
cIcon[which]->draw( X+(rdata.branchIconW>>1)-(cIcon[which]->w()>>1), Y+(currentH>>1)-(cIcon[which]->h()>>1) );
1796
if( !skipCollapser )
1798
X += cIcon[which]->w();
1805
// draw some more connectors
1806
if( doDraw && rdata.showConnectors && rdata.showBranches )
1808
int hGap = rdata.hGap;
1810
hGap += bIcon[which]->w();
1811
if( skipCollapser && CHECK(SOME_VISIBLE_CHILDREN) )
1812
draw_vert_dash( X-halfHGap, Y-halfVGap, hGap, currentH+rdata.vGap );
1813
else if( !which || !CHECK(SOME_VISIBLE_CHILDREN) )
1814
draw_Ldash( X-halfHGap, Y-halfVGap, hGap, currentH+rdata.vGap );
1816
draw_Lflip( X-halfHGap, Y-halfVGap, hGap, currentH+rdata.vGap );
1819
// draw the branch icon
1823
bIcon[which]->draw( X, Y+(currentH>>1)-(bIcon[which]->h()>>1) );
1824
X += bIcon[which]->w() + rdata.wGap;
1834
if( CHECK(SHOW_LABEL) && !CHECK(SWAP_LABEL_AND_WIDGET) )
1838
fl_draw_box( FL_FLAT_BOX, X, Y+(currentH>>1)-(textH>>1), textW, textH, bgColor );
1840
fl_font( textFont, textSize );
1841
fl_draw( text.c_str(), X, Y+(currentH>>1)-(textH>>1), textW, textH, FL_ALIGN_LEFT );
1848
int widgetW = _widget->w->w();
1849
int widgetH = _widget->w->h();
1852
if( CHECK(AUTO_COLOR) )
1853
_widget->w->color( bgColor );
1854
if( CHECK(AUTO_LABEL_COLOR) )
1855
_widget->w->labelcolor( tColor );
1856
if( CHECK(AUTO_LABEL) )
1857
_widget->w->label( text.c_str() );
1858
_widget->w->redraw();
1859
_widget->w->position( X, Y+(currentH>>1)-(widgetH>>1) );
1860
if( CHECK(EXPAND_TO_WIDTH) )
1861
_widget->w->size( MAX( _widget->defaultW, rdata.browserW - (X-rdata.browserX) ), _widget->w->h() );
1864
if( CHECK(EXPAND_TO_WIDTH) )
1866
if( _widget->w->w() == _widget->defaultW )
1867
X += _widget->defaultW;
1873
if( CHECK(SHOW_LABEL) && CHECK(SWAP_LABEL_AND_WIDGET) )
1877
fl_draw_box( FL_FLAT_BOX, X, Y+(currentH>>1)-(textH>>1), textW, textH, bgColor );
1879
fl_font( textFont, textSize );
1880
fl_draw( text.c_str(), X, Y+(currentH>>1)-(textH>>1), textW, textH, FL_ALIGN_LEFT );
1885
// draw the leaf icon to the right of the label and widget
1886
if( is_leaf() && lIcon && CHECK(ICON_AT_END) )
1889
lIcon->draw( X, Y+(currentH>>1)-(lIcon->h()>>1) );
1890
X += lIcon->w() + rdata.wGap;
1893
// if hilighted, draw a box outlining the entry
1894
if( Fl::focus() == tree && rdata.hilighted == this && doDraw )
1896
fl_color( hilightColor );
1897
fl_line_style( FL_DOT, 1 );
1898
fl_rect( rdata.browserX, Y, rdata.browserW, currentH, hilightColor );
1902
rdata.totalW = MAX( rdata.totalW, X );
1905
void Flu_Tree_Browser :: Node :: select( bool b )
1907
if( (CHECK(SELECTED)==b) && (tree->when() != FL_WHEN_NOT_CHANGED) )
1911
if( tree->when() == FL_WHEN_RELEASE )
1914
do_callback( FLU_SELECTED );
1916
do_callback( FLU_UNSELECTED );
1919
void Flu_Tree_Browser :: Node :: open( bool b )
1924
if( CHECK(ALWAYS_OPEN) || tree->rdata.allBranchesAlwaysOpen )
1927
if( (open() == b) && (tree->when() != FL_WHEN_NOT_CHANGED) )
1930
tree->rdata.justOpenedClosed = true;
1934
if( open() && (_parent != 0) ) // root node doesn't count as a single open branch
1936
if( ( tree->rdata.lastOpenBranch != this ) && tree->rdata.singleBranchOpen )
1937
tree->rdata.lastOpenBranch->close();
1938
tree->rdata.lastOpenBranch = this;
1941
tree->rdata.forceResize = true;
1942
tree->rdata.visibilityChanged = true;
1944
do_callback( FLU_OPENED );
1946
do_callback( FLU_CLOSED );
1949
void Flu_Tree_Browser :: Node :: active( bool b )
1951
if( CHECK(ACTIVE) == b && tree->when() != FL_WHEN_NOT_CHANGED )
1957
_widget->w->activate();
1959
_widget->w->deactivate();
1961
if( !CHECK(ACTIVE) )
1963
if( tree->rdata.hilighted == this )
1964
tree->set_hilighted( NULL );
1970
void Flu_Tree_Browser :: Node :: unselect_all( Node* except )
1972
if( this != except )
1974
for( int i = 0; i < _children.size(); i++ )
1975
_children.child(i)->unselect_all( except );
1978
void Flu_Tree_Browser :: Node :: select_all()
1981
for( int i = 0; i < _children.size(); i++ )
1982
_children.child(i)->select_all();
1985
bool Flu_Tree_Browser :: Node :: isMoveValid( Node* &n1, int &where, Node* &n2 )
1987
// if n1 is NULL, then check it as if it were a node being moved from another tree
1992
// check the validity of the move:
1993
// 1) the source and destination nodes can't be the same
1994
// 2) you can't move before the root node
1995
// 3) you can't move an unmovable node or move a branch node such that it would become a descendent of itself
1996
// 4) if moving only within the same group, check that the parents are the same
1997
// 5) if moving into a sorted tree, the destination node MUST be a branch
1998
// 6) a move AFTER an OPEN branch is a move BEFORE its first child
1999
// 7) you can't move a node into a non-droppable branch node
2004
if( where==MOVE_BEFORE && n2->is_root() )
2009
if( !n1->movable() )
2011
if( n1->is_branch() )
2012
if( n1->is_descendent( n2 ) )
2016
bool sameGroup = n2->tree->move_only_same_group();
2017
if( sameGroup && n1 )
2019
if( n1->parent() != n2->parent() || where==MOVE_INSIDE )
2023
int iMode = n2->tree->insertion_mode();
2024
if( iMode == FLU_INSERT_SORTED || iMode == FLU_INSERT_SORTED_REVERSE )
2026
if( n2->is_branch() )
2028
where = MOVE_INSIDE;
2035
if( where==MOVE_AFTER && n2->is_branch() && n2->open() )
2037
// can't move inside a branch if within the same group, unless the first node is dragged
2038
// from outside the tree (in which case n1 is NULL)
2039
if( sameGroup && n1 )
2041
if( n2->_children.size() > 0 )
2044
else if( n2->_children.size() > 0 )
2046
where = MOVE_BEFORE;
2047
n2 = n2->_children.child(0);
2050
where = MOVE_INSIDE;
2053
if( where==MOVE_INSIDE )
2055
if( !n2->droppable() )
2058
else if( n2->parent() )
2059
if( !n2->parent()->droppable() )
2065
int Flu_Tree_Browser :: Node :: recurse( RData &rdata, int type, int event )
2072
if( type == COUNT_SELECTED )
2075
return (int)CHECK(SELECTED);
2078
int total = (int)CHECK(SELECTED);
2079
for( i = 0; i < _children.size(); i++ )
2080
total += _children.child(i)->recurse( rdata, type, event );
2085
// see if this entry is even visible
2086
if( rdata.y > rdata.browserY+rdata.browserH )
2090
else if( type == HANDLE )
2095
bool skipEntry = ( is_root() && !rdata.showRoot ) || ( is_leaf() && !rdata.showLeaves ) || ( is_branch() && !rdata.showBranches );
2096
bool skipCollapser = is_root() && rdata.showRoot && ( CHECK(ALWAYS_OPEN) || rdata.allBranchesAlwaysOpen );
2098
// find the size of the entry label
2099
if( (type == MEASURE) || (type == MEASURE_THIS_OPEN) )
2101
if( CHECK(SHOW_LABEL) )
2104
fl_font( textFont, textSize );
2105
fl_measure( text.c_str(), W, H );
2106
W += 4; H += 4; // hack - it looks better
2115
// remember vertically where this node is w.r.t the browser
2120
// find the total size of the entry, depending on if there's a widget
2122
currentH = MAX( _widget->w->h(), currentH );
2124
// find the total height of this entry by taking the max height of the entry and icons
2128
currentH = MAX( currentH, lIcon->h() );
2132
currentH = MAX( currentH, cIcon[which]->h() );
2134
currentH = MAX( currentH, bIcon[which]->h() );
2138
bool skipAhead = (rdata.y + currentH) < rdata.browserY;
2140
// process the entry
2145
if( skipEntry || skipAhead ) break;
2147
draw( rdata, false );
2149
// draw any vertical connectors connecting our parents, grandparents, etc.,
2150
if( rdata.showBranches )
2153
for( i = 0; i < rdata.branchConnectors.size(); i++ )
2157
fl_color( rdata.lineColor );
2158
fl_line_style( rdata.lineStyle, rdata.lineWidth );
2159
fl_line( rdata.branchConnectors[i], rdata.y, rdata.branchConnectors[i], rdata.y+currentH );
2165
rdata.shadedIndex = 1 - rdata.shadedIndex; // toggle the even/odd entry for shading
2171
CLEAR( SOME_VISIBLE_CHILDREN );
2174
// find out whether the branch has any children that could be visible
2175
bool someVisibleChildren = rdata.showLeaves && ( _children.size() > 0 );
2176
for( i = 0; i < _children.size(); i++ )
2178
if( _children.child(i)->is_branch() )
2180
someVisibleChildren = true;
2184
SET( SOME_VISIBLE_CHILDREN, someVisibleChildren );
2187
case MEASURE_THIS_OPEN:
2188
if( skipEntry ) break;
2189
draw( rdata, true );
2194
if( skipEntry || skipAhead || !CHECK(ACTIVE) ) break;
2196
if( event != FL_DRAG && event != FL_NO_EVENT )
2197
rdata.justOpenedClosed = false;
2199
// if we are trying to select all entries between 2 widgets due to a shift-select...
2200
if( rdata.shiftSelect )
2202
if( (rdata.hilighted == this) || (rdata.grabbed == this) )
2204
if( !rdata.shiftSelectAll )
2206
rdata.shiftSelectAll = true;
2208
if( is_branch() && rdata.openOnSelect )
2215
rdata.shiftSelect = false;
2216
rdata.shiftSelectAll = false;
2219
if( is_branch() && rdata.openOnSelect )
2225
else if( rdata.shiftSelectAll )
2228
if( is_branch() && rdata.openOnSelect )
2236
// check for the keyboard event
2237
if( event == FL_KEYDOWN )
2239
// check for the spacebar selecting this entry
2240
if( Fl::event_key() == ' ' && rdata.hilighted == this )
2242
if( Fl::event_state(FL_CTRL) )
2243
select( !CHECK(SELECTED) );
2246
rdata.root->unselect_all( this );
2249
if( is_branch() && rdata.openOnSelect )
2256
// check for the enter key opening/closing this entry
2257
else if( (Fl::event_key() == FL_Enter) && (rdata.hilighted == this) )
2263
// check for the left/right cursor keys opening/closing this entry
2264
else if( (Fl::event_key() == FL_Left) && (rdata.hilighted == this) )
2269
else if( (Fl::event_key() == FL_Right) && (rdata.hilighted == this) )
2276
// check for the "up" cursor key moving the hilighted entry
2277
if( rdata.delta == -1 && rdata.hilighted == this && rdata.previous != NULL )
2279
tree->set_hilighted( rdata.previous );
2284
// check for the "down" cursor key moving the hilighted entry
2285
if( rdata.delta == 1 && rdata.hilighted == rdata.previous )
2287
tree->set_hilighted( this );
2292
rdata.previous = this;
2294
// the event is not ours to use
2295
//if( _widget && !rdata.dragging )
2296
//if( Fl::event_inside( _widget->w ) )
2299
bool inExpander = false;
2304
inExpander = Fl::event_inside( rdata.x, rdata.y+(currentH>>1)-(cIcon[which]->h()>>1),
2305
cIcon[which]->w(), cIcon[which]->h() );
2307
inExpander = Fl::event_inside( rdata.x+(rdata.branchIconW>>1)-(cIcon[which]->w()>>1),
2308
rdata.y+(currentH>>1)-(cIcon[which]->h()>>1),
2309
cIcon[which]->w(), cIcon[which]->h() );
2312
if( event == FL_PUSH )
2314
// check for expand/collapse
2315
if( Fl::event_button() == FL_LEFT_MOUSE && inExpander )
2317
if( rdata.openWOChildren || CHECK(SOME_VISIBLE_CHILDREN) )
2320
rdata.dragging = false;
2327
if( event == FL_DRAG && rdata.justOpenedClosed )
2330
// if no selections, return
2331
if( rdata.selectionMode == FLU_NO_SELECT )
2334
// if the event is not inside us, return
2335
if( !Fl::event_inside( rdata.browserX, rdata.y, rdata.browserW, currentH ) )
2339
if( rdata.selectionMode == FLU_SINGLE_SELECT )
2341
if( event == FL_MOVE && rdata.selectUnderMouse )
2344
rdata.root->unselect_all( this );
2348
else if( event == FL_PUSH )
2350
//rdata.dragging = true;
2351
rdata.grabbed = this;
2353
if( rdata.selectUnderMouse )
2354
rdata.root->unselect_all();
2356
rdata.root->unselect_all( this );
2357
tree->set_hilighted( this );
2358
if( Fl::event_state(FL_CTRL) )
2359
select( !CHECK(SELECTED) );
2365
if( Fl::event_clicks() > 0 )
2367
Fl::event_clicks(0);
2368
do_callback( FLU_DOUBLE_CLICK );
2373
if( Fl::event_clicks() > 0 )
2375
Fl::event_clicks(0);
2376
if( rdata.doubleClickToOpen )
2378
if( rdata.openWOChildren || CHECK(SOME_VISIBLE_CHILDREN) )
2382
do_callback( FLU_DOUBLE_CLICK );
2384
else if( rdata.openOnSelect )
2392
else if( event == FL_DRAG )
2394
if( rdata.selectionDragMode == FLU_DRAG_IGNORE )
2396
rdata.dragging = true;
2397
//if( ( rdata.selectionDragMode == FLU_DRAG_IGNORE || rdata.selectionDragMode == FLU_DRAG_TO_MOVE) && ( tree->insertion_mode() == FLU_INSERT_FRONT || tree->insertion_mode() == FLU_INSERT_BACK ) )
2399
rdata.root->unselect_all( this );
2400
tree->set_hilighted( this );
2404
else if( event == FL_RELEASE && tree->when() == FL_WHEN_RELEASE && selected() && !inExpander )
2406
do_callback( FLU_SELECTED );
2411
// multiple selection
2412
else if( rdata.selectionMode == FLU_MULTI_SELECT )
2414
if( event == FL_PUSH )
2416
//rdata.dragging = true;
2417
rdata.grabbed = this;
2419
if( Fl::event_state(FL_CTRL) )
2421
select( !CHECK(SELECTED) );
2422
tree->set_hilighted( this );
2424
else if( Fl::event_state(FL_SHIFT) )
2426
// select everything from the last selected entry to this one
2427
if( rdata.hilighted == this )
2432
if( Fl::event_clicks() > 0 )
2434
Fl::event_clicks(0);
2435
if( rdata.doubleClickToOpen )
2437
if( rdata.openWOChildren || CHECK(SOME_VISIBLE_CHILDREN) )
2441
do_callback( FLU_DOUBLE_CLICK );
2443
else if( rdata.openOnSelect )
2451
rdata.shiftSelectAll = false;
2452
rdata.shiftSelect = true;
2453
rdata.grabbed = this;
2454
rdata.root->recurse( rdata, HANDLE, 0 );
2455
tree->set_hilighted( this );
2460
rdata.root->unselect_all( this );
2464
if( Fl::event_clicks() > 0 )
2466
Fl::event_clicks(0);
2467
do_callback( FLU_DOUBLE_CLICK );
2472
if( Fl::event_clicks() > 0 )
2474
Fl::event_clicks(0);
2475
if( rdata.doubleClickToOpen )
2477
if( rdata.openWOChildren || CHECK(SOME_VISIBLE_CHILDREN) )
2481
do_callback( FLU_DOUBLE_CLICK );
2483
else if( rdata.openOnSelect )
2488
tree->set_hilighted( this );
2493
else if( event == FL_DRAG )
2495
if( rdata.selectionDragMode == FLU_DRAG_IGNORE )
2497
rdata.dragging = true;
2498
//if( ( rdata.selectionDragMode == FLU_DRAG_IGNORE || rdata.selectionDragMode == FLU_DRAG_TO_MOVE) && ( tree->insertion_mode() == FLU_INSERT_FRONT || tree->insertion_mode() == FLU_INSERT_BACK ) )
2501
tree->set_hilighted( this );
2504
else if( event == FL_RELEASE && tree->when() == FL_WHEN_RELEASE && selected() && !inExpander )
2506
do_callback( FLU_SELECTED );
2514
// advance the counters vertically to the next entry
2516
rdata.y += currentH + rdata.vGap;
2518
if( !is_root() && rdata.first && !skipEntry )
2519
rdata.first = false;
2521
// if we're a leaf, no need to process further
2525
// should we bail out already if we're done processing?
2526
if( closed() && !skipEntry && !skipCollapser && ( type != MEASURE_THIS_OPEN ) )
2529
if( !CHECK(SOME_VISIBLE_CHILDREN) )
2532
// advance the counters horizontally to the next entry
2533
if( rdata.showBranches )
2535
if( !skipEntry && !skipCollapser )
2536
rdata.x += cIcon[which]->w() + rdata.hGap;
2538
rdata.totalW = MAX( rdata.totalW, rdata.x );
2540
// the branchIconW is the width of the branch icon at this level
2541
// it is used to center all children icons under the branch icon
2542
int lastBranchIconW = rdata.branchIconW;
2543
if( rdata.showBranches )
2546
rdata.branchIconW = bIcon[which]->w();
2548
rdata.branchIconW = cIcon[which]->w();
2551
rdata.branchIconW = 0;
2553
// process all children
2555
int tempW = rdata.branchIconW >> 1;
2556
for( i = 0; i < _children.size(); i++ )
2558
// prepare the recursive data structure for the next level
2561
rdata.last = (i == _children.size()-1 );
2563
// if child "i" is not the last child,
2564
// then there is a long connector that needs drawn between this node and the last child.
2565
// push the horizontal position of the connector onto the stack
2566
if( (type == DRAW) && rdata.showConnectors && ( i < _children.size()-1 ) )
2568
rdata.branchConnectors.push_back( rdata.x+tempW );
2569
val = _children.child(i)->recurse( rdata, type, event );
2570
rdata.branchConnectors.pop_back();
2573
val = _children.child(i)->recurse( rdata, type, event );
2579
// set the branch icon width back to what it was before we changed it
2580
rdata.branchIconW = lastBranchIconW;
2582
// move back horizontally from the last entry
2583
if( rdata.showBranches )
2585
if( !skipEntry && !skipCollapser )
2586
rdata.x -= cIcon[which]->w() + rdata.hGap;
2592
void Flu_Tree_Browser :: print()
2597
void Flu_Tree_Browser :: clear()
2602
while( _box->children() )
2603
_box->remove( *_box->child(0) );
2605
rdata.cbNode = NULL;
2606
rdata.cbReason = FLU_NOTHING;
2607
rdata.hilighted = NULL;
2608
rdata.dragging = false;
2609
rdata.forceResize = true;
2610
rdata.lastOpenBranch = NULL;
2611
rdata.shiftSelect = false;
2612
rdata.shiftSelectAll = false;
2614
rdata.searchIndex = 1;
2617
Flu_Tree_Browser::Node* Flu_Tree_Browser :: set_root( const char *label, Fl_Widget *w, bool showLabel )
2623
root.SET(Node::SHOW_LABEL,showLabel);
2624
root.cIcon[0] = rdata.collapseIcons[0];
2625
root.cIcon[1] = rdata.collapseIcons[1];
2626
root.bIcon[0] = rdata.branchIcons[0];
2627
root.bIcon[1] = rdata.branchIcons[1];
2628
root.textColor = rdata.defBranchColor;
2629
root.textFont = rdata.defBranchFont;
2630
root.textSize = rdata.defBranchSize;
2631
rdata.forceResize = true;
2636
Flu_Tree_Browser::Node* Flu_Tree_Browser :: add( const char* fullpath, Fl_Widget *w, bool showLabel )
2638
return( root.modify( fullpath, Node::ADD, rdata, w, showLabel ) );
2641
Flu_Tree_Browser::Node* Flu_Tree_Browser :: add( const char* path, const char* text, Fl_Widget *w, bool showLabel )
2643
// if the path does not end in '/', add it
2644
std::string s = path;
2645
if( path[strlen(path)-1] != '/' )
2649
return add( s.c_str(), w, showLabel );
2652
Flu_Tree_Browser::Node* Flu_Tree_Browser :: add_branch( const char* fullpath, Fl_Widget *w, bool showLabel )
2654
std::string p( fullpath );
2655
if( p.size() && p[p.size()-1] != '/' && p[p.size()-1] != '\\' ) p += "/";
2656
return add( p.c_str(), w, showLabel );
2659
Flu_Tree_Browser::Node* Flu_Tree_Browser :: add_branch( const char* path, const char* name, Fl_Widget *w, bool showLabel )
2661
std::string p( name );
2662
if( p.size() && p[p.size()-1] != '/' && p[p.size()-1] != '\\' ) p += "/";
2663
return add( path, p.c_str(), w, showLabel );
2666
Flu_Tree_Browser::Node* Flu_Tree_Browser :: add_leaf( const char* fullpath, Fl_Widget *w, bool showLabel )
2668
std::string p( fullpath );
2669
if( p.size() && ( p[p.size()-1] == '/' || p[p.size()-1] == '\\' ) ) p[p.size()-1] = '\0';
2670
return add( p.c_str(), w, showLabel );
2673
Flu_Tree_Browser::Node* Flu_Tree_Browser :: add_leaf( const char* path, const char* name, Fl_Widget *w, bool showLabel )
2675
std::string p( name );
2676
if( p.size() && ( p[p.size()-1] == '/' || p[p.size()-1] == '\\' ) ) p[p.size()-1] = '\0';
2677
return add( path, p.c_str(), w, showLabel );
2680
size_t Flu_Tree_Browser :: remove( const char *fullpath )
2682
return( (size_t)root.modify( fullpath, Node::REMOVE, rdata ) );
2685
size_t Flu_Tree_Browser :: remove( const char *path, const char *text )
2687
// if the path does not end in '/', add it
2688
std::string s = path;
2689
if( path[strlen(path)-1] != '/' )
2692
return remove( s.c_str() );
2695
size_t Flu_Tree_Browser :: remove( size_t id )
2697
return root.remove( id );
2700
size_t Flu_Tree_Browser :: Node :: remove( size_t id )
2705
for( int i = 0; i < _children.size(); i++ )
2707
Node *n = _children.child(i);
2710
_children.erase( i );
2711
tree->rdata.forceResize = true;
2712
//if( tree->rdata.cbNode == n )
2713
//tree->rdata.cbNode = NULL;
2715
if( tree->rdata.autoBranches )
2720
else if( n->remove( id ) )
2727
size_t Flu_Tree_Browser :: remove( Fl_Widget *w )
2729
return root.remove( w );
2732
size_t Flu_Tree_Browser :: Node :: remove( Fl_Widget *w )
2736
for( int i = 0; i < _children.size(); i++ )
2738
Node *n = _children.child(i);
2741
if( n->_widget->w == w )
2744
_children.erase( i );
2745
tree->rdata.forceResize = true;
2746
//if( tree->rdata.cbNode == n )
2747
//tree->rdata.cbNode = NULL;
2749
if( tree->rdata.autoBranches )
2756
int id = n->remove( w );
2764
int Flu_Tree_Browser :: find_number( const char *fullpath )
2767
root.modify( fullpath, Node::FIND_NUMBER, rdata );
2768
return rdata.counter;
2771
int Flu_Tree_Browser :: find_number( const char *path, const char *text )
2773
// if the path does not end in '/', add it
2774
std::string s = path;
2775
if( path[strlen(path)-1] != '/' )
2778
return find_number( s.c_str() );
2781
Flu_Tree_Browser::Node* Flu_Tree_Browser :: find_next( const char *fullpath, Node* startNode )
2783
// degenerate case: root node
2784
if( strcmp( fullpath, "/" ) == 0 )
2786
rdata.previous = startNode;
2787
return( root.modify( fullpath, Node::FIND, rdata ) );
2790
Flu_Tree_Browser::Node* Flu_Tree_Browser :: find_next( const char *path, const char *text )
2792
// if the path does not end in '/', add it
2793
std::string s = path;
2794
if( path[strlen(path)-1] != '/' )
2797
return find_next( s.c_str() );
2800
Flu_Tree_Browser::Node* Flu_Tree_Browser :: find( const char *path, const char *text )
2802
// if the path does not end in '/', add it
2803
std::string s = path;
2804
if( path[strlen(path)-1] != '/' )
2807
return find( s.c_str() );
2810
Flu_Tree_Browser::Node* Flu_Tree_Browser :: find( size_t id )
2812
return root.find( id );
2815
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: find( size_t id )
2823
for( int i = 0; i < _children.size(); i++ )
2825
Node *n = _children.child(i)->find( id );
2833
Flu_Tree_Browser::Node* Flu_Tree_Browser :: find( Fl_Widget *w )
2835
return root.find( w );
2838
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: find( Fl_Widget *w )
2841
if( _widget->w == w )
2844
for( int i = 0; i < _children.size(); i++ )
2846
Node *n = _children.child(i)->find( w );
2855
bool Flu_Tree_Browser :: Node :: findPath( size_t id, RData &rdata )
2872
std::string oldPath = rdata.path;
2879
for( int i = 0; i < _children.size(); i++ )
2881
if( _children.child(i)->findPath( id, rdata ) )
2887
rdata.path = oldPath;
2891
bool Flu_Tree_Browser :: Node :: findPath( Fl_Widget *w, RData &rdata )
2894
if( _widget->w == w )
2909
std::string oldPath = rdata.path;
2916
for( int i = 0; i < _children.size(); i++ )
2918
if( _children.child(i)->findPath( w, rdata ) )
2924
rdata.path = oldPath;
2929
const char* Flu_Tree_Browser :: find_path( size_t id )
2931
// degenerate case: the root is always id==0
2935
if( root.findPath( id, rdata ) )
2936
return rdata.path.c_str();
2941
const char* Flu_Tree_Browser :: find_path( Fl_Widget *w )
2944
if( root.findPath( w, rdata ) )
2945
return rdata.path.c_str();
2950
static std::string remove_escape_chars( const char *str )
2952
// remove any escape characters
2953
std::string text(str);
2955
for( int pIndex = 0; pIndex < (int)strlen( str ); pIndex++ )
2957
if( str[pIndex] != '\\' )
2958
text[tIndex++] = str[pIndex];
2960
text.resize(tIndex);
2964
void Flu_Tree_Browser :: Node :: do_callback( int reason )
2966
//if( tree->rdata.when == FL_WHEN_NEVER )
2967
if( tree->when() == FL_WHEN_NEVER )
2969
//if( tree->rdata.cb )
2971
tree->rdata.cbReason = reason;
2972
tree->rdata.cbNode = this;
2973
//tree->rdata.cb( tree, tree->rdata.cbd );
2974
((Fl_Widget*)tree)->do_callback();
2978
unsigned short Flu_Tree_Browser :: Node :: depth() const
2990
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: add_branch( const char* fullpath, Fl_Widget *w, bool showLabel )
2992
std::string p( fullpath );
2993
if( p.size() && p[p.size()-1] != '/' && p[p.size()-1] != '\\' ) p += "/";
2994
return add( p.c_str(), w, showLabel );
2997
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: add_leaf( const char* fullpath, Fl_Widget *w, bool showLabel )
2999
std::string p( fullpath );
3000
if( p.size() && ( p[p.size()-1] == '/' || p[p.size()-1] == '\\' ) ) p[p.size()-1] = '\0';
3001
return add( p.c_str(), w, showLabel );
3004
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: add( const char* path, const char* name, Fl_Widget *w, bool showLabel )
3006
std::string p( path );
3007
if( p.size() && p[p.size()-1] != '/' && p[p.size()-1] != '\\' ) p += "/";
3009
return add( p.c_str(), w, showLabel );
3012
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: add_branch( const char* path, const char* name, Fl_Widget *w, bool showLabel )
3014
std::string p( path );
3015
if( p.size() && p[p.size()-1] != '/' && p[p.size()-1] != '\\' ) p += "/";
3017
return add_branch( p.c_str(), w, showLabel );
3020
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: add_leaf( const char* path, const char* name, Fl_Widget *w, bool showLabel )
3022
std::string p( path );
3023
if( p.size() && p[p.size()-1] != '/' && p[p.size()-1] != '\\' ) p += "/";
3025
return add_leaf( p.c_str(), w, showLabel );
3028
Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: modify( const char* path, int what, RData &rdata, Fl_Widget *w, bool showLabel )
3030
// find the selected entry at rdata.searchIndex among all selected entries
3031
if( what == GET_SELECTED )
3033
if( CHECK(SELECTED) )
3036
if( rdata.counter == rdata.searchIndex )
3039
for( int i = 0; i < _children.size(); i++ )
3041
Node *n = _children.child(i)->modify( path, what, rdata, w );
3048
// trivial test for a bogus empty path
3052
// if the path starts with '/', skip the '/'
3053
if( path[0] == '/' )
3056
// trivial test for a bogus empty path
3057
if( path[0] == '\0' )
3060
const char *remainingPath;
3061
std::string nodeName;
3062
bool lastNode, branchNode;
3063
Node *retNode = NULL;
3065
///////////// extract the next node name from the path ///////////////////
3067
// find the next '/' that is not preceded by the escape character '\'
3068
const char *slash = strchr( path, '/' );
3071
// find the next '/'
3072
if( slash == NULL ) // there isn't one, so we're done
3074
// test for escape character
3075
else if( slash[-1] == '\\' ) // path[0] can never be '/', so this is a safe test
3076
slash = strchr( slash+1, '/' );
3082
// if there is no slash, then the node name is the path and it is a leaf and the last node in the path
3086
nodeName = remove_escape_chars(path); // remove the escape characters
3088
remainingPath = NULL;
3090
// otherwise the node name is the path up to the slash, it is also a branch and may not be the last node in the path
3095
nodeName.resize(slash-path);
3096
nodeName = remove_escape_chars(nodeName.c_str()); // remove the escape characters
3098
lastNode = ( slash[1] == '\0' ); // this is the last node if there is nothing after the slash
3101
//if( rdata.autoBranches )
3102
//branchNode = false;
3103
remainingPath = NULL;
3106
remainingPath = slash+1;
3109
///////////// process the node ///////////////////
3115
// if the new node is a leaf node, add the string as a leaf and return
3118
// is there already a node with this name?
3120
Node *n = _children.find( nodeName );
3123
// if that node is a branch node, we can't add a new one with the same name
3124
if( n->is_branch() )
3127
// if we are not allowed to add multiple nodes with the same name,
3129
if( !rdata.allowDuplication )
3135
retNode = new Node( true, nodeName.c_str(), this, rdata, w, showLabel );
3136
_children.add( retNode );
3137
rdata.forceResize = true;
3138
rdata.visibilityChanged = true;
3140
if( tree->rdata.autoBranches )
3143
// otherwise make sure the node name exists as a branch and recurse on it
3146
// if there is already a node with this name, just use it
3149
n = _children.find( nodeName );
3152
// make sure it is a branch
3158
// else add a new node
3161
// only add the widget for the last node
3162
n = new Node( false, nodeName.c_str(), this, rdata, lastNode?w:NULL, lastNode?showLabel:true );
3164
rdata.forceResize = true;
3165
rdata.visibilityChanged = true;
3168
if( tree->rdata.autoBranches )
3171
// recurse on the remainder of the path, if not the last node
3175
retNode = n->modify( remainingPath, what, rdata, w, showLabel );
3182
// try to find the indicated node. if we can't find it, just return
3183
Node *n = _children.find( nodeName.c_str() );
3187
// if this is the last node, remove it.
3191
_children.erase( n );
3192
//if( tree->rdata.cbNode == n )
3193
//tree->rdata.cbNode = NULL;
3195
retNode = (Node*)ID; // non-null return value means remove was successful
3196
rdata.forceResize = true;
3197
rdata.visibilityChanged = true;
3199
if( tree->rdata.autoBranches )
3203
// otherwise recurse on the remainder of the path
3205
retNode = n->modify( remainingPath, what, rdata, w, showLabel );
3211
// if this node equals the starting node for a find_next,
3212
// then by clearing rdata.previous we flag that we are allowed to return the next match
3213
if( rdata.previous == this )
3214
rdata.previous = NULL;
3220
// if, according to the path, this is not the last node, then just recursively
3221
// search for the named node
3222
n = _children.find( nodeName.c_str() );
3225
retNode = n->modify( remainingPath, what, rdata, w, showLabel );
3229
// otherwise, according to the path, this is the last node (i.e. a leaf).
3230
// since only leaves can have multiple identical entries,
3231
// try to find the indicated node, accounting for the possibility
3232
// that it may not be the one we're after
3236
// look for the named node
3237
n = _children.find( nodeName.c_str(), next++ );
3239
// if we can't find it, just return, because it's not here
3243
// we are only allowed to return a match if the previous node is NULL,
3244
// indicating we have passed the starting node for a find_next
3245
if( rdata.previous == NULL )
3251
// if the found node equals the starting node for a find_next,
3252
// then by clearing rdata.previous we flag that we are allowed to return the next match
3253
if( rdata.previous == n )
3254
rdata.previous = NULL;
3262
if( lastNode ) // can only match multiple leaves if the path says this is the last node
3264
rdata.counter += _children.findNum( nodeName.c_str() );
3266
else // otherwise recurse down the remaining path
3268
Node *n = _children.find( nodeName.c_str() );
3269
n->modify( remainingPath, what, rdata, w, showLabel );
3278
void Flu_Tree_Browser :: Node :: widgetCB()
3283
_widget->CB( _widget->w, _widget->CBData );
3285
do_callback( FLU_WIDGET_CALLBACK );
3288
void Flu_Tree_Browser :: Node :: widget( Fl_Widget *w )
3290
tree->rdata.forceResize = true;
3294
Fl_Group *p = _widget->w->parent();
3296
p->remove( *(_widget->w) );
3305
_widget = new WidgetInfo;
3307
_widget->defaultW = _widget->w->w();
3308
if( USE_FLU_WIDGET_CALLBACK )
3310
_widget->CB = _widget->w->callback();
3311
_widget->CBData = _widget->w->user_data();
3312
_widget->w->callback( _widgetCB, this );
3316
Fl_Group *p = w->parent();
3322
tree->_box->add( w );
3328
p->_group = new Fl_Group( tree->_box->x(), tree->_box->y(), tree->_box->w(), tree->_box->h() );
3330
tree->_box->add( p->_group );
3332
p->_group->add( w );
3336
void Flu_Tree_Browser :: Node :: branch_icons( Fl_Image *closed, Fl_Image *open )
3342
tree->rdata.forceResize = true;
3346
void Flu_Tree_Browser :: Node :: collapse_icons( Fl_Image *closed, Fl_Image *open )
3350
if( !closed || !open )
3352
cIcon[0] = tree->rdata.defaultCollapseIcons[0];
3353
cIcon[1] = tree->rdata.defaultCollapseIcons[1];
3360
tree->rdata.forceResize = true;
3364
void Flu_Tree_Browser :: Node :: leaf_icon( Fl_Image *icon )
3369
tree->rdata.forceResize = true;
3373
bool Flu_Tree_Browser :: Node :: is_branch() const
3375
if( tree->rdata.autoBranches )
3376
return( _children.size() != 0 );
3378
return !CHECK(LEAF);
3381
bool Flu_Tree_Browser :: Node :: is_leaf() const
3383
if( tree->rdata.autoBranches )
3384
return( _children.size() == 0 && !is_root() );
3390
#if 0 // CG test code: set to 1 and run "fltk-config --compile Flu_Tree_Browser.cpp"
3393
#include <FL/Fl_Window.H>
3394
#include <FL/Fl_Box.H>
3395
#include <FL/Fl_Input.H>
3396
#include <FL/Fl_Choice.H>
3397
#include <FL/Fl_Check_Button.H>
3398
#include "Flu_Tree_Browser.h"
3400
void add_points(Flu_Tree_Browser::Node *n)
3403
for(int i = 0; i < 2; i++){
3404
sprintf(str, "Point %d", i);
3409
void add_lines(Flu_Tree_Browser::Node *n)
3412
for(int i = 0; i < 10; i++){
3413
sprintf(str, "Line %d/", i);
3414
Flu_Tree_Browser::Node *n2 = n->add(str);
3419
void add_surfaces(Flu_Tree_Browser::Node *n)
3422
for(int i = 0; i < 100; i++){
3423
sprintf(str, "Surface %d/", i);
3424
Flu_Tree_Browser::Node *n2 = n->add(str);
3429
void add_volumes(Flu_Tree_Browser::Node *n)
3432
for(int i = 0; i < 1000; i++){
3433
sprintf(str, "Volume %d/", i);
3434
Flu_Tree_Browser::Node *n2 = n->add(str);
3439
int main(int argc, char **argv)
3441
printf("sizeof Node = %d\n", sizeof(Flu_Tree_Browser :: Node));
3443
Fl_Window *window = new Fl_Window(300, 600);
3444
Flu_Tree_Browser *tree = new Flu_Tree_Browser(0, 0, 300, 600);
3445
tree->show_root(false);
3446
tree->insertion_mode(FLU_INSERT_BACK);
3447
tree->branch_icons(0, 0);
3450
for(int i = 0; i < 2; i++){
3451
sprintf(str, "Model %d <<DLR_F6>>/", i);
3452
Fl_Check_Button *b = new Fl_Check_Button(0, 0, 20, 20);
3453
b->tooltip("Set active");
3454
Flu_Tree_Browser::Node *n = tree->add(str, b);
3455
Flu_Tree_Browser::Node *e = n->add("Elementary entities/");
3460
Flu_Tree_Browser::Node *g = n->add("Physical groups/");
3461
Flu_Tree_Browser::Node *p = n->add("Mesh partitions/");
3463
Flu_Tree_Browser::Node *p = tree->add("Post-processing views/");
3464
for(int i = 0; i < 7; i++){
3465
sprintf(str, "View %d", i);
3469
window->resizable(tree);
3470
window->show(argc, argv);