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

生物技术

开发平台:

C/C++

  1. /*
  2.  * ===========================================================================
  3.  * PRODUCTION $Log: Flu_Tree_Browser.cpp,v $
  4.  * PRODUCTION Revision 1000.1  2004/06/01 21:06:08  gouriano
  5.  * PRODUCTION PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.6
  6.  * PRODUCTION
  7.  * ===========================================================================
  8.  */
  9. /*
  10.  * These files were imported into NCBI's CVS directly from FLU version 2.9.1.
  11.  * Modifications to the source are listed below.
  12.  *
  13.  * ==========================================================================
  14.  * $Log: Flu_Tree_Browser.cpp,v $
  15.  * Revision 1000.1  2004/06/01 21:06:08  gouriano
  16.  * PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.6
  17.  *
  18.  * Revision 1.6  2004/05/21 22:27:51  gorelenk
  19.  * Added PCH ncbi_pch.hpp
  20.  *
  21.  * Revision 1.5  2004/03/11 14:10:10  dicuccio
  22.  * Fixes for 64-bit compilation.  Cast away const from strchr() where needed
  23.  *
  24.  * Revision 1.4  2004/03/11 13:51:40  dicuccio
  25.  * Imported FLU version 2.9.1.  Altered export specifiers to match NCBI layout.
  26.  * Altered include paths to match NCBI toolkit layout.
  27.  *
  28.  * ==========================================================================
  29.  */
  30. // $Id: Flu_Tree_Browser.cpp,v 1000.1 2004/06/01 21:06:08 gouriano Exp $
  31. /***************************************************************
  32.  *                FLU - FLTK Utility Widgets 
  33.  *  Copyright (C) 2002 Ohio Supercomputer Center, Ohio State University
  34.  *
  35.  * This file and its content is protected by a software license.
  36.  * You should have received a copy of this license with this file.
  37.  * If not, please contact the Ohio Supercomputer Center immediately:
  38.  * Attn: Jason Bryan Re: FLU 1224 Kinnear Rd, Columbus, Ohio 43212
  39.  * 
  40.  ***************************************************************/
  41. #include <ncbi_pch.hpp>
  42. #include <FL/Fl.H>
  43. #include <FL/fl_draw.H>
  44. #include <FL/math.h>
  45. #include <stdlib.h>
  46. #include <gui/widgets/FLU/Flu_Tree_Browser.h>
  47. #include <gui/widgets/FLU/flu_pixmaps.h>
  48. #define MAX( x, y ) ( (x)>(y) ? (x) : (y) )
  49. #define MIN( x, y ) ( (x)<(y) ? (x) : (y) )
  50. #define ABS( x ) ( (x)>0 ? (x) : -(x) )
  51. #define LERP( t, x0, x1 ) ( (x0) + (t)*( (x1) - (x0) ) )
  52. #ifdef USE_FLU_DND
  53. Flu_Tree_Browser :: DND_Object :: DND_Object() : Flu_DND( "DND_Object" )
  54. {
  55. }
  56. #endif
  57. Flu_Tree_Browser :: IntStack :: IntStack()
  58. {
  59.   _list = NULL;
  60.   _size = _bufferSize = 0;
  61. }
  62. Flu_Tree_Browser :: IntStack :: IntStack( const Flu_Tree_Browser::IntStack& s )
  63. {
  64.   _list = NULL;
  65.   _size = _bufferSize = 0;
  66.   *this = s;
  67. }
  68. Flu_Tree_Browser :: IntStack :: ~IntStack()
  69. {
  70.   clear();
  71. }
  72. Flu_Tree_Browser::IntStack& Flu_Tree_Browser :: IntStack :: operator =( const Flu_Tree_Browser::IntStack& s )
  73. {
  74.   clear();
  75.   if( s._size )
  76.     {
  77.       _list = (int*)malloc( s._size*sizeof(int) );
  78.       memcpy( _list, s._list, s._size*sizeof(int) );
  79.       _size = _bufferSize = s._size;
  80.     }
  81.   return *this;
  82. }
  83. void Flu_Tree_Browser :: IntStack :: push( int i )
  84. {
  85.   if( _size == _bufferSize )
  86.     {
  87.       // allocate a new list
  88.       _bufferSize += 4;
  89.       int *newList = (int*)malloc( _bufferSize*sizeof(int) );
  90.       // copy the old list
  91.       if( _size > 0 )
  92. memcpy( newList, _list, _size*sizeof(int) );
  93.       if( _list )
  94. free( _list );
  95.       _list = newList;
  96.     }
  97.   // add the new item
  98.   _list[_size] = i;
  99.   _size++;
  100. }
  101. int Flu_Tree_Browser :: IntStack :: pop()
  102. {
  103.   if( _size == 0 )
  104.     return 0;
  105.   int val = _list[_size];
  106.   _size--;
  107.   return val;
  108. }
  109. void Flu_Tree_Browser :: IntStack :: clear()
  110. {
  111.   if( _list )
  112.     free( _list );
  113.   _list = NULL;
  114.   _size = _bufferSize = 0;
  115. }
  116. Flu_Tree_Browser :: NodeList :: NodeList()
  117. {
  118.   _nodes = NULL;
  119.   _nNodes = _size = 0;
  120. }
  121. Flu_Tree_Browser :: NodeList :: ~NodeList()
  122. {
  123.   clear();
  124. }
  125. typedef Flu_Tree_Browser::Node* NodeP;
  126. bool Flu_Tree_Browser :: NodeList :: search( const char *n, int &index )
  127. {
  128.   index = _nNodes;
  129.   if( _nNodes == 0 )
  130.     return false;
  131.   // we know we have at least one node. so use it to get the RData struct to find out what
  132.   // the insertion mode is
  133.   int iMode = _nodes[0]->tree->insertion_mode();
  134.   if( iMode == FLU_INSERT_SORTED || iMode == FLU_INSERT_SORTED_REVERSE )
  135.     return( binSearch( n, index ) );
  136.   else
  137.     return( linSearch( n, index ) );
  138. }
  139. bool Flu_Tree_Browser :: NodeList :: search( Node *n, int &index )
  140. {
  141.   index = _nNodes;
  142.   if( _nNodes == 0 )
  143.     return false;
  144.   // we know we have at least one node. so use it to get the RData struct to find out what
  145.   // the insertion mode is
  146.   int iMode = _nodes[0]->tree->insertion_mode();
  147.   if( iMode == FLU_INSERT_SORTED || iMode == FLU_INSERT_SORTED_REVERSE )
  148.     return( binSearch( n, index ) );
  149.   else
  150.     return( linSearch( n, index ) );
  151. }
  152. bool Flu_Tree_Browser :: NodeList :: linSearch( const char *n, int &index )
  153. {
  154.   index = _nNodes;
  155.   for( int i = 0; i < _nNodes; i++ )
  156.     {
  157.       if( strcmp( n, _nodes[i]->label() ) == 0 )
  158. {
  159.   index = i;
  160.   return true;
  161. }
  162.     }
  163.   return false;
  164. }
  165. bool Flu_Tree_Browser :: NodeList :: linSearch( Node *n, int &index )
  166. {
  167.   index = _nNodes;
  168.   for( int i = 0; i < _nNodes; i++ )
  169.     {
  170.       if( n == _nodes[i] )
  171. {
  172.   index = i;
  173.   return true;
  174. }
  175.     }
  176.   return false;
  177. }
  178. bool Flu_Tree_Browser :: NodeList :: binSearch( Node *n, int &index )
  179. {
  180.   if( binSearch( n->label(), index ) )
  181.     {
  182.       // the search found the first node with the label. since there are identical entries
  183.       // allowed, it may not be the actual node we want. therefore search forward until we find it
  184.       for( ; index < _nNodes; index++ )
  185. if( _nodes[index] == n )
  186.   return true;
  187.       return false;
  188.     }
  189.   else
  190.     return false;
  191. }
  192. bool Flu_Tree_Browser :: NodeList :: binSearch( const char *n, int &index )
  193. {
  194.   // do a binary search for a child with name == "n"
  195.   // return true if the child is found, and store its index in "index"
  196.   // return false if the child is not found, and store the index it would
  197.   // be in in "index"
  198.   // special case: no nodes
  199.   if( _nNodes == 0 )
  200.     {
  201.       index = 0;
  202.       return false;
  203.     }
  204.   // we know we have at least one node. so use it to get the RData struct to find out what
  205.   // the insertion mode is
  206.   int iMode = _nodes[0]->tree->insertion_mode();
  207.   // special case: 1 node
  208.   if( _nNodes == 1 )
  209.     {
  210.       int val = strcmp( n, _nodes[0]->label() );
  211.       if( iMode == FLU_INSERT_SORTED_REVERSE )
  212. val *= -1;
  213.       if( val == 0 )
  214. {
  215.   index = 0;
  216.   return true;
  217. }
  218.       else if( val < 0 )
  219. index = 0;
  220.       else
  221. index = 1;
  222.       return false;
  223.     }
  224.   int first = 0, last = _nNodes - 1;
  225.   int val1, val2, mVal;
  226.   for(;;)
  227.     {
  228.       // the range is down to 2 nodes
  229.       if( last == first + 1 )
  230. {
  231.   val1 = strcmp( n, _nodes[first]->label() );
  232.   if( iMode == FLU_INSERT_SORTED_REVERSE )
  233.     val1 = -val1;
  234.   if( val1 < 0 )
  235.     {
  236.       index = first;
  237.       return false;
  238.     }
  239.   else if( val1 == 0 )
  240.     {
  241.       index = first;
  242.       break;
  243.     }
  244.   val2 = strcmp( n, _nodes[last]->label() );
  245.   if( iMode == FLU_INSERT_SORTED_REVERSE )
  246.     val2 = -val2;
  247.   if( val2 < 0 )
  248.     {
  249.       index = last;
  250.       return false;
  251.     }
  252.   else if( val2 == 0 )
  253.     {
  254.       index = last;
  255.       break;
  256.     }
  257.   else
  258.     {
  259.       index = last+1;
  260.       return false;
  261.     }
  262. }
  263.       // pick which half of the array to search next
  264.       int midpoint = first + ((last-first)>>1);
  265.       mVal = strcmp( n, _nodes[midpoint]->label() );
  266.       if( iMode == FLU_INSERT_SORTED_REVERSE )
  267. mVal = -mVal;
  268.       if( mVal < 0 )
  269. last = midpoint;
  270.       else if( mVal > 0 )
  271. first = midpoint;
  272.       else
  273. {
  274.   index = midpoint;
  275.   break;
  276. }
  277.     }
  278.   // we found *a* node equal to "n", now find the first node equal to "n"
  279.   // by searching until we hit a node not equal to "n"
  280.   for( first = index; first > 0; first-- )
  281.     if( strcmp( n, _nodes[first-1]->label() ) != 0 )
  282.       break;
  283.   index = first;
  284.   return true;
  285. }
  286. int Flu_Tree_Browser :: NodeList :: compareNodes( const void *arg1, const void* arg2 )
  287. {
  288.   Flu_Tree_Browser::Node *n1 = *((Flu_Tree_Browser::Node**)arg1), *n2 = *((Flu_Tree_Browser::Node**)arg2);
  289.   return strcmp( n1->text.c_str(), n2->text.c_str() );
  290. }
  291. int Flu_Tree_Browser :: NodeList :: reverseCompareNodes( const void *arg1, const void* arg2 )
  292. {
  293.   Flu_Tree_Browser::Node *n1 = *((Flu_Tree_Browser::Node**)arg1), *n2 = *((Flu_Tree_Browser::Node**)arg2);
  294.   return( -strcmp( n1->text.c_str(), n2->text.c_str() ) );
  295. }
  296. void Flu_Tree_Browser :: NodeList :: sort()
  297. {
  298.   if( _nNodes )
  299.     {
  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();
  303.       if( iMode == FLU_INSERT_SORTED )
  304. qsort( _nodes, _nNodes, sizeof(Node*), compareNodes );
  305.       else if( iMode == FLU_INSERT_SORTED_REVERSE )
  306. qsort( _nodes, _nNodes, sizeof(Node*), reverseCompareNodes );
  307.     }
  308. }
  309. bool Flu_Tree_Browser :: Node :: move( Node* n1, int where, Node* n2 )
  310. {
  311.   if( isMoveValid( n1, where, n2 ) )
  312.     return( NodeList::move( n1, where, n2 ) );
  313.   else
  314.     return false;
  315. }
  316. void Flu_Tree_Browser :: Node :: sort()
  317. {
  318.   _children.sort();
  319.   for( int i = 0; i < _children.size(); i++ )
  320.     _children.child(i)->sort();
  321. }
  322. bool Flu_Tree_Browser :: Node :: is_ancestor( Node* n )
  323. {
  324.   Node *p = parent();
  325.   while( p )
  326.     {
  327.       if( p == n )
  328. return true;
  329.       else
  330. p = p->parent();
  331.     }
  332.   return false;
  333. }
  334. bool Flu_Tree_Browser :: Node :: is_descendent( Node* n )
  335. {
  336.   return n->is_ancestor( this );
  337. }
  338. bool Flu_Tree_Browser :: NodeList :: move( Node* n1, int where, Node* n2 )
  339. {
  340.   if( !n1 || !n2 )
  341.     return false;
  342.   if( n1->tree )
  343.     n1->tree->redraw();
  344.   if( n2->tree )
  345.     n2->tree->redraw();
  346.   // try to move n1 to the first child position of n2
  347.   if( where == MOVE_INSIDE )
  348.     {
  349.       if( !n2->is_branch() )
  350. return false;
  351.       // get the parent of n1
  352.       Node* p1 = n1->parent();
  353.       if( p1 )
  354. // remove n1 from its parent's list
  355. p1->_children.erase( n1 );
  356.       // insert into n2
  357.       int iMode = n1->tree->insertion_mode();
  358.       if( iMode == FLU_INSERT_SORTED || iMode == FLU_INSERT_SORTED_REVERSE )
  359. n2->_children.add( n1 );
  360.       else
  361. n2->_children.add( n1, 0 );
  362.       // update the parent of n1
  363.       n1->_parent = n2;
  364.       return true;
  365.     }
  366.   // find the position of n2 in its parent's list
  367.   Node* p2 = n2->parent();
  368.   if( !p2 )
  369.     return false;
  370.   int index = 0, removed = -1;
  371.   if( p2->_children.search( n2, index ) )
  372.     {
  373.       // get the parent of n1
  374.       Node* p1 = n1->parent();
  375.       if( p1 )
  376. // remove n1 from its parent's list. remember the position it was removed from
  377. removed = p1->_children.erase( n1 );
  378.       // if n1 and n2 have the same parent, and if n1 came before the spot where
  379.       // n2 will be inserted, then our indexing is off by one because n1 has been removed
  380.       if( p1 == p2 && removed <= index )
  381. index--;
  382.       if( where == MOVE_AFTER )
  383.        index++;
  384.       // insert n1 at the proper position
  385.       p2->_children.add( n1, index );
  386.       // update the parent of n1
  387.       n1->_parent = p2;
  388.     }
  389.   return true;
  390. }
  391. void Flu_Tree_Browser :: NodeList :: add( Node* n, int position )
  392. {
  393.   int i, index;
  394.   int mode = n->tree->insertion_mode();
  395.   // if the list is out of room, allocate a new one that's bigger
  396.   if( _nNodes == _size )
  397.     {
  398.       int newSize = ( _size == 0 ) ? 1 : _size*2; // double the size of the old list (same behavior as STL vector)
  399.       // allocate the new list
  400.       Node** newNodes = new NodeP[ newSize ];
  401.       // copy the old list to the new list
  402.       memcpy( newNodes, _nodes, _nNodes*sizeof(Node*) );
  403.       // delete the old list and replace it with the new list
  404.       delete[] _nodes;
  405.       //n->tree->rdata.cbNode = NULL;
  406.       _nodes = newNodes;
  407.       _size = newSize;
  408.     }
  409.   if( position >= 0 )
  410.     {
  411.       if( position > _nNodes )
  412. index = _nNodes;
  413.       else
  414. index = position;
  415.     }
  416.   else if( mode == FLU_INSERT_SORTED || mode == FLU_INSERT_SORTED_REVERSE )
  417.     {
  418.       // search through the list until we find where to insert the node
  419.       binSearch( n->label(), index );
  420.     }
  421.   else if( mode == FLU_INSERT_FRONT )
  422.     {
  423.       index = 0;
  424.     }
  425.   else if( mode == FLU_INSERT_BACK )
  426.     {
  427.       index = _nNodes;
  428.     }
  429.   else
  430.     return;
  431.   // shift all entries from the new insertion point down one spot
  432.   // to make room for the new node
  433.   for( i = _nNodes - 1; i >= index; i-- )
  434.     _nodes[i+1] = _nodes[i];
  435.   // add the new node
  436.   _nodes[index] = n;
  437.   _nNodes++;
  438. }
  439. int Flu_Tree_Browser :: NodeList :: erase( Node *n )
  440. {
  441.   if( n == NULL )
  442.     return -1;
  443.   int index;
  444.   if( search( n, index ) )
  445.     {
  446.       // move all the others down one spot to remove the node
  447.       for( int i = index; i < _nNodes-1; i++ )
  448. _nodes[i] = _nodes[i+1];
  449.       _nNodes--;
  450.       return index;
  451.     }
  452.   return -1;
  453. }
  454. int Flu_Tree_Browser :: NodeList :: erase( const char* n )
  455. {
  456.   if( _nNodes == 0 )
  457.     return -1;
  458.   int index;
  459.   if( search( n, index ) )
  460.     {
  461.       // move all the others down one spot to remove the node
  462.       for( int i = index; i < _nNodes-1; i++ )
  463. _nodes[i] = _nodes[i+1];
  464.       _nNodes--;
  465.       return index;
  466.     }
  467.   return -1;
  468. }
  469. void Flu_Tree_Browser :: NodeList :: erase( int n )
  470. {
  471.   // make sure n is in range
  472.   if( ( n < 0 ) || ( n >= _nNodes ) )
  473.     return;
  474.   // move all the others down one spot to remove the node
  475.   for( int i = n; i < _nNodes-1; i++ )
  476.     _nodes[i] = _nodes[i+1];
  477.   _nNodes--;
  478. }
  479. void Flu_Tree_Browser :: NodeList :: clear()
  480. {
  481.   if( _nodes )
  482.     {
  483.       //if( _nNodes )
  484.       //if( _nodes[0] )
  485.       //  _nodes[0]->tree->rdata.cbNode = NULL;
  486.       delete[] _nodes;
  487.     }
  488.   _nodes = NULL;
  489.   _nNodes = _size = 0;
  490. }
  491. int Flu_Tree_Browser :: NodeList :: findNum( const char *n )
  492. {
  493.   if( ( _nNodes == 0 ) || ( n == 0 ) )
  494.     return 0;
  495.   // see if there is a first node equal to "n"
  496.   int index, last;
  497.   if( !search( n, index ) )
  498.     return 0;
  499.   // now search forward until we hit a node not equal to "n"
  500.   for( last = index; last < _nNodes-1; last++ )
  501.     if( strcmp( n, _nodes[last+1]->label() ) != 0 )
  502.       break;
  503.   return last - index + 1;
  504. }
  505. Flu_Tree_Browser::Node* Flu_Tree_Browser :: NodeList :: find( const char* n, int which )
  506. {
  507.   if( ( _nNodes == 0 ) || ( n == 0 ) || ( which == 0 ) )
  508.     return NULL;
  509.   // see if there is a first node equal to "n"
  510.   int index, first;
  511.   if( !search( n, first ) )
  512.     return NULL;
  513.   // now search forward and try to find the which'th node named "n"
  514.   int total = 0;
  515.   for( index = first; index < _nNodes; index++ )
  516.     {
  517.       if( strcmp( n, _nodes[index]->label() ) == 0 )
  518. {
  519.   total++;
  520.   if( total == which )
  521.     break;
  522. }
  523.       else
  524. break;
  525.     }
  526.   if( total != which )
  527.     return NULL;
  528.   return _nodes[index];
  529. }
  530. #define SCROLL_SIZE 15
  531. Flu_Tree_Browser :: Flu_Tree_Browser( int x, int y, int w, int h, const char *l )
  532.   : Fl_Group( x, y, w, h )
  533. #ifdef USE_FLU_DND
  534.   , Flu_DND( "Flu_Tree_Browser" )
  535. #endif
  536. {
  537.   lastEvent = -1;
  538.   autoScrollX = autoScrollY = 0.0f;
  539. #ifdef USE_FLU_DND
  540.   dnd_allow_type( "Flu_Tree_Browser" );
  541.   dnd_allow_type( "DND_Object" );
  542.   dnd_allow_text( true );
  543. #endif
  544.   // add some widgets
  545.   _box = new Fl_Group( x, y, w-SCROLL_SIZE, h-SCROLL_SIZE );
  546.   _box->resizable( NULL );
  547.   _box->end();
  548.   scrollV = new Fl_Scrollbar( x+w-SCROLL_SIZE, y, SCROLL_SIZE, h-SCROLL_SIZE );
  549.   scrollV->type( FL_VERTICAL );
  550.   scrollV->callback( _scrollCB, this );
  551.   scrollV->value( 0, 1, 0, 0 );
  552.   scrollH = new Fl_Scrollbar( x, y+h-SCROLL_SIZE, w-SCROLL_SIZE, SCROLL_SIZE );
  553.   scrollH->type( FL_HORIZONTAL );
  554.   scrollH->callback( _scrollCB, this );
  555.   scrollH->value( 0, 1, 0, 0 );
  556.   scrollBox = new Fl_Group( x+w-SCROLL_SIZE, y+h-SCROLL_SIZE, SCROLL_SIZE, SCROLL_SIZE );
  557.   scrollBox->box( FL_UP_BOX );
  558.   scrollBox->end();
  559.   resizable( _box );
  560.   // set up the recursive data structure
  561.   memset( &rdata, 0, sizeof(rdata) );
  562.   rdata.root = &root;
  563.   root.tree = this;
  564.   rdata.tree = this;
  565.   rdata.dragging = false;
  566.   rdata.forceResize = true;
  567.   rdata.shiftSelect = false;
  568.   rdata.shiftSelectAll = false;
  569.   rdata.nextId = 1;
  570.   rdata.searchIndex = 1;
  571.   rdata.defaultCollapseIcons[0] = new Fl_Pixmap( (char*const*)plus_xpm );
  572.   rdata.defaultCollapseIcons[1] = new Fl_Pixmap( (char*const*)minus_xpm );
  573.   rdata.defaultBranchIcons[0] = new Fl_Pixmap( (char*const*)folder_closed_xpm );
  574.   rdata.defaultBranchIcons[1] = new Fl_Pixmap( (char*const*)folder_open_xpm );
  575.   end();
  576.   // set the default values for the tree
  577.   auto_branches( false );
  578.   animate( false );
  579.   collapse_time( 0.1f );
  580.   double_click_opens( true );
  581.   move_only_same_group( false );
  582.   frame_rate( 100.0f );
  583.   allow_leaf_duplication( true );
  584.   shaded_entry_colors( FL_WHITE, FL_WHITE );
  585.   collapse_icons( NULL, NULL );
  586.   //branch_icons( NULL, NULL );
  587.   rdata.branchIcons[0] = rdata.defaultBranchIcons[0];
  588.   rdata.branchIcons[1] = rdata.defaultBranchIcons[1];
  589.   leaf_icon( NULL );
  590.   branch_text( FL_BLACK, FL_HELVETICA_BOLD, 12 );
  591.   leaf_text( FL_BLACK, FL_HELVETICA, 12 );
  592.   callback( NULL );
  593.   when( FL_WHEN_CHANGED );
  594.   color( FL_WHITE );
  595.   selection_color( FL_SELECTION_COLOR );
  596.   box( FL_NO_BOX );
  597.   connector_style( FL_DARK2, FL_DOT );
  598.   selection_mode( FLU_MULTI_SELECT );
  599.   selection_drag_mode( FLU_DRAG_TO_SELECT );
  600.   insertion_mode( FLU_INSERT_SORTED );
  601.   show_connectors( true );
  602.   show_root( true );
  603.   show_leaves( true );
  604.   show_branches( true );
  605.   open_on_select( false );
  606.   //root_always_open( false );
  607.   horizontal_gap( 2 );
  608.   vertical_gap( 0 );
  609.   widget_gap( 2 );
  610.   set_root( l );
  611. }
  612. Flu_Tree_Browser :: ~Flu_Tree_Browser()
  613. {
  614.   Fl::remove_timeout( _timerRedrawCB, this );
  615.   Fl::remove_timeout( _timerScrollCB, this );
  616.   delete rdata.defaultCollapseIcons[0];
  617.   delete rdata.defaultCollapseIcons[1];
  618.   delete rdata.defaultBranchIcons[0];
  619.   delete rdata.defaultBranchIcons[1];
  620. }
  621. void Flu_Tree_Browser :: auto_branches( bool b )
  622. {
  623.   rdata.autoBranches = b;
  624. }
  625. void Flu_Tree_Browser :: collapse_icons( Fl_Image *closed, Fl_Image *open )
  626. {
  627.   if( closed )
  628.     rdata.collapseIcons[0] = closed;
  629.   else
  630.     rdata.collapseIcons[0] = rdata.defaultCollapseIcons[0];
  631.   if( open )
  632.     rdata.collapseIcons[1] = open;
  633.   else
  634.     rdata.collapseIcons[1] = rdata.defaultCollapseIcons[1];
  635. }
  636. void Flu_Tree_Browser :: branch_icons( Fl_Image *closed, Fl_Image *open )
  637. {
  638.   if( closed )
  639.     rdata.branchIcons[0] = closed;
  640.   //else
  641.   //rdata.branchIcons[0] = rdata.defaultBranchIcons[0];
  642.   if( open )
  643.     rdata.branchIcons[1] = open;
  644.   //else
  645.   //rdata.branchIcons[1] = rdata.defaultBranchIcons[1];
  646. }
  647. void Flu_Tree_Browser :: leaf_icon( Fl_Image *icon )
  648. {
  649.   rdata.leafIcon = icon;
  650. }
  651. void Flu_Tree_Browser :: resize( int X, int Y, int W, int H )
  652. {
  653.   Fl_Group::resize( X, Y, W, H );
  654.   int dx = Fl::box_dx(box()), dy = Fl::box_dy(box()), dw = Fl::box_dw(box()), dh = Fl::box_dh(box());
  655.   rdata.x = X+dx; rdata.y = Y+dy; rdata.totalW = rdata.x;
  656.   root.recurse( rdata, Node::MEASURE );
  657.   rdata.totalW -= X-dx;
  658.   rdata.totalH = rdata.y - Y-dy;
  659.   // if the size of the tree is bigger than the window, turn on the scrollbars
  660.   bool hOn = false, vOn = false;
  661.   if( rdata.totalW > W-dw )
  662.     hOn = true;
  663.   if( rdata.totalH > H-dh )
  664.     vOn = true;
  665.   // check if turning on one scrollbar actually forces the other to turn on
  666.   if( hOn && ( rdata.totalH > H-SCROLL_SIZE ) )
  667.     vOn = true;
  668.   if( vOn && ( rdata.totalW > W-SCROLL_SIZE ) )
  669.     hOn = true;
  670.   // now resize the other kids depending on the state of the scrollbars
  671.   _box->resize( X, Y, W, H );
  672.   if( hOn && vOn )  // both scrollbars on
  673.     {
  674.       scrollH->resize( X+dx, Y+H-SCROLL_SIZE-dy, W-SCROLL_SIZE-dw, SCROLL_SIZE );
  675.       scrollH->show();
  676.       scrollV->resize( X+W-SCROLL_SIZE-dx, Y+dy, SCROLL_SIZE, H-SCROLL_SIZE-dh );
  677.       scrollV->show();
  678.       scrollBox->resize( X+W-SCROLL_SIZE-dx, Y+H-SCROLL_SIZE-dy, SCROLL_SIZE, SCROLL_SIZE );
  679.       scrollBox->show();
  680.       // set the scrollbar sizes and values
  681.       int hDelta = rdata.totalW - W+dw + SCROLL_SIZE, scrollHW = scrollH->w()-SCROLL_SIZE-SCROLL_SIZE;
  682.       hDelta = MAX( hDelta, 0 );
  683.       scrollH->value( MIN( scrollH->value(), hDelta ), 1, 0, hDelta );
  684.       scrollH->slider_size( MAX( (float)SCROLL_SIZE/float(scrollHW), float(scrollHW-hDelta)/float(scrollHW) ) );
  685.       int vDelta = rdata.totalH - H+dh + SCROLL_SIZE, scrollVH = scrollV->h()-SCROLL_SIZE-SCROLL_SIZE;
  686.       vDelta = MAX( vDelta, 0 );
  687.       scrollV->value( MIN( scrollV->value(), vDelta ), 1, 0, vDelta );
  688.       scrollV->slider_size( MAX( (float)SCROLL_SIZE/float(scrollVH), float(scrollVH-vDelta)/float(scrollVH) ) );
  689.     }
  690.   else if( !hOn && !vOn )  // neither on
  691.     {
  692.       scrollH->hide();
  693.       scrollV->hide();
  694.       scrollBox->hide();
  695.     }
  696.   else if( hOn )  // just horizontal on
  697.     {
  698.       scrollH->resize( X+dx, Y+H-SCROLL_SIZE-dy, W-dw, SCROLL_SIZE );
  699.       scrollH->show();
  700.       scrollV->hide();
  701.       scrollBox->hide();
  702.       // set the scrollbar size and value
  703.       int hDelta = rdata.totalW - W+dw, scrollHW = scrollH->w()-SCROLL_SIZE-SCROLL_SIZE;
  704.       hDelta = MAX( hDelta, 0 );
  705.       scrollH->value( MIN( scrollH->value(), hDelta ), 1, 0, hDelta );
  706.       scrollH->slider_size( MAX( (float)SCROLL_SIZE/float(scrollHW), float(scrollHW-hDelta)/float(scrollHW) ) );
  707.     }
  708.   else if( vOn )  // just vertical on
  709.     {
  710.       scrollH->hide();
  711.       scrollV->resize( X+W-SCROLL_SIZE-dx, Y+dy, SCROLL_SIZE, H-dh );
  712.       scrollV->show();
  713.       scrollBox->hide();
  714.       // set the scrollbar size and value
  715.       int vDelta = rdata.totalH - H+dh, scrollVH = scrollV->h()-SCROLL_SIZE-SCROLL_SIZE;
  716.       vDelta = MAX( vDelta, 0 );
  717.       scrollV->value( MIN( scrollV->value(), vDelta ), 1, 0, vDelta );
  718.       scrollV->slider_size( MAX( (float)SCROLL_SIZE/float(scrollVH), float(scrollVH-vDelta)/float(scrollVH) ) );
  719.     }
  720.   rdata.browserX = _box->x() + dx;
  721.   rdata.browserY = _box->y() + dy;
  722.   rdata.browserW = _box->w() - dw;
  723.   rdata.browserH = _box->h() - dh;
  724.   redraw();
  725.   rdata.forceResize = true;  // weird hack to get the scrollbars to turn on right the first time
  726. }
  727. void Flu_Tree_Browser :: on_dnd_leave()
  728. {
  729. #ifdef USE_FLU_DND
  730.   rdata.isMoveValid = false;
  731.   redraw();
  732.   // X
  733.   if( scrollH->visible() )
  734.     {
  735.       float max = 0.01f * (scrollH->maximum() - scrollH->minimum());
  736.       if( max < 10.0f ) max = 10.0f;
  737.       if( autoScrollX > 0.0f )
  738. autoScrollX = max;
  739.       else
  740. autoScrollX = -max;
  741.     }
  742.   // Y
  743.   if( scrollV->visible() )
  744.     {
  745.       float max = 0.01f * (scrollV->maximum() - scrollV->minimum());
  746.       if( max < 10.0f ) max = 10.0f;
  747.       if( autoScrollY > 0.0f )
  748. autoScrollY = max;
  749.       else
  750. autoScrollY = -max;
  751.     }
  752. #endif
  753. }
  754. bool Flu_Tree_Browser :: on_dnd_drag( int X, int Y )
  755. {
  756. #ifdef USE_FLU_DND
  757.   rdata.dragging = true;
  758.   autoScrollX = autoScrollY = 0.0f;
  759.   if( scrollH->visible() )
  760.     {
  761.       // auto-scroll the horizontal scrollbars based on how close to the left or right of the browser the mouse is
  762.       float min = 1.0f, max = 0.01f * (scrollH->maximum() - scrollH->minimum());
  763.       if( max < 10.0f ) max = 10.0f;
  764.       if( X < (x()+30) ) // left
  765. {
  766.   float t = float((x()+30) - X) / 30.0f;
  767.   autoScrollX = -LERP( t*t, min, max );
  768.   if( !scrolledTimerOn )
  769.     Fl::add_timeout( 0.0, _timerScrollCB, this );
  770. }
  771.       else if( X > (x()+w()-30) ) // right
  772. {
  773.   float t = float(X - (x()+w()-30)) / 30.0f;
  774.   autoScrollX = LERP( t*t, min, max );
  775.   if( !scrolledTimerOn )
  776.     Fl::add_timeout( 0.0, _timerScrollCB, this );
  777. }
  778.     }
  779.   if( scrollV->visible() )
  780.     {
  781.       // auto-scroll the vertical scrollbars based on how close to the top or bottom of the browser the mouse is
  782.       float min = 1.0f, max = 0.01f * (scrollV->maximum() - scrollV->minimum());
  783.       if( max < 10.0f ) max = 10.0f;
  784.       if( Y < (y()+30) ) // top
  785. {
  786.   float t = float((y()+30) - Y) / 30.0f;
  787.   autoScrollY = -LERP( t*t, min, max );
  788.   if( !scrolledTimerOn )
  789.     Fl::add_timeout( 0.0, _timerScrollCB, this );
  790. }
  791.       else if( Y > (y()+h()-30) ) // bottom
  792. {
  793.   float t = float(Y - (y()+h()-30)) / 30.0f;
  794.   autoScrollY = LERP( t*t, min, max );
  795.   if( !scrolledTimerOn )
  796.     Fl::add_timeout( 0.0, _timerScrollCB, this );
  797. }
  798.     }
  799.   if( autoScrollX == 0.0f && autoScrollY == 0.0f )
  800.     {
  801.       Fl::remove_timeout( _timerScrollCB, this );
  802.       scrolledTimerOn = false;
  803.     }
  804.   int dx = Fl::box_dx(box()), dy = Fl::box_dy(box());
  805.   rdata.x = x()+dx; rdata.y = y()+dy;
  806.   if( scrollH->visible() )
  807.     rdata.x -= scrollH->value();
  808.   if( scrollV->visible() )
  809.     rdata.y -= scrollV->value();
  810.   rdata.delta = 0;
  811.   root.recurse( rdata, Node::HANDLE, FL_DND_DRAG );
  812.   rdata.isMoveValid = Fl::event_inside( this ) && Node::isMoveValid( rdata.grabbed, rdata.dragWhere, rdata.dragNode );
  813.   redraw();
  814.   Fl::flush();
  815.   if( rdata.isMoveValid )
  816.     return true;
  817.   else
  818. #endif
  819.     return false;
  820. }
  821. void Flu_Tree_Browser :: on_dnd_release()
  822. {
  823. #ifdef USE_FLU_DND
  824.   Fl::remove_timeout( _timerScrollCB, this );
  825.   scrolledTimerOn = false;
  826.   redraw();
  827.   Fl::flush();
  828. #endif
  829. }
  830. void Flu_Tree_Browser :: on_dnd_drop( const Flu_DND_Event *e )
  831. {
  832. #ifdef USE_FLU_DND
  833.   bool newNode = false;
  834.   if( !rdata.isMoveValid )
  835.     rdata.grabbed = 0;
  836.   else if( e->event_is_text() && rdata.dnd )
  837.     {
  838.       // create a new node with the text as the name and make it the grabbed node
  839.       rdata.grabbed = new Node( true, e->text(), NULL, rdata, NULL, true );
  840.       if( rdata.grabbed )
  841. newNode = true;
  842.     }
  843.   else
  844.     {
  845.       if( e->is_data_type( "Flu_Tree_Browser" ) )
  846. {
  847.   if( rdata.moveOnlySameGroup && ( rdata.grabbed->parent() != rdata.dragNode->parent() ) )
  848.     rdata.grabbed = NULL;
  849. }
  850.       else if( e->is_data_type( "DND_Object" ) && rdata.dnd )
  851. {
  852.   // create a new node with the text as the name and make it the grabbed node
  853.   DND_Object *o = (DND_Object*)e->data();
  854.   rdata.grabbed = new Node( true, o->name(), NULL, rdata, NULL, true );
  855.   if( rdata.grabbed )
  856.     {
  857.       rdata.grabbed->user_data( e->data() );
  858.       newNode = true;
  859.     }
  860. }
  861.       else
  862. rdata.grabbed = NULL;
  863.     }
  864.   // select only the new/moved node
  865.   root.unselect_all( rdata.grabbed );
  866.   set_hilighted( rdata.grabbed );
  867.   if( rdata.grabbed )
  868.     {
  869.       rdata.grabbed->select( true );
  870.   
  871.       // move the node
  872.       if( NodeList::move( rdata.grabbed, rdata.dragWhere, rdata.dragNode ) )
  873. {
  874.   if( newNode )
  875.     rdata.grabbed->do_callback( FLU_NEW_NODE );
  876.   else
  877.     rdata.grabbed->do_callback( FLU_MOVED_NODE );
  878. }
  879.       rdata.forceResize = true;
  880.     }
  881.   Fl::focus(this);
  882.   rdata.dragging = false;
  883.   rdata.grabbed = 0;
  884.   rdata.dragNode = 0;
  885.   Fl::remove_timeout( _timerScrollCB, this );
  886.   scrolledTimerOn = false;
  887.   redraw();
  888. #endif
  889. }
  890. int Flu_Tree_Browser :: handle( int event )
  891. {
  892. #ifdef USE_FLU_DND
  893.   if( dnd_handle( event ) )
  894.     return 1;
  895. #endif
  896.   //if( event == FL_NO_EVENT || event == FL_MOVE )
  897.   //return 0;
  898.   if( event == FL_FOCUS && rdata.lastHilighted )
  899.     {
  900.       set_hilighted( rdata.lastHilighted );
  901.       lastEvent = event;
  902.       Fl_Group::handle( event );
  903.       redraw();
  904.       return 1;
  905.     }
  906.   if( event == FL_UNFOCUS )
  907.     {
  908.       if( lastEvent != FL_LEAVE )
  909. {
  910.   rdata.lastHilighted = rdata.hilighted;
  911. }
  912.       set_hilighted( NULL );
  913.       lastEvent = event;
  914.       Fl_Group::handle( event );
  915.       redraw();
  916.       return 1;
  917.     }
  918.   if( !rdata.dragging )
  919.     {
  920.       if( ! (event == FL_MOVE || event == FL_ENTER || event == FL_LEAVE ) )
  921. _box->redraw();
  922.       if( Fl_Group::handle( event ) )
  923. {
  924.   //if( event == FL_KEYDOWN || event == FL_KEYUP )
  925.     // redraw();
  926.   return 1;
  927. }
  928.     }
  929.   if( event == FL_RELEASE )
  930.     {
  931.       Fl::focus(this);
  932.       rdata.dragging = false;
  933.       rdata.grabbed = 0;
  934.       rdata.dragNode = 0;
  935.       redraw();
  936.     }
  937.   int dx = Fl::box_dx(box()), dy = Fl::box_dy(box());
  938.   // set some initial values for the recursive data structure
  939.   // account for the scrollbar positions
  940.   rdata.x = x()+dx; rdata.y = y()+dy;
  941.   if( scrollH->visible() )
  942.     rdata.x -= scrollH->value();
  943.   if( scrollV->visible() )
  944.     rdata.y -= scrollV->value();
  945.   rdata.previous = NULL;
  946.   rdata.delta = 0;
  947.   rdata.visibilityChanged = false;
  948.   // catch cursor keys for moving the hilighted entry or selecting all entries
  949.   if( event == FL_KEYDOWN )
  950.     {
  951.       // move hilighted entry up
  952.       if( Fl::event_key() == FL_Up )
  953. {
  954.   rdata.delta = -1;
  955.   Fl::focus(this);
  956.   redraw();
  957. }
  958.       // move hilighted entry down
  959.       else if( Fl::event_key() == FL_Down )
  960. {
  961.   rdata.delta = 1;
  962.   Fl::focus(this);
  963.   redraw();
  964. }
  965.       // select all
  966.       else if( Fl::event_state(FL_CTRL) && Fl::event_key() == 'a' )
  967. {
  968.   select_all();
  969.   Fl::focus(this);
  970.   redraw();
  971.   return 1;
  972. }
  973.       // check for the Home key
  974.       else if( Fl::event_key() == FL_Home )
  975. {
  976.   // set the hilighted entry to be the first entry
  977.   if( rdata.showRoot || ( rdata.root->_children.size() == 0 ) )
  978.     set_hilighted( rdata.root );
  979.   else if( rdata.root->_children.size() > 0 )
  980.     set_hilighted( rdata.root->_children.child(0) );
  981.   redraw();
  982. }
  983.       // check for the End key
  984.       else if( Fl::event_key() == FL_End )
  985. {
  986.   // set the hilighted entry to be the last visible entry
  987.   if( rdata.showRoot && ( rdata.root->_children.size() == 0 ) )
  988.     set_hilighted( rdata.root );
  989.   else
  990.     {
  991.       // find the last node by repeatedly looking for the last child until there are no more branches
  992.       Node *n = &root;
  993.       while( n->_children.size() && n->open() )
  994. n = n->_children.child( n->_children.size()-1 );
  995.       set_hilighted( n );
  996.     }
  997.   redraw();
  998. }
  999.     }
  1000.   // pass the event down the tree
  1001.   int val = root.recurse( rdata, Node::HANDLE, event );
  1002.   if( val )
  1003.     {
  1004.       redraw();
  1005.       if( rdata.visibilityChanged )
  1006. root.determineVisibility();
  1007.       if( val == 1 )
  1008. return 1;
  1009.     }
  1010.   // special case: if multi-select or single-select and user clicks on no items, unselect all items
  1011.   else if( (rdata.selectionMode != FLU_NO_SELECT) && (event == FL_PUSH) && (!Fl::event_state(FL_CTRL)) )
  1012.     {
  1013.       unselect_all();
  1014.       set_hilighted( NULL );
  1015.       rdata.forceResize = true;
  1016.       redraw();
  1017.       return 1;
  1018.     }
  1019.   if( event == FL_SHOW || event == FL_HIDE )
  1020.     root.determineVisibility();
  1021.   return Fl_Group::handle( event );
  1022.   //return 0;
  1023. }
  1024. void Flu_Tree_Browser :: insertion_mode( int m )
  1025. {
  1026.   rdata.insertionMode = m;
  1027.   root.sort();
  1028. }
  1029. void Flu_Tree_Browser :: set_hilighted( Flu_Tree_Browser::Node* n )
  1030. {
  1031.   if( rdata.hilighted == n  &&  rdata.when != FL_WHEN_NOT_CHANGED )
  1032.     return;
  1033.   if( rdata.hilighted )
  1034.     rdata.hilighted->do_callback( FLU_UNHILIGHTED );
  1035.   rdata.hilighted = n;
  1036.   if( rdata.hilighted )
  1037.     rdata.hilighted->do_callback( FLU_HILIGHTED );
  1038.   if( rdata.hilighted )
  1039.     {
  1040.       int extraH = scrollH->visible() ? scrollH->h() : 0;
  1041.       // if the hilighted entry is below the visible bounds of the browser, move the vertical scrollbar
  1042.       // so the hilighted entry is the last visible entry
  1043.       if( rdata.hilighted->currentY-y()+rdata.hilighted->currentH > scrollV->value()+h()-extraH )
  1044. ((Fl_Valuator*)scrollV)->value( rdata.hilighted->currentY-y() - h()+extraH + rdata.hilighted->currentH );
  1045.       // if the hilighted entry is above the visible bounds of the browser, move the vertical scrollbar
  1046.       // so the hilighted entry is the first visible entry
  1047.       if( rdata.hilighted->currentY-y() < scrollV->value() )
  1048. ((Fl_Valuator*)scrollV)->value( rdata.hilighted->currentY-y() );
  1049.     }
  1050.   redraw();
  1051. }
  1052. int Flu_Tree_Browser :: num_selected()
  1053. {
  1054.   return root.recurse( rdata, Node::COUNT_SELECTED );
  1055. }
  1056. Flu_Tree_Browser::Node* Flu_Tree_Browser :: get_selected( int index )
  1057. {
  1058.   rdata.counter = 0;
  1059.   rdata.searchIndex = index;
  1060.   Node *n = root.modify( 0, Node::GET_SELECTED, rdata );
  1061.   rdata.searchIndex = 1;
  1062.   return n;
  1063. }
  1064. Flu_Tree_Browser :: Node :: Node( const char *lbl )
  1065. {
  1066.   flags = 0;
  1067.   userData = 0;
  1068.   _parent = 0;
  1069.   _widget = 0;
  1070.   SET(ACTIVE);
  1071.   CLEAR(LEAF);
  1072.   _id = 0;
  1073.   CLEAR(ALWAYS_OPEN);
  1074.   SET(COLLAPSED);
  1075.   SET(MOVABLE);
  1076.   SET(DROPPABLE);
  1077.   currentY = currentH = 0;
  1078.   totalChildH = 0;
  1079.   CLEAR(SELECTED);
  1080.   CLEAR(EXPAND_TO_WIDTH);
  1081.   SET(SHOW_LABEL);
  1082.   if( lbl == 0 )
  1083.     text = "";
  1084.   else
  1085.     text = lbl;
  1086.   cIcon[0] = cIcon[1] = bIcon[0] = bIcon[1] = lIcon = 0;
  1087. }
  1088. Flu_Tree_Browser :: Node :: Node( bool l, const char* n, Node *p, RData &rdata, Fl_Widget *w, bool showLbl )
  1089. {
  1090.   flags = 0;
  1091.   userData = 0;
  1092.   SET(LEAF,l);
  1093.   text = n;
  1094.   _id = 0;
  1095.   SET(ACTIVE);
  1096.   _parent = p;
  1097.   CLEAR(ALWAYS_OPEN);
  1098.   SET(COLLAPSED);
  1099.   CLEAR(SELECTED);
  1100.   CLEAR(EXPAND_TO_WIDTH);
  1101.   SET(MOVABLE);
  1102.   SET(DROPPABLE);
  1103.   _widget = 0;
  1104.   totalChildH = 0;
  1105.   currentY = currentH = 0;
  1106.   cIcon[0] = cIcon[1] = bIcon[0] = bIcon[1] = lIcon = 0;
  1107.   SET( SHOW_LABEL, showLbl );
  1108.   tree = rdata.tree;
  1109.   initType();
  1110.   _id = rdata.nextId++;
  1111.   widget( w );
  1112. }
  1113. void Flu_Tree_Browser :: Node :: initType()
  1114. {
  1115.   if( is_leaf() )
  1116.     {
  1117.       lIcon = tree->rdata.leafIcon;
  1118.       textColor = tree->rdata.defLeafColor;
  1119.       textFont = tree->rdata.defLeafFont;
  1120.       textSize = tree->rdata.defLeafSize;
  1121.     }
  1122.   else
  1123.     {
  1124.       cIcon[0] = tree->rdata.collapseIcons[0];
  1125.       cIcon[1] = tree->rdata.collapseIcons[1];
  1126.       bIcon[0] = tree->rdata.branchIcons[0];
  1127.       bIcon[1] = tree->rdata.branchIcons[1];
  1128.       textColor = tree->rdata.defBranchColor;
  1129.       textFont = tree->rdata.defBranchFont;
  1130.       textSize = tree->rdata.defBranchSize;
  1131.     }
  1132. }
  1133. Flu_Tree_Browser :: Node :: ~Node()
  1134. {
  1135.   // if this node is in a tree, make sure it isn't holding a reference to us
  1136.   if( tree )
  1137.     {
  1138.       if( tree->rdata.hilighted == this ) tree->rdata.hilighted = NULL;
  1139.       if( tree->rdata.lastHilighted == this ) tree->rdata.lastHilighted = NULL;
  1140.       if( tree->rdata.grabbed == this ) tree->rdata.grabbed = NULL;
  1141.       if( tree->rdata.dragNode == this ) tree->rdata.dragNode = NULL;
  1142.     }
  1143.   clear();
  1144. }
  1145. Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: first()
  1146. {
  1147.   return this;
  1148. }
  1149. Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: first_branch()
  1150. {
  1151.   Node *n = first();
  1152.   while( n )
  1153.     {
  1154.       if( n->is_branch() )
  1155. return n;
  1156.       else
  1157. n = n->next();
  1158.     }
  1159.   return NULL;
  1160. }
  1161. Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: first_leaf()
  1162. {
  1163.   Node *n = first();
  1164.   while( n )
  1165.     {
  1166.       if( n->is_leaf() )
  1167. return n;
  1168.       else
  1169. n = n->next();
  1170.     }
  1171.   return NULL;
  1172. }
  1173. Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: last()
  1174. {
  1175.   if( children() == 0 )
  1176.     return this;
  1177.   else
  1178.     return( child( children() - 1 )->last() );
  1179. }
  1180. Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: last_branch()
  1181. {
  1182.   Node *n = last();
  1183.   while( n )
  1184.     {
  1185.       if( n->is_branch() )
  1186. return n;
  1187.       else
  1188. n = n->previous();
  1189.     }
  1190.   return NULL;
  1191. }
  1192. Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: last_leaf()
  1193. {
  1194.   Node *n = last();
  1195.   while( n )
  1196.     {
  1197.       if( n->is_leaf() )
  1198. return n;
  1199.       else
  1200. n = n->previous();
  1201.     }
  1202.   return NULL;
  1203. }
  1204. Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: next_sibling()
  1205. {
  1206.   if( is_root() )
  1207.     return NULL;
  1208.   int index;
  1209.   for( index = 0; index < _parent->children(); index++ )
  1210.     if( _parent->child(index) == this )
  1211.       break;
  1212.   // if we are the last child of our parent, then we have no next sibling
  1213.   if( index == _parent->children()-1 )
  1214.     return NULL;
  1215.   // otherwise return our next sibling
  1216.   else
  1217.     return( _parent->child(index+1) );
  1218. }
  1219. Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: previous_sibling()
  1220. {
  1221.   if( is_root() )
  1222.     return NULL;
  1223.   int index;
  1224.   for( index = 0; index < _parent->children(); index++ )
  1225.     if( _parent->child(index) == this )
  1226.       break;
  1227.   // if we are the first child of our parent, then we have no previous sibling
  1228.   if( index == 0 )
  1229.     return NULL;
  1230.   // otherwise return our previous sibling
  1231.   else
  1232.     return( _parent->child(index-1) );
  1233. }
  1234. int Flu_Tree_Browser :: Node :: index() const
  1235. {
  1236.   if( is_root() )
  1237.     return -1;
  1238.   int index;
  1239.   for( index = 0; index < _parent->children(); index++ )
  1240.     if( _parent->child(index) == this )
  1241.       return index;
  1242.   return -1;
  1243. }
  1244. Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: next()
  1245. {
  1246.   // take care of the root node as a special case
  1247.   if( is_root() )
  1248.     {
  1249.       if( children() )
  1250. return child(0);
  1251.       else
  1252. return NULL;
  1253.     }
  1254.   // if we are a branch, then the next node is our first child, unless we don't have any children
  1255.   if( is_branch() && _children.size() )
  1256.     return _children.child(0);
  1257.   else
  1258.     {
  1259.       // otherwise, the next node is our next sibling. if there is no next sibling (because we
  1260.       // are the last child of our parent), then the next node is the next sibling of our parent (and so on...)
  1261.       Node *p = parent(), *n = next_sibling();
  1262.       while( p )
  1263. {
  1264.   if( n )
  1265.     return n;
  1266.   else
  1267.     {
  1268.       n = p->next_sibling();
  1269.       p = p->parent();
  1270.     }
  1271. }
  1272.       return NULL;
  1273.     }
  1274. }
  1275. Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: next_branch()
  1276. {
  1277.   Node *n = next();
  1278.   while( n )
  1279.     {
  1280.       if( n->is_branch() )
  1281. return n;
  1282.       else
  1283. n = n->next();
  1284.     }
  1285.   return NULL;
  1286. }
  1287. Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: next_leaf()
  1288. {
  1289.   Node *n = next();
  1290.   while( n )
  1291.     {
  1292.       if( n->is_leaf() )
  1293. return n;
  1294.       else
  1295. n = n->next();
  1296.     }
  1297.   return NULL;
  1298. }
  1299. Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: previous()
  1300. {
  1301.   // take care of the root node as a special case
  1302.   if( is_root() )
  1303.     return NULL;
  1304.   // the previous node is either our parent's
  1305.   // previous sibling (if that sibling exists and is a leaf or a branch with no children),
  1306.   // or the last child of our parent's previous sibling (if that sibling exists and is
  1307.   // a branch with children). if there is no previous sibling, then the previous node
  1308.   // is our parent
  1309.   Node *n = previous_sibling();
  1310.   if( !n )
  1311.     return _parent;
  1312.   else
  1313.     {
  1314.       if( n->is_leaf() )  // is leaf, so that is the previous node
  1315. return n;
  1316.       else if( n->children() )  // is branch with some children, so previous node is last child
  1317. return( n->last() );
  1318.       else  // is branch with no children, so that is the previous node
  1319. return n;
  1320.     }
  1321. }
  1322. Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: previous_branch()
  1323. {
  1324.   Node *n = previous();
  1325.   while( n )
  1326.     {
  1327.       if( n->is_branch() )
  1328. return n;
  1329.       else
  1330. n = n->previous();
  1331.     }
  1332.   return NULL;
  1333. }
  1334. Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: previous_leaf()
  1335. {
  1336.   Node *n = previous();
  1337.   while( n )
  1338.     {
  1339.       if( n->is_leaf() )
  1340. return n;
  1341.       else
  1342. n = n->previous();
  1343.     }
  1344.   return NULL;
  1345. }
  1346. void Flu_Tree_Browser :: Node :: determineVisibility( bool parentVisible )
  1347. {
  1348.   if( _widget )
  1349.     {
  1350.       if( parentVisible )
  1351. _widget->w->show();
  1352.       else
  1353. _widget->w->hide();
  1354.     }
  1355.   for( int i = 0; i < _children.size(); i++ )
  1356.     _children.child(i)->determineVisibility( parentVisible && open() );
  1357. }
  1358. Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: child( int i ) const
  1359. {
  1360.   if( i < 0 || i >= _children.size() )
  1361.     return 0;
  1362.   else
  1363.     return _children.child(i);
  1364. }
  1365. void Flu_Tree_Browser :: Node :: clear()
  1366. {
  1367.   widget(NULL);
  1368.   for( int i = 0; i < _children.size(); i++ )
  1369.     {
  1370.       //if( tree->rdata.cbNode == _children.child(i) )
  1371.       //tree->rdata.cbNode = NULL;
  1372.       delete _children.child(i);
  1373.     }
  1374.   _children.clear();
  1375. }
  1376. void Flu_Tree_Browser :: Node :: print( int spaces )
  1377. {
  1378.   for( int s = 0; s < spaces; s++ )
  1379.     printf( " " );
  1380.   if( is_leaf() )
  1381.     printf( "  %sn", text.c_str() );
  1382.   else
  1383.     printf( "[%s]n", text.c_str() );
  1384.   for( int i = 0; i < _children.size(); i++ )
  1385.     _children.child(i)->print( spaces+2 );
  1386. }
  1387. void Flu_Tree_Browser :: draw()
  1388. {
  1389.   if( rdata.forceResize )
  1390.     {
  1391.       resize( x(), y(), w(), h() );
  1392.       rdata.forceResize = false;
  1393.     }
  1394.   // draw the background color
  1395.   fl_draw_box( FL_FLAT_BOX, _box->x(), _box->y(), _box->w(), _box->h(), _box->color() );
  1396.   int dx = Fl::box_dx(box()), dy = Fl::box_dy(box());
  1397.   // set up the recursive data structure
  1398.   rdata.x = x()+dx; rdata.y = y()+dy;
  1399.   // account for the positions of the scrollbars
  1400.   if( scrollH->visible() )
  1401.     rdata.x -= scrollH->value();
  1402.   if( scrollV->visible() )
  1403.     rdata.y -= scrollV->value();
  1404.   rdata.first = true;
  1405.   rdata.last = true;
  1406.   rdata.bgColor = _box->color();
  1407.   rdata.shadedIndex = 0;
  1408.   // pick the connector line and selection colors depending on the active state
  1409.   if( active() )
  1410.     {
  1411.       rdata.lineColor = rdata.defLineColor;
  1412.       rdata.selectionColor = rdata.defSelectionColor;
  1413.     }
  1414.   else
  1415.     {
  1416.       rdata.lineColor = fl_inactive( rdata.defLineColor );
  1417.       rdata.selectionColor = fl_inactive( rdata.defSelectionColor );
  1418.     }
  1419.   // draw the tree
  1420.   fl_push_clip( _box->x(), _box->y(), _box->w(), _box->h() );
  1421.   root.recurse( rdata, Node::DRAW );
  1422.   // if dragging to move, draw a bar showing where the dragged node will be inserted
  1423. #ifdef USE_FLU_DND
  1424.   if( dnd_is_dragging() && rdata.isMoveValid && rdata.dragging )
  1425.     {
  1426.       bool drawLine = false;
  1427.       if( dnd_event_is_text() )
  1428. drawLine = true;
  1429.       else if( dnd_is_data_type( "Flu_Tree_Browser" ) )
  1430. {
  1431.   if( !rdata.moveOnlySameGroup || ( rdata.grabbed->parent() == rdata.dragNode->parent() ) )
  1432.     drawLine = true;
  1433. }
  1434.       else if( dnd_is_data_type( "DND_Object" ) )
  1435. drawLine = true;
  1436.       if( drawLine && rdata.dragWhere != MOVE_INSIDE )
  1437. {
  1438.   fl_color( FL_RED );
  1439.   fl_line_style( FL_SOLID, 2 );
  1440.   fl_line( _box->x(), rdata.dragPos, _box->x()+_box->w(), rdata.dragPos );
  1441.   fl_line_style( 0 );
  1442. }
  1443.     }
  1444. #endif
  1445.   fl_pop_clip();
  1446.   // draw the kids
  1447.   draw_child( *scrollBox );
  1448.   draw_child( *scrollH );
  1449.   draw_child( *scrollV );
  1450.   // draw the box last so it's on top
  1451.   fl_draw_box( _box->box(), _box->x(), _box->y(), _box->w(), _box->h(), _box->color() );
  1452. }
  1453. inline void draw_T( int x, int y, int w, int h )
  1454. {
  1455.   int w2 = w >> 1;
  1456.   int h2 = h >> 1;
  1457.   fl_line( x+w2, y, x+w2, y+h );
  1458.   fl_line( x+w2, y+h2, x+w, y+h2 );
  1459. }
  1460. inline void draw_L( int x, int y, int w, int h )
  1461. {
  1462.   int w2 = w >> 1;
  1463.   int h2 = h >> 1;
  1464.   fl_line( x+w2, y, x+w2, y+h2 );
  1465.   fl_line( x+w2, y+h2, x+w, y+h2 );
  1466. }
  1467. inline void draw_Lflip( int x, int y, int w, int h )
  1468. {
  1469.   int w2 = w >> 1;
  1470.   int h2 = h >> 1;
  1471.   fl_line( x+w2, y+h, x+w2, y+h2 );
  1472.   fl_line( x+w2, y+h2, x, y+h2 );
  1473. }
  1474. inline void draw_Ldash( int x, int y, int w, int h )
  1475. {
  1476.   w = w >> 1;
  1477.   h = h >> 1;
  1478.   fl_line( x, y+h, x+w, y+h );
  1479. }
  1480. inline void draw_vert_dash( int x, int y, int w, int h )
  1481. {
  1482.   w = w >> 1;
  1483.   fl_line( x+w, y+(h>>1), x+w, y+h );
  1484. }
  1485. inline void draw_Rdash( int x, int y, int w, int h )
  1486. {
  1487.   h = h >> 1;
  1488.   fl_line( x+w, y+h, x+(w>>1), y+h );
  1489. }
  1490. void Flu_Tree_Browser :: Node :: draw( RData &rdata, bool measure )
  1491. {
  1492.   int which = open(); // i.e. which icon: open or closed?
  1493.   bool skipCollapser = is_root() && rdata.showRoot && ( CHECK(ALWAYS_OPEN) || rdata.allBranchesAlwaysOpen );
  1494.   int halfHGap = rdata.hGap >> 1, halfVGap = rdata.vGap >> 1;
  1495.   bool doDraw = !measure;
  1496.   int X = rdata.x;
  1497.   int Y = rdata.y;
  1498.   Fl_Color bgColor = rdata.shadedColors[rdata.shadedIndex], tColor = textColor, hilightColor = rdata.selectionColor;
  1499.   // pick the text color depending on the active state
  1500.   if( !rdata.tree->active() || !CHECK(ACTIVE))
  1501.     tColor = fl_inactive( tColor );
  1502.   if( doDraw )
  1503.     {
  1504.       // draw the background for the entry using the entry background color
  1505.       fl_draw_box( FL_FLAT_BOX, rdata.browserX, Y, rdata.browserW, currentH, bgColor );
  1506.       // if dragging to the inside of a branch, hilight that branch
  1507. #ifdef USE_FLU_DND
  1508.       if( tree->dnd_is_dragging() && rdata.isMoveValid && rdata.dragWhere == MOVE_INSIDE && rdata.dragNode == this )
  1509. {
  1510.   bgColor = FL_RED;
  1511.   tColor = fl_contrast( tColor, bgColor );
  1512.   hilightColor = rdata.bgColor;
  1513.   fl_draw_box( FL_FLAT_BOX, rdata.browserX, Y, rdata.browserW, currentH, bgColor );
  1514. }
  1515.       // if selected, draw a filled selection box and reverse the normal draw colors
  1516.       else if( CHECK(SELECTED) )
  1517. #else
  1518.       if( CHECK(SELECTED) )
  1519. #endif
  1520. {
  1521.   bgColor = rdata.selectionColor;
  1522.   tColor = fl_contrast( tColor, bgColor );
  1523.   hilightColor = rdata.bgColor;
  1524.   fl_draw_box( FL_FLAT_BOX, rdata.browserX, Y, rdata.browserW, currentH, bgColor );
  1525. }
  1526.       fl_color( rdata.lineColor );
  1527.       fl_line_style( rdata.lineStyle, rdata.lineWidth );
  1528.     }
  1529.   if( is_leaf() ) // draw leaves one way...
  1530.     {
  1531.       // draw the connectors
  1532.       if( doDraw && rdata.showConnectors && rdata.showBranches )
  1533. {
  1534.   if( rdata.last )
  1535.     draw_L( X-halfHGap, Y-halfVGap, rdata.branchIconW+rdata.hGap, currentH+rdata.vGap );
  1536.   else
  1537.     draw_T( X-halfHGap, Y-halfVGap, rdata.branchIconW+rdata.hGap, currentH+rdata.vGap );
  1538. }
  1539.       // account for leaf icon spacing
  1540.       if( rdata.showBranches )
  1541. {
  1542.   if( lIcon )
  1543.     X += rdata.collapseIcons[which]->w() + rdata.hGap;
  1544.   else
  1545.     X += rdata.collapseIcons[which]->w() + rdata.wGap;
  1546. }
  1547.       else 
  1548. X += rdata.wGap;
  1549.       // draw some more connectors
  1550.       if( doDraw && rdata.showConnectors && lIcon && rdata.showBranches )
  1551. draw_Ldash( X-halfHGap, Y-halfVGap, lIcon->w()+rdata.hGap, currentH+rdata.vGap );
  1552.       // draw the leaf icon
  1553.       if( lIcon )
  1554. {
  1555.   if( doDraw )
  1556.     lIcon->draw( X, Y+(currentH>>1)-(lIcon->h()>>1) );
  1557.   X += lIcon->w() + rdata.wGap;
  1558. }
  1559.     }
  1560.   else // ...and branches another
  1561.     {
  1562.       // force the root to the left if it has no visible children
  1563.       if( _parent == 0 && !CHECK(SOME_VISIBLE_CHILDREN) )
  1564. {
  1565.   skipCollapser = true;
  1566.   which = 0;
  1567. }
  1568.       if( !CHECK(SOME_VISIBLE_CHILDREN) && !rdata.showLeaves )
  1569. which = 0;
  1570.       // draw the connectors
  1571.       if( doDraw && !skipCollapser && rdata.showConnectors && rdata.showBranches )
  1572. {
  1573.   if( _parent==0 )
  1574.     {
  1575.       if( CHECK(SOME_VISIBLE_CHILDREN) )
  1576. draw_Rdash( X-halfHGap, Y-halfVGap, rdata.collapseIcons[which]->w()+4+rdata.hGap, currentH+rdata.vGap );
  1577.     }
  1578.   else if( rdata.last )
  1579.     draw_L( X-halfHGap, Y-halfVGap, rdata.branchIconW+rdata.hGap, currentH+rdata.vGap );
  1580.   else
  1581.     draw_T( X-halfHGap, Y-halfVGap, rdata.branchIconW+rdata.hGap, currentH+rdata.vGap );
  1582. }
  1583.       // draw the collapsed icons
  1584.       if( doDraw && !skipCollapser && !CHECK(ALWAYS_OPEN) && !rdata.allBranchesAlwaysOpen )
  1585. {
  1586.   if( CHECK(SOME_VISIBLE_CHILDREN) || rdata.showLeaves )
  1587.     {
  1588.       if( _parent==0 )
  1589. cIcon[which]->draw( X, Y+(currentH>>1)-(cIcon[which]->h()>>1) );
  1590.       else
  1591. cIcon[which]->draw( X+(rdata.branchIconW>>1)-(cIcon[which]->w()>>1), Y+(currentH>>1)-(cIcon[which]->h()>>1) );
  1592.     }
  1593. }
  1594.       if( !skipCollapser )
  1595. {
  1596.   X += cIcon[which]->w();
  1597.   if( bIcon[which] )
  1598.     X += rdata.hGap;
  1599.   else
  1600.     X += rdata.wGap;
  1601. }
  1602.       // draw some more connectors
  1603.       if( doDraw && rdata.showConnectors && rdata.showBranches )
  1604. {
  1605.   int hGap = rdata.hGap;
  1606.   if( bIcon[which] )
  1607.     hGap += bIcon[which]->w();
  1608.   if( skipCollapser && CHECK(SOME_VISIBLE_CHILDREN) )
  1609.     draw_vert_dash( X-halfHGap, Y-halfVGap, hGap, currentH+rdata.vGap );
  1610.   else if( !which || !CHECK(SOME_VISIBLE_CHILDREN) )
  1611.     draw_Ldash( X-halfHGap, Y-halfVGap, hGap, currentH+rdata.vGap );
  1612.   else
  1613.     draw_Lflip( X-halfHGap, Y-halfVGap, hGap, currentH+rdata.vGap );
  1614. }
  1615.       // draw the branch icon
  1616.       if( bIcon[which] )
  1617. {
  1618.   if( doDraw )
  1619.     bIcon[which]->draw( X, Y+(currentH>>1)-(bIcon[which]->h()>>1) );
  1620.   X += bIcon[which]->w() + rdata.wGap;
  1621. }
  1622.     }
  1623.   if( doDraw )
  1624.     fl_line_style( 0 );
  1625.   // draw the entry
  1626.   if( CHECK(SHOW_LABEL) )
  1627.     {
  1628.       if( doDraw )
  1629. {
  1630.   fl_draw_box( FL_FLAT_BOX, X, Y+(currentH>>1)-(textH>>1), textW, textH, bgColor );
  1631.   fl_color( tColor );
  1632.   fl_font( textFont, textSize );
  1633.   fl_draw( text.c_str(), X, Y+(currentH>>1)-(textH>>1), textW, textH, FL_ALIGN_LEFT );
  1634. }
  1635.       X += textW;
  1636.     }
  1637.   if( _widget )
  1638.     {
  1639.       int widgetW = _widget->w->w();
  1640.       int widgetH = _widget->w->h();
  1641.       if( doDraw )
  1642. {
  1643.   _widget->w->redraw();
  1644.   _widget->w->position( X, Y+(currentH>>1)-(widgetH>>1) );
  1645.   if( CHECK(EXPAND_TO_WIDTH) )
  1646.     _widget->w->size( MAX( _widget->defaultW, rdata.browserW - (X-rdata.browserX) ), _widget->w->h() );
  1647.   _widget->w->draw();
  1648. }
  1649.       if( CHECK(EXPAND_TO_WIDTH) )
  1650. {
  1651.   if( _widget->w->w() == _widget->defaultW )
  1652.     X += _widget->defaultW;
  1653. }
  1654.       else
  1655. X += widgetW;
  1656.     }
  1657.   // if hilighted, draw a box outlining the entry
  1658.   if( rdata.hilighted == this && doDraw )
  1659.     {
  1660.       fl_color( hilightColor );
  1661.       fl_line_style( FL_DOT, 1 );
  1662.       fl_rect( rdata.browserX, Y, rdata.browserW, currentH, hilightColor );
  1663.       fl_line_style( 0 );
  1664.     }
  1665.   rdata.totalW = MAX( rdata.totalW, X );
  1666. }
  1667. void Flu_Tree_Browser :: Node :: select( bool b )
  1668. {
  1669.   if( (CHECK(SELECTED)==b) && (tree->rdata.when != FL_WHEN_NOT_CHANGED) )
  1670.     return;
  1671.   SET(SELECTED,b);
  1672.   tree->redraw();
  1673.   if( tree->rdata.when == FL_WHEN_RELEASE )
  1674.     return;
  1675.   if( b )
  1676.     do_callback( FLU_SELECTED );
  1677.   else
  1678.     do_callback( FLU_UNSELECTED );
  1679. }
  1680. void Flu_Tree_Browser :: timerScrollCB()
  1681. {
  1682.   bool doRedraw = false;
  1683.   float val = scrollV->value() + autoScrollY;
  1684.   if( val < 0.0f )
  1685.     val = 0.0f;
  1686.   if( val > scrollV->maximum() )
  1687.     val = scrollV->maximum();
  1688.   doRedraw |= ( val != scrollV->value() );
  1689.   ((Fl_Valuator*)scrollV)->value( val );
  1690.   val = scrollH->value() + autoScrollX;
  1691.   if( val < 0.0f )
  1692.     val = 0.0f;
  1693.   if( val > scrollH->maximum() )
  1694.     val = scrollH->maximum();
  1695.   doRedraw |= ( val != scrollH->value() );
  1696.   ((Fl_Valuator*)scrollH)->value( val );
  1697.   Fl::repeat_timeout( 0.02, _timerScrollCB, this );
  1698.   scrolledTimerOn = true;
  1699.   if( doRedraw )
  1700.     redraw();
  1701. }
  1702. void Flu_Tree_Browser :: timerRedrawCB()
  1703. {
  1704.   if( rdata.animating )
  1705.     Fl::repeat_timeout( 1.0f/rdata.fps, _timerRedrawCB, this );
  1706.   redraw();
  1707. }
  1708. void Flu_Tree_Browser :: Node :: open( bool b )
  1709. {
  1710.   if( is_leaf() )
  1711.     return;
  1712.   if( CHECK(ALWAYS_OPEN) || tree->rdata.allBranchesAlwaysOpen )
  1713.     return;
  1714.   if( (open() == b) && (tree->rdata.when != FL_WHEN_NOT_CHANGED) )
  1715.     return;
  1716.   tree->rdata.justOpenedClosed = true;
  1717.   SET(COLLAPSED,!b);
  1718.   if( tree->rdata.animate )
  1719.     {
  1720.       // if we aren't yet animating a node, animate it!
  1721.       if( !tree->rdata.animating && !tree->rdata.animatedNode )
  1722. {
  1723.   // if we don't know how high all the children are, find out
  1724.   // (this only happens once per node, the first time it is opened)
  1725.   if( totalChildH == 0 )
  1726.     {
  1727.       RData r = tree->rdata;
  1728.       r.x = r.y = r.totalW = 0;
  1729.       recurse( r, Node::MEASURE_THIS_OPEN );
  1730.     }
  1731.   // set the initial offset based on whether the branch is open or closed
  1732.   tree->rdata.animationOffset = b ? -totalChildH : -1;
  1733.   // the delta is how much to change the offset each frame
  1734.   tree->rdata.animationDelta = totalChildH / ( tree->rdata.collapseTime * tree->rdata.fps );
  1735.   tree->rdata.animationDelta = b ? tree->rdata.animationDelta : -tree->rdata.animationDelta;
  1736.   tree->rdata.animating = true;
  1737.   tree->rdata.animatedNode = this;
  1738.   Fl::add_timeout( 1.0f/tree->rdata.fps, _timerRedrawCB, tree );
  1739. }
  1740.       // otherwise reverse the direction of the animation, only if we are animating this node
  1741.       else if( tree->rdata.animating && tree->rdata.animatedNode==this )
  1742. {
  1743.   if( b ^ (tree->rdata.animationDelta>0) )
  1744.     tree->rdata.animationDelta = -tree->rdata.animationDelta;
  1745. }
  1746.     }
  1747.   if( open() && (_parent != 0) ) // root node doesn't count as a single open branch
  1748.     {
  1749.       if( ( tree->rdata.lastOpenBranch != this ) && tree->rdata.singleBranchOpen )
  1750. tree->rdata.lastOpenBranch->close();
  1751.       tree->rdata.lastOpenBranch = this;
  1752.     }
  1753.   tree->rdata.forceResize = true;
  1754.   tree->rdata.visibilityChanged = true;
  1755.   if( b )
  1756.     do_callback( FLU_OPENED );
  1757.   else
  1758.     do_callback( FLU_CLOSED );
  1759. }
  1760. void Flu_Tree_Browser :: Node :: active( bool b )
  1761. {
  1762.   if( CHECK(ACTIVE) == b  &&  tree->rdata.when != FL_WHEN_NOT_CHANGED )
  1763.     return;
  1764.   SET( ACTIVE, b );
  1765.   if( _widget )
  1766.     {
  1767.       if( b )
  1768. _widget->w->activate();
  1769.       else
  1770. _widget->w->deactivate();
  1771.     }
  1772.   if( !CHECK(ACTIVE) )
  1773.     {
  1774.       if( tree->rdata.hilighted == this )
  1775. tree->set_hilighted( NULL );
  1776.       select( false );
  1777.       open( false );
  1778.     }
  1779. }
  1780. void Flu_Tree_Browser :: Node :: unselect_all( Node* except )
  1781. {
  1782.   if( this != except )
  1783.     select( false );
  1784.   for( int i = 0; i < _children.size(); i++ )
  1785.     _children.child(i)->unselect_all( except );
  1786. }
  1787. void Flu_Tree_Browser :: Node :: select_all()
  1788. {
  1789.   select( true );
  1790.   for( int i = 0; i < _children.size(); i++ )
  1791.     _children.child(i)->select_all();
  1792. }
  1793. bool Flu_Tree_Browser :: Node :: isMoveValid( Node* &n1, int &where, Node* &n2 )
  1794. {
  1795.   // if n1 is NULL, then check it as if it were a node being moved from another tree
  1796.   if( n2 == NULL )
  1797.     return false;
  1798.   // check the validity of the move:
  1799.   // 1) the source and destination nodes can't be the same
  1800.   // 2) you can't move before the root node
  1801.   // 3) you can't move an unmovable node or move a branch node such that it would become a descendent of itself
  1802.   // 4) if moving only within the same group, check that the parents are the same
  1803.   // 5) if moving into a sorted tree, the destination node MUST be a branch
  1804.   // 6) a move AFTER an OPEN branch is a move BEFORE its first child
  1805.   // 7) you can't move a node into a non-droppable branch node
  1806.   if( n1 == n2 )
  1807.     return false;
  1808.   if( where==MOVE_BEFORE && n2->is_root() )
  1809.     return false;
  1810.   if( n1 )
  1811.     {
  1812.       if( !n1->movable() )
  1813. return false;
  1814.       if( n1->is_branch() )
  1815. if( n1->is_descendent( n2 ) )
  1816.   return false;
  1817.     }
  1818.   bool sameGroup = n2->tree->move_only_same_group();
  1819.   if( sameGroup && n1 )
  1820.     {
  1821.       if( n1->parent() != n2->parent() || where==MOVE_INSIDE )
  1822. return false;
  1823.     }
  1824.   int iMode = n2->tree->insertion_mode();
  1825.   if( iMode == FLU_INSERT_SORTED || iMode == FLU_INSERT_SORTED_REVERSE )
  1826.     {
  1827.       if( n2->is_branch() )
  1828. {
  1829.   where = MOVE_INSIDE;
  1830.   return true;
  1831. }
  1832.       else
  1833. return false;
  1834.     }
  1835.   if( where==MOVE_AFTER && n2->is_branch() && n2->open() )
  1836.     {
  1837.       // can't move inside a branch if within the same group, unless the first node is dragged 
  1838.       // from outside the tree (in which case n1 is NULL)
  1839.       if( sameGroup && n1 )
  1840. {
  1841.   if( n2->_children.size() > 0 )
  1842.     return false;
  1843. }
  1844.       else if( n2->_children.size() > 0 )
  1845. {
  1846.   where = MOVE_BEFORE;
  1847.   n2 = n2->_children.child(0);
  1848. }
  1849.       else
  1850. where = MOVE_INSIDE;
  1851.     }
  1852.   if( where==MOVE_INSIDE )
  1853.     {
  1854.       if( !n2->droppable() )
  1855. return false;
  1856.     }
  1857.   else if( n2->parent() )
  1858.     if( !n2->parent()->droppable() )
  1859.       return false;
  1860.   return true;
  1861. }
  1862. int Flu_Tree_Browser :: Node :: recurse( RData &rdata, int type, int event )
  1863. {
  1864.   int i;
  1865.   if( type == COUNT_SELECTED )
  1866.     {
  1867.       if( is_leaf() )
  1868. return (int)CHECK(SELECTED);
  1869.       else
  1870. {
  1871.   int total = (int)CHECK(SELECTED);
  1872.   for( i = 0; i < _children.size(); i++ )
  1873.     total += _children.child(i)->recurse( rdata, type, event );
  1874.   return total;
  1875. }
  1876.     }
  1877.   // see if this entry is even visible
  1878.   if( rdata.y > rdata.browserY+rdata.browserH )
  1879.     {
  1880.       if( type == DRAW )
  1881. return 1;
  1882.       else if( type == HANDLE )
  1883. return 0;
  1884.     }
  1885.   int which = open();
  1886.   bool skipEntry = ( is_root() && !rdata.showRoot ) || ( is_leaf() && !rdata.showLeaves ) || ( is_branch() && !rdata.showBranches );
  1887.   bool skipCollapser = is_root() && rdata.showRoot && ( CHECK(ALWAYS_OPEN) || rdata.allBranchesAlwaysOpen );
  1888.   // find the size of the entry label
  1889.   if( (type == MEASURE) || (type == MEASURE_THIS_OPEN) )
  1890.     {
  1891.       if( CHECK(SHOW_LABEL) )
  1892. {
  1893.   int W = 0, H;
  1894.   fl_font( textFont, textSize );
  1895.   fl_measure( text.c_str(), W, H );
  1896.   W += 4; H += 4;  // hack - it looks better
  1897.   textW = W;
  1898.   textH = H;
  1899. }
  1900.       else
  1901. {
  1902.   textW = textH = 0;
  1903. }
  1904.       // remember vertically where this node is w.r.t the browser
  1905.       currentY = rdata.y;
  1906.       currentH = textH;
  1907.       // find the total size of the entry, depending on if there's a widget
  1908.       if( _widget )
  1909. currentH = MAX( _widget->w->h(), currentH );
  1910.       // find the total height of this entry by taking the max height of the entry and icons
  1911.       if( is_leaf() )
  1912. {
  1913.   if( lIcon )
  1914.     currentH = MAX( currentH, lIcon->h() );
  1915. }
  1916.       else
  1917. {
  1918.   currentH = MAX( currentH, cIcon[which]->h() );
  1919.   if( bIcon[which] )
  1920.     currentH = MAX( currentH, bIcon[which]->h() );
  1921. }
  1922.     }
  1923.   bool skipAhead = (rdata.y + currentH) < rdata.browserY;
  1924.   // process the entry
  1925.   switch( type )
  1926.     {
  1927.     case DRAW:
  1928.       {
  1929. if( skipEntry || skipAhead ) break;
  1930. draw( rdata, false );
  1931. // draw any vertical connectors connecting our aunts, great aunts, etc.,
  1932. if( rdata.showBranches )
  1933.   {
  1934.     int d = depth()-1;
  1935.     for( i = 0; i < rdata.branchConnectors.size(); i++ )
  1936.       {
  1937. if( i != d )
  1938.   {
  1939.     fl_color( rdata.lineColor );
  1940.     fl_line_style( rdata.lineStyle, rdata.lineWidth );
  1941.     fl_line( rdata.branchConnectors[i], rdata.y, rdata.branchConnectors[i], rdata.y+currentH );
  1942.     fl_line_style( 0 );
  1943.   }
  1944.       }
  1945.   }
  1946. rdata.shadedIndex = 1 - rdata.shadedIndex;  // toggle the even/odd entry for shading
  1947.       }
  1948.       break;
  1949.     case MEASURE:
  1950.       if( is_leaf() )
  1951. CLEAR( SOME_VISIBLE_CHILDREN );
  1952.       else
  1953. {
  1954.   // find out whether the branch has any children that could be visible
  1955.   bool someVisibleChildren = rdata.showLeaves && ( _children.size() > 0 );
  1956.   for( i = 0; i < _children.size(); i++ )
  1957.     {
  1958.       if( _children.child(i)->is_branch() )
  1959. {
  1960.   someVisibleChildren = true;
  1961.   break;
  1962. }
  1963.     }
  1964.   SET( SOME_VISIBLE_CHILDREN, someVisibleChildren );
  1965. }
  1966.     case MEASURE_THIS_OPEN:
  1967.       if( skipEntry ) break;
  1968.       draw( rdata, true );
  1969.       break;
  1970.     case HANDLE:
  1971.       {
  1972. if( skipEntry || skipAhead || !CHECK(ACTIVE) ) break;
  1973. if( event != FL_DRAG && event != FL_NO_EVENT )
  1974.   rdata.justOpenedClosed = false;
  1975. // if we are trying to select all entries between 2 widgets due to a shift-select...
  1976. if( rdata.shiftSelect )
  1977.   {
  1978.     if( (rdata.hilighted == this) || (rdata.grabbed == this) )
  1979.       {
  1980. if( !rdata.shiftSelectAll )
  1981.   {
  1982.     rdata.shiftSelectAll = true;
  1983.     select( true );
  1984.     if( is_branch() && rdata.openOnSelect )
  1985.       {
  1986. open( true );
  1987.       }
  1988.   }
  1989. else
  1990.   {
  1991.     rdata.shiftSelect = false;
  1992.     rdata.shiftSelectAll = false;
  1993.     rdata.grabbed = 0;
  1994.     select( true );
  1995.     if( is_branch() && rdata.openOnSelect )
  1996.       {
  1997. open( true );
  1998.       }
  1999.   }
  2000.       }
  2001.     else if( rdata.shiftSelectAll )
  2002.       {
  2003. select( true );
  2004. if( is_branch() && rdata.openOnSelect )
  2005.   {
  2006.     open( true );
  2007.   }
  2008.       }
  2009.     break;
  2010.   }
  2011. // check for the keyboard event
  2012. if( event == FL_KEYDOWN )
  2013.   {
  2014.     // check for the spacebar selecting this entry
  2015.     if( Fl::event_key() == ' ' && rdata.hilighted == this )
  2016.       {
  2017. if( Fl::event_state(FL_CTRL) )
  2018.   select( !CHECK(SELECTED) );
  2019. else
  2020.   {
  2021.     rdata.root->unselect_all( this );
  2022.     select( true );
  2023.   }
  2024. if( is_branch() && rdata.openOnSelect )
  2025.   {
  2026.     open( true );
  2027.   }
  2028. return 1;
  2029.       }
  2030.     // check for the enter key opening/closing this entry
  2031.     else if( (Fl::event_key() == FL_Enter) && (rdata.hilighted == this) )
  2032.       {
  2033. open( !open() );
  2034. return 1;
  2035.       }
  2036.     // check for the left/right cursor keys opening/closing this entry
  2037.     else if( (Fl::event_key() == FL_Left) && (rdata.hilighted == this) )
  2038.       {
  2039. open( false );
  2040. return 1;
  2041.       }
  2042.     else if( (Fl::event_key() == FL_Right) && (rdata.hilighted == this) )
  2043.       {
  2044. open( true );
  2045. return 1;
  2046.       }
  2047.   }
  2048. // check for the "up" cursor key moving the hilighted entry
  2049. if( rdata.delta == -1 && rdata.hilighted == this && rdata.previous != NULL )
  2050.   {
  2051.     tree->set_hilighted( rdata.previous );
  2052.     rdata.delta = 0;
  2053.     return 1;
  2054.   }
  2055. // check for the "down" cursor key moving the hilighted entry
  2056. if( rdata.delta == 1 && rdata.hilighted == rdata.previous )
  2057.   {
  2058.     tree->set_hilighted( this );
  2059.     rdata.delta = 0;
  2060.     return 1;
  2061.   }
  2062. rdata.previous = this;
  2063. // the event is not ours to use
  2064. //if( _widget && !rdata.dragging )
  2065. //if( Fl::event_inside( _widget->w ) )
  2066. //  return 2;
  2067. bool inExpander = false;
  2068. if( is_branch() )
  2069.   {
  2070.     int which = open();
  2071.     if( _parent==0 )
  2072.       inExpander = Fl::event_inside( rdata.x, rdata.y+(currentH>>1)-(cIcon[which]->h()>>1),
  2073.      cIcon[which]->w(), cIcon[which]->h() );
  2074.     else
  2075.       inExpander = Fl::event_inside( rdata.x+(rdata.branchIconW>>1)-(cIcon[which]->w()>>1),
  2076.      rdata.y+(currentH>>1)-(cIcon[which]->h()>>1),
  2077.      cIcon[which]->w(), cIcon[which]->h() );
  2078.   }
  2079. if( event == FL_PUSH )
  2080.   {
  2081.     // check for expand/collapse
  2082.     if( Fl::event_button() == FL_LEFT_MOUSE && inExpander )
  2083.       {
  2084. /*
  2085. if( is_branch() )
  2086.   {
  2087.     bool c = false;
  2088.     int which = open();
  2089.     if( _parent==0 )
  2090.       {
  2091. c = Fl::event_inside( rdata.x, rdata.y+(currentH>>1)-(cIcon[which]->h()>>1),
  2092.       cIcon[which]->w(), cIcon[which]->h() );
  2093.       }
  2094.     else
  2095.       c = Fl::event_inside( rdata.x+(rdata.branchIconW>>1)-(cIcon[which]->w()>>1),
  2096.     rdata.y+(currentH>>1)-(cIcon[which]->h()>>1),
  2097.     cIcon[which]->w(), cIcon[which]->h() );
  2098.     if( inExpander )
  2099.     {*/
  2100. open( !open() );
  2101. rdata.dragging = false;
  2102. rdata.dragNode = 0;
  2103. return 1;
  2104. //}
  2105. //}
  2106.       }
  2107.   }
  2108. if( event == FL_DRAG && rdata.justOpenedClosed )
  2109.   return 0;
  2110. // if no selections, return
  2111. if( rdata.selectionMode == FLU_NO_SELECT )
  2112.   break;
  2113. // if the event is not inside us, return
  2114. if( !Fl::event_inside( rdata.browserX, rdata.y, rdata.browserW, currentH ) )
  2115.   break;
  2116. #ifdef USE_FLU_DND
  2117. // check for grabbing of a node for DND
  2118. if( event == FL_DRAG && rdata.selectionDragMode == FLU_DRAG_TO_MOVE && !is_root() && rdata.grabbed &&
  2119.     //rdata.insertionMode!=FLU_INSERT_SORTED && rdata.insertionMode!=FLU_INSERT_SORTED_REVERSE &&
  2120.     !tree->dnd_is_dragging() && !rdata.justOpenedClosed && CHECK(MOVABLE) )
  2121.   {
  2122.     tree->dnd_grab( this, "Flu_Tree_Browser" );
  2123.     return 1;
  2124.   }
  2125. // dragging to move a node
  2126. if( event == FL_DND_DRAG )
  2127.   {
  2128.     rdata.dragNode = this; // remember which node to move the grabbed node before/after
  2129.     if( is_root() )
  2130.       {
  2131. rdata.dragWhere = MOVE_AFTER;
  2132. rdata.dragPos = rdata.y + currentH;
  2133.       }
  2134.     else
  2135.       {
  2136. // if this is a leaf or an open branch, then can only move before or after
  2137. // otherwise can move inside
  2138. if( is_branch() && !open() )
  2139.   {
  2140.     int t = MAX( currentH / 3, 1 );
  2141.     if( (Fl::event_y()-rdata.y) <= t )
  2142.       rdata.dragWhere = MOVE_BEFORE;
  2143.     else if( (Fl::event_y()-rdata.y) <= (t<<1) )
  2144.       rdata.dragWhere = MOVE_INSIDE;
  2145.     else
  2146.       rdata.dragWhere = MOVE_AFTER;
  2147.   }
  2148. else
  2149.   {
  2150.     if( (Fl::event_y()-rdata.y) <= (currentH>>1) )
  2151.       rdata.dragWhere = MOVE_BEFORE;
  2152.     else
  2153.       rdata.dragWhere = MOVE_AFTER;
  2154.   }
  2155. // where to draw the insertion position?
  2156. if( rdata.dragWhere == MOVE_BEFORE || rdata.dragWhere == MOVE_INSIDE )
  2157.   rdata.dragPos = rdata.y;
  2158. else
  2159.   rdata.dragPos = rdata.y + currentH;
  2160.       }
  2161.     return 1;
  2162.   }
  2163. #endif
  2164. // single selection
  2165. if( rdata.selectionMode == FLU_SINGLE_SELECT )
  2166.   {
  2167.     if( event == FL_PUSH )
  2168.       {
  2169. //rdata.dragging = true;
  2170. rdata.grabbed = this;
  2171. rdata.root->unselect_all( this );
  2172. tree->set_hilighted( this );
  2173. if( Fl::event_state(FL_CTRL) )
  2174.   select( !CHECK(SELECTED) );
  2175. else
  2176.   select( true );
  2177. if( is_leaf() )
  2178.   {
  2179.     if( Fl::event_clicks() > 0 )
  2180.       {
  2181. Fl::event_clicks(0);
  2182. do_callback( FLU_DOUBLE_CLICK );
  2183.       }
  2184.   }
  2185. else
  2186.   {
  2187.     if( Fl::event_clicks() > 0 )
  2188.       {
  2189. Fl::event_clicks(0);
  2190. if( rdata.doubleClickToOpen )
  2191.   open( !open() );
  2192. else
  2193.   do_callback( FLU_DOUBLE_CLICK );
  2194.       }
  2195.     else if( rdata.openOnSelect )
  2196.       {
  2197. open( true );
  2198.       }
  2199.   }
  2200. return 1;
  2201.       }
  2202.     else if( event == FL_DRAG )
  2203.       {
  2204. rdata.dragging = true;
  2205. //if( ( rdata.selectionDragMode == FLU_DRAG_IGNORE || rdata.selectionDragMode == FLU_DRAG_TO_MOVE) && ( tree->insertion_mode() == FLU_INSERT_FRONT || tree->insertion_mode() == FLU_INSERT_BACK ) )
  2206. //return 1;
  2207. rdata.root->unselect_all( this );
  2208. tree->set_hilighted( this );
  2209. select( true );
  2210. return 1;
  2211.       }
  2212.     else if( event == FL_RELEASE && rdata.when == FL_WHEN_RELEASE && selected() && !inExpander )
  2213.       {
  2214. do_callback( FLU_SELECTED );
  2215. return 1;
  2216.       }
  2217.   }
  2218. // multiple selection
  2219. else if( rdata.selectionMode == FLU_MULTI_SELECT )
  2220.   {
  2221.     if( event == FL_PUSH )
  2222.       {
  2223. //rdata.dragging = true;
  2224. rdata.grabbed = this;
  2225. if( Fl::event_state(FL_CTRL) )
  2226.   {
  2227.     select( !CHECK(SELECTED) );
  2228.     tree->set_hilighted( this );
  2229.   }
  2230. else if( Fl::event_state(FL_SHIFT) )
  2231.   {
  2232.     // select everything from the last selected entry to this one
  2233.     if( rdata.hilighted == this )
  2234.       {
  2235. select( true );
  2236. if( is_branch() )
  2237.   {
  2238.     if( Fl::event_clicks() > 0 )
  2239.       {
  2240. Fl::event_clicks(0);
  2241. if( rdata.doubleClickToOpen )
  2242.   open( !open() );
  2243. else
  2244.   do_callback( FLU_DOUBLE_CLICK );
  2245.       }
  2246.     else if( rdata.openOnSelect )
  2247.       {
  2248. open( !open() );
  2249.       }
  2250.   }
  2251.       }
  2252.     else
  2253.       {
  2254. rdata.shiftSelectAll = false;
  2255. rdata.shiftSelect = true;
  2256. rdata.grabbed = this;
  2257. rdata.root->recurse( rdata, HANDLE, 0 );
  2258. tree->set_hilighted( this );
  2259.       }
  2260.   }
  2261. else
  2262.   {
  2263.     rdata.root->unselect_all( this );
  2264.     select( true );
  2265.     if( is_leaf() )
  2266.       {
  2267. if( Fl::event_clicks() > 0 )
  2268.   {
  2269.     Fl::event_clicks(0);
  2270.     do_callback( FLU_DOUBLE_CLICK );
  2271.   }
  2272.       }
  2273.     else
  2274.       {
  2275. if( Fl::event_clicks() > 0 )
  2276.   {
  2277.     Fl::event_clicks(0);
  2278.     if( rdata.doubleClickToOpen )
  2279.       open( !open() );
  2280.     else
  2281.       do_callback( FLU_DOUBLE_CLICK );
  2282.   }
  2283. else if( rdata.openOnSelect )
  2284.   {
  2285.     open( true );
  2286.   }
  2287.       }
  2288.     tree->set_hilighted( this );
  2289.   }
  2290. return 1;
  2291.       }
  2292.     else if( event == FL_DRAG )
  2293.       {
  2294. rdata.dragging = true;
  2295. //if( ( rdata.selectionDragMode == FLU_DRAG_IGNORE || rdata.selectionDragMode == FLU_DRAG_TO_MOVE) && ( tree->insertion_mode() == FLU_INSERT_FRONT || tree->insertion_mode() == FLU_INSERT_BACK ) )
  2296. //return 1;
  2297. select( true );
  2298. tree->set_hilighted( this );
  2299. return 1;
  2300.       }
  2301.     else if( event == FL_RELEASE && rdata.when == FL_WHEN_RELEASE && selected() && !inExpander )
  2302.       {
  2303. do_callback( FLU_SELECTED );
  2304. return 1;
  2305.       }
  2306.   }
  2307.       }
  2308.       break;
  2309.     }
  2310.   // advance the counters vertically to the next entry
  2311.   if( !skipEntry )
  2312.     rdata.y += currentH + rdata.vGap;
  2313.   // if we're a leaf, no need to process further
  2314.   if( is_leaf() )
  2315.     return 0;
  2316.   // should we bail out already if we're done processing?
  2317.   if( closed() && !skipEntry && !skipCollapser && tree->rdata.animatedNode!=this && ( type != MEASURE_THIS_OPEN ) )
  2318.     return 0;
  2319.   if( !CHECK(SOME_VISIBLE_CHILDREN) )
  2320.     return 0;
  2321.   // advance the counters horizontally to the next entry
  2322.   if( rdata.showBranches )
  2323.     {
  2324.       if( !skipEntry && !skipCollapser )
  2325. rdata.x += cIcon[which]->w() + rdata.hGap;
  2326.     }
  2327.   rdata.totalW = MAX( rdata.totalW, rdata.x );
  2328.   // the branchIconW is the width of the branch icon at this level
  2329.   // it is used to center all children icons under the branch icon
  2330.   int lastBranchIconW = rdata.branchIconW;
  2331.   if( rdata.showBranches )
  2332.     {
  2333.       if( bIcon[which] )
  2334. rdata.branchIconW = bIcon[which]->w();
  2335.       else
  2336. rdata.branchIconW = cIcon[which]->w();
  2337.     }
  2338.   else
  2339.     rdata.branchIconW = 0;
  2340.   // update the animation
  2341.   if( tree->rdata.animatedNode==this && ( type == DRAW ) )
  2342.     {
  2343.       // check for termination (if opening)
  2344.       if( (rdata.animationOffset+rdata.animationDelta) >= 0.0f )
  2345. {
  2346.   tree->rdata.animatedNode = NULL;
  2347.   rdata.animating = false;
  2348.   tree->rdata.forceResize = true;
  2349.   Fl::remove_timeout( _timerRedrawCB, tree );
  2350. }
  2351.       else
  2352. {
  2353.   // update the offset
  2354.   rdata.animationOffset += rdata.animationDelta;
  2355.   fl_push_clip( rdata.browserX, rdata.y, rdata.browserW, rdata.browserH );
  2356.   rdata.y += (int)rdata.animationOffset;
  2357. }
  2358.     }
  2359.   if( ( type == MEASURE ) || ( type == MEASURE_THIS_OPEN ) )
  2360.     totalChildH = rdata.y;
  2361.   // process all children
  2362.   int val;
  2363.   int tempW = rdata.branchIconW >> 1;
  2364.   for( i = 0; i < _children.size(); i++ )
  2365.     {
  2366.       // prepare the recursive data structure for the next level
  2367.       if( !skipEntry )
  2368. rdata.first = false;
  2369.       rdata.last = (i == _children.size()-1 );
  2370.       // if child "i" is not the last child,
  2371.       // then there is a long connector that needs drawn between this node and the last child.
  2372.       // push the horizontal position of the connector onto the stack
  2373.       if( (type == DRAW) && rdata.showConnectors && ( i < _children.size()-1 ) )
  2374. {
  2375.   rdata.branchConnectors.push( rdata.x+tempW );
  2376.   val = _children.child(i)->recurse( rdata, type, event );
  2377.   rdata.branchConnectors.pop();
  2378. }
  2379.       else
  2380. val = _children.child(i)->recurse( rdata, type, event );
  2381.       if( val )
  2382. return val;
  2383.     }
  2384.   // set the branch icon width back to what it was before we changed it
  2385.   rdata.branchIconW = lastBranchIconW;
  2386.   if( ( type == MEASURE ) || ( type == MEASURE_THIS_OPEN ) )
  2387.     totalChildH = rdata.y - totalChildH;
  2388.   // update the animation
  2389.   if( tree->rdata.animatedNode==this && ( type == DRAW ) )
  2390.     {
  2391.       fl_pop_clip();
  2392.       // check for termination (if closing)
  2393.       if( rdata.animationOffset <= (float)(-totalChildH) )
  2394. {
  2395.   tree->rdata.animatedNode = NULL;
  2396.   rdata.animating = false;
  2397.   tree->rdata.forceResize = true;
  2398.   Fl::remove_timeout( _timerRedrawCB, tree );
  2399. }
  2400.     }
  2401.   // move back horizontally from the last entry
  2402.   if( rdata.showBranches )
  2403.     {
  2404.       if( !skipEntry && !skipCollapser )
  2405. rdata.x -= cIcon[which]->w() + rdata.hGap;
  2406.     }
  2407.   return 0;
  2408. }
  2409. void Flu_Tree_Browser :: print()
  2410. {
  2411.   root.print();
  2412. }
  2413. void Flu_Tree_Browser :: clear()
  2414. {
  2415.   root.clear();
  2416.   root.text = "";
  2417.   rdata.hilighted = NULL;
  2418.   rdata.dragging = false;
  2419.   rdata.forceResize = true;
  2420.   rdata.lastOpenBranch = NULL;
  2421.   rdata.shiftSelect = false;
  2422.   rdata.shiftSelectAll = false;
  2423.   rdata.nextId = 1;
  2424.   rdata.searchIndex = 1;
  2425. }
  2426. Flu_Tree_Browser::Node* Flu_Tree_Browser :: set_root( const char *label, Fl_Widget *w, bool showLabel )
  2427. {
  2428.   if( label == 0 )
  2429.     label = "";
  2430.   root.text = label;
  2431.   root.widget( w );
  2432.   root.SET(Node::SHOW_LABEL,showLabel);
  2433.   root.cIcon[0] = rdata.collapseIcons[0];
  2434.   root.cIcon[1] = rdata.collapseIcons[1];
  2435.   root.bIcon[0] = rdata.branchIcons[0];
  2436.   root.bIcon[1] = rdata.branchIcons[1];
  2437.   root.textColor = rdata.defBranchColor;
  2438.   root.textFont = rdata.defBranchFont;
  2439.   root.textSize = rdata.defBranchSize;
  2440.   rdata.forceResize = true;
  2441.   return &root;
  2442. }
  2443. Flu_Tree_Browser::Node* Flu_Tree_Browser :: add( const char* fullpath, Fl_Widget *w, bool showLabel )
  2444. {
  2445.   return( root.modify( fullpath, Node::ADD, rdata, w, showLabel ) );
  2446. }
  2447. Flu_Tree_Browser::Node* Flu_Tree_Browser :: add( const char* path, const char* text, Fl_Widget *w, bool showLabel )
  2448. {
  2449.   // if the path does not end in '/', add it
  2450.   FluSimpleString s = path;
  2451.   if( path[strlen(path)-1] != '/' )
  2452.     s += "/";
  2453.   s += text;
  2454.   return add( s.c_str(), w, showLabel );
  2455. }
  2456. Flu_Tree_Browser::Node* Flu_Tree_Browser :: add_branch( const char* fullpath, Fl_Widget *w, bool showLabel )
  2457. {
  2458.   FluSimpleString p( fullpath );
  2459.   if( p.size() && p[p.size()-1] != '/' && p[p.size()-1] != '\' ) p += "/";
  2460.   return add( p.c_str(), w, showLabel );
  2461. }
  2462. Flu_Tree_Browser::Node* Flu_Tree_Browser :: add_branch( const char* path, const char* name, Fl_Widget *w, bool showLabel )
  2463. {
  2464.   FluSimpleString p( name );
  2465.   if( p.size() && p[p.size()-1] != '/' && p[p.size()-1] != '\' ) p += "/";
  2466.   return add( path, p.c_str(), w, showLabel );
  2467. }
  2468. Flu_Tree_Browser::Node* Flu_Tree_Browser :: add_leaf( const char* fullpath, Fl_Widget *w, bool showLabel )
  2469. {
  2470.   FluSimpleString p( fullpath );
  2471.   if( p.size() && ( p[p.size()-1] == '/' || p[p.size()-1] == '\' ) ) p[p.size()-1] = '';
  2472.   return add( p.c_str(), w, showLabel );
  2473. }
  2474. Flu_Tree_Browser::Node* Flu_Tree_Browser :: add_leaf( const char* path, const char* name, Fl_Widget *w, bool showLabel )
  2475. {
  2476.   FluSimpleString p( name );
  2477.   if( p.size() && ( p[p.size()-1] == '/' || p[p.size()-1] == '\' ) ) p[p.size()-1] = '';
  2478.   return add( path, p.c_str(), w, showLabel );
  2479. }
  2480. unsigned int Flu_Tree_Browser :: remove( const char *fullpath )
  2481. {
  2482.   return( reinterpret_cast<long>(root.modify( fullpath, Node::REMOVE, rdata ) ));
  2483. }
  2484. unsigned int Flu_Tree_Browser :: remove( const char *path, const char *text )
  2485. {
  2486.   // if the path does not end in '/', add it
  2487.   FluSimpleString s = path;
  2488.   if( path[strlen(path)-1] != '/' )
  2489.     s += "/";
  2490.   s += text;
  2491.   return remove( s.c_str() );
  2492. }
  2493. unsigned int Flu_Tree_Browser :: remove( unsigned int id )
  2494. {
  2495.   return root.remove( id );
  2496. }
  2497. unsigned int Flu_Tree_Browser :: Node :: remove( unsigned int id )
  2498. {
  2499.   if( id == 0 )
  2500.     return 0;
  2501.   for( int i = 0; i < _children.size(); i++ )
  2502.     {
  2503.       Node *n = _children.child(i);
  2504.       if( n->id() == id )
  2505. {
  2506.   _children.erase( i );
  2507.   tree->rdata.forceResize = true;
  2508.   //if( tree->rdata.cbNode == n )
  2509.   //tree->rdata.cbNode = NULL;
  2510.   delete n;
  2511.   if( tree->rdata.autoBranches )
  2512.     initType();
  2513.   return id;
  2514. }
  2515.       else if( n->remove( id ) )
  2516. return id;
  2517.     }
  2518.   return 0;
  2519. }
  2520. unsigned int Flu_Tree_Browser :: remove( Fl_Widget *w )
  2521. {
  2522.   return root.remove( w );
  2523. }
  2524. unsigned int Flu_Tree_Browser :: Node :: remove( Fl_Widget *w )
  2525. {
  2526.   if( !w )
  2527.     return 0;
  2528.   for( int i = 0; i < _children.size(); i++ )
  2529.     {
  2530.       Node *n = _children.child(i);
  2531.       if( n->_widget )
  2532. {
  2533.   if( n->_widget->w == w )
  2534.     {
  2535.       int id = n->id();
  2536.       _children.erase( i );
  2537.       tree->rdata.forceResize = true;
  2538.       //if( tree->rdata.cbNode == n )
  2539.       //tree->rdata.cbNode = NULL;
  2540.       delete n;
  2541.       if( tree->rdata.autoBranches )
  2542. initType();
  2543.       return id;
  2544.     }
  2545. }
  2546.       int id = n->remove( w );
  2547.       if( id )
  2548. return id;
  2549.     }
  2550.   return 0;
  2551. }
  2552. int Flu_Tree_Browser :: find_number( const char *fullpath )
  2553. {
  2554.   rdata.counter = 0;
  2555.   root.modify( fullpath, Node::FIND_NUMBER, rdata );
  2556.   return rdata.counter;
  2557. }
  2558. int Flu_Tree_Browser :: find_number( const char *path, const char *text )
  2559. {
  2560.   // if the path does not end in '/', add it
  2561.   FluSimpleString s = path;
  2562.   if( path[strlen(path)-1] != '/' )
  2563.     s += "/";
  2564.   s += text;
  2565.   return find_number( s.c_str() );
  2566. }
  2567. Flu_Tree_Browser::Node* Flu_Tree_Browser :: find_next( const char *fullpath, Node* startNode )
  2568. {
  2569.   // degenerate case: root node
  2570.   if( strcmp( fullpath, "/" ) == 0 )
  2571.     return &root;
  2572.   rdata.previous = startNode;
  2573.   return( root.modify( fullpath, Node::FIND, rdata ) );
  2574. }
  2575. Flu_Tree_Browser::Node* Flu_Tree_Browser :: find_next( const char *path, const char *text )
  2576. {
  2577.   // if the path does not end in '/', add it
  2578.   FluSimpleString s = path;
  2579.   if( path[strlen(path)-1] != '/' )
  2580.     s += "/";
  2581.   s += text;
  2582.   return find_next( s.c_str() );
  2583. }
  2584. Flu_Tree_Browser::Node* Flu_Tree_Browser :: find( const char *path, const char *text )
  2585. {
  2586.   // if the path does not end in '/', add it
  2587.   FluSimpleString s = path;
  2588.   if( path[strlen(path)-1] != '/' )
  2589.     s += "/";
  2590.   s += text;
  2591.   return find( s.c_str() );
  2592. }
  2593. Flu_Tree_Browser::Node* Flu_Tree_Browser :: find( unsigned int id )
  2594. {
  2595.   return root.find( id );
  2596. }
  2597. Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: find( unsigned int id )
  2598. {
  2599.   if( id == 0 )
  2600.     return NULL;
  2601.   if( _id == id )
  2602.     return this;
  2603.   for( int i = 0; i < _children.size(); i++ )
  2604.     {
  2605.       Node *n = _children.child(i)->find( id );
  2606.       if( n )
  2607. return n;
  2608.     }
  2609.   return NULL;
  2610. }
  2611. Flu_Tree_Browser::Node* Flu_Tree_Browser :: find( Fl_Widget *w )
  2612. {
  2613.   return root.find( w );
  2614. }
  2615. Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: find( Fl_Widget *w )
  2616. {
  2617.   if( _widget )
  2618.     if( _widget->w == w )
  2619.       return this;
  2620.   for( int i = 0; i < _children.size(); i++ )
  2621.     {
  2622.       Node *n = _children.child(i)->find( w );
  2623.       if( n )
  2624. return n;
  2625.     }
  2626.   return NULL;
  2627. }
  2628. bool Flu_Tree_Browser :: Node :: findPath( unsigned int id, RData &rdata )
  2629. {
  2630.   if( _id == id )
  2631.     {
  2632.       if( is_leaf() )
  2633. rdata.path += text;
  2634.       else
  2635. {
  2636.   rdata.path += text;
  2637.   rdata.path += "/";
  2638. }
  2639.       return true;
  2640.     }
  2641.   if( is_leaf() )
  2642.     return false;
  2643.   char *oldPath = strdup( rdata.path.c_str() );
  2644.   if( _parent != 0 )
  2645.     {
  2646.       rdata.path += text;
  2647.       rdata.path += "/";
  2648.     }
  2649.   for( int i = 0; i < _children.size(); i++ )
  2650.     {
  2651.       if( _children.child(i)->findPath( id, rdata ) )
  2652. {
  2653.   free( oldPath );
  2654.   return true;
  2655. }
  2656.     }
  2657.   rdata.path = oldPath;
  2658.   free( oldPath );
  2659.   return false;
  2660. }
  2661. bool Flu_Tree_Browser :: Node :: findPath( Fl_Widget *w, RData &rdata )
  2662. {
  2663.   if( _widget )
  2664.     if( _widget->w == w )
  2665.       {
  2666. if( is_leaf() )
  2667.   rdata.path += text;
  2668. else
  2669.   {
  2670.     rdata.path += text;
  2671.     rdata.path += "/";
  2672.   }
  2673. return true;
  2674.       }
  2675.   if( is_leaf() )
  2676.     return false;
  2677.   char *oldPath = strdup( rdata.path.c_str() );
  2678.   if( _parent != 0 )
  2679.     {
  2680.       rdata.path += text;
  2681.       rdata.path += "/";
  2682.     }
  2683.   for( int i = 0; i < _children.size(); i++ )
  2684.     {
  2685.       if( _children.child(i)->findPath( w, rdata ) )
  2686. {
  2687.   free( oldPath );
  2688.   return true;
  2689. }
  2690.     }
  2691.   rdata.path = oldPath;
  2692.   free( oldPath );
  2693.   return false;
  2694. }
  2695. const char* Flu_Tree_Browser :: find_path( unsigned int id )
  2696. {
  2697.   // degenerate case: the root is always id==0
  2698.   if( id == 0 )
  2699.     return "/";
  2700.   rdata.path = "/";
  2701.   if( root.findPath( id, rdata ) )
  2702.     return rdata.path.c_str();
  2703.   else
  2704.     return "";
  2705. }
  2706. const char* Flu_Tree_Browser :: find_path( Fl_Widget *w )
  2707. {
  2708.   rdata.path = "/";
  2709.   if( root.findPath( w, rdata ) )
  2710.     return rdata.path.c_str();
  2711.   else
  2712.     return "";
  2713. }
  2714. static char* remove_escape_chars( const char *str )
  2715. {
  2716.   // remove any escape characters
  2717.   char *text = strdup( str );
  2718.   int tIndex = 0;
  2719.   for( int pIndex = 0; pIndex < (int)strlen( str ); pIndex++ )
  2720.     {
  2721.       if( str[pIndex] != '\' )
  2722. text[tIndex++] = str[pIndex];
  2723.     }
  2724.   text[tIndex] = '';
  2725.   return text;
  2726. }
  2727. void Flu_Tree_Browser :: Node :: do_callback( int reason )
  2728. {
  2729.   if( tree->rdata.when == FL_WHEN_NEVER )
  2730.     return;
  2731.   if( tree->rdata.cb )
  2732.     {
  2733.       tree->rdata.cbReason = reason;
  2734.       tree->rdata.cbNode = this;
  2735.       tree->rdata.cb( tree, tree->rdata.cbd );
  2736.     }
  2737. }
  2738. unsigned short Flu_Tree_Browser :: Node :: depth() const
  2739. {
  2740.   int d = 0;
  2741.   Node *p = _parent;
  2742.   while( p )
  2743.     {
  2744.       d++;
  2745.       p = p->_parent;
  2746.     }
  2747.   return d;
  2748. }
  2749. Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: add_branch( const char* fullpath, Fl_Widget *w, bool showLabel )
  2750. {
  2751.   FluSimpleString p( fullpath );
  2752.   if( p.size() && p[p.size()-1] != '/' && p[p.size()-1] != '\' ) p += "/";
  2753.   return add( p.c_str(), w, showLabel );
  2754. }
  2755. Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: add_leaf( const char* fullpath, Fl_Widget *w, bool showLabel )
  2756. {
  2757.   FluSimpleString p( fullpath );
  2758.   if( p.size() && ( p[p.size()-1] == '/' || p[p.size()-1] == '\' ) ) p[p.size()-1] = '';
  2759.   return add( p.c_str(), w, showLabel );
  2760. }
  2761. Flu_Tree_Browser::Node* Flu_Tree_Browser :: Node :: modify( const char* path, int what, RData &rdata, Fl_Widget *w, bool showLabel )
  2762. {
  2763.   // find the selected entry at rdata.searchIndex among all selected entries
  2764.   if( what == GET_SELECTED )
  2765.     {
  2766.       if( CHECK(SELECTED) )
  2767. {
  2768.   rdata.counter++;
  2769.   if( rdata.counter == rdata.searchIndex )
  2770.     return this;
  2771. }
  2772.       for( int i = 0; i < _children.size(); i++ )
  2773. {
  2774.   Node *n = _children.child(i)->modify( path, what, rdata, w );
  2775.   if( n )
  2776.     return n;
  2777. }
  2778.       return NULL;
  2779.     }
  2780.   // trivial test for a bogus empty path
  2781.   if( path == 0 )
  2782.     return NULL;
  2783.   // if the path starts with '/', skip the '/'
  2784.   if( path[0] == '/' )
  2785.     path++;
  2786.   // trivial test for a bogus empty path
  2787.   if( path[0] == '' )
  2788.     return NULL;
  2789.   const char *remainingPath;
  2790.   char *nodeName;
  2791.   bool lastNode, branchNode;
  2792.   Node *retNode = NULL;
  2793.   ///////////// extract the next node name from the path ///////////////////
  2794.   // find the next '/' that is not preceded by the escape character ''
  2795.   const char *slash = strchr( path, '/' );
  2796.   for(;;)
  2797.     {
  2798.       // find the next '/'
  2799.       if( slash == NULL ) // there isn't one, so we're done
  2800. break;
  2801.       // test for escape character
  2802.       else if( slash[-1] == '\' ) // path[0] can never be '/', so this is a safe test
  2803. slash = strchr( slash+1, '/' );
  2804.       // we have it
  2805.       else
  2806. break;
  2807.     }
  2808.   // if there is no slash, then the node name is the path and it is a leaf and the last node in the path
  2809.   if( slash == NULL )
  2810.     {
  2811.       branchNode = false;
  2812.       char *name = strdup( path ); // copy the path
  2813.       nodeName = remove_escape_chars( name ); // remove the escape characters
  2814.       free( name );
  2815.       lastNode = true;
  2816.       remainingPath = NULL;
  2817.     }
  2818.   // 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
  2819.   else
  2820.     {
  2821.       branchNode = true;
  2822.       char *name = (char*)malloc( slash-path+1 );
  2823.       strncpy( name, path, slash-path );
  2824.       name[slash-path] = '';
  2825.       nodeName = remove_escape_chars( name ); // remove the escape characters
  2826.       free( name );
  2827.       lastNode = ( slash[1] == '' ); // this is the last node if there is nothing after the slash
  2828.       if( lastNode )
  2829. {
  2830.   //if( rdata.autoBranches )
  2831.   //branchNode = false;
  2832.   remainingPath = NULL;
  2833. }
  2834.       else
  2835. remainingPath = slash+1;
  2836.     }
  2837.   ///////////// process the node ///////////////////
  2838.   switch( what )
  2839.     {
  2840.     case ADD:
  2841.       {
  2842. // if the new node is a leaf node, add the string as a leaf and return
  2843. if( !branchNode )
  2844.   {
  2845.     // is there already a node with this name?
  2846.     Node *n = _children.find( nodeName );
  2847.     if( n )
  2848.       {
  2849. // if that node is a branch node, we can't add a new one with the same name
  2850. if( n->is_branch() )
  2851.   break;
  2852. // if we are not allowed to add multiple nodes with the same name,
  2853. // then just return
  2854. if( !rdata.allowDuplication )
  2855.   break;
  2856.       }
  2857.     // add a new node
  2858.     retNode = new Node( true, nodeName, this, rdata, w, showLabel );
  2859.     _children.add( retNode );
  2860.     rdata.forceResize = true;
  2861.     rdata.visibilityChanged = true;
  2862.     if( tree->rdata.autoBranches )
  2863.       initType();
  2864.   }
  2865. // otherwise make sure the node name exists as a branch and recurse on it
  2866. else
  2867.   {
  2868.     // if there is already a node with this name, just use it
  2869.     Node *n = NULL;
  2870.     n = _children.find( nodeName );
  2871.     if( n )
  2872.       {
  2873. // make sure it is a branch
  2874. if( n->is_leaf() )
  2875.   break;
  2876.       }
  2877.     // else add a new node
  2878.     if( n == NULL )
  2879.       {
  2880. // only add the widget for the last node
  2881. n = new Node( false, nodeName, this, rdata, lastNode?w:NULL, lastNode?showLabel:true );
  2882. _children.add( n );
  2883. rdata.forceResize = true;
  2884. rdata.visibilityChanged = true;
  2885.       }
  2886.     if( tree->rdata.autoBranches )
  2887.       initType();
  2888.     // recurse on the remainder of the path, if not the last node
  2889.     if( lastNode )
  2890.       retNode = n;
  2891.     else
  2892.       retNode = n->modify( remainingPath, what, rdata, w, showLabel );
  2893.   }
  2894.       }
  2895.       break;
  2896.     case REMOVE:
  2897.       {
  2898. // try to find the indicated node. if we can't find it, just return
  2899. Node *n = _children.find( nodeName );
  2900. if( !n )
  2901.   break;
  2902. // if this is the last node, remove it.
  2903. if( lastNode )
  2904.   {
  2905.     int ID = n->id();
  2906.     _children.erase( n );
  2907.     //if( tree->rdata.cbNode == n )
  2908.     //tree->rdata.cbNode = NULL;
  2909.     delete n;
  2910.     retNode = (Node*)ID; // non-null return value means remove was successful
  2911.     rdata.forceResize = true;
  2912.     rdata.visibilityChanged = true;
  2913.     if( tree->rdata.autoBranches )
  2914.       initType();
  2915.   }
  2916. // otherwise recurse on the remainder of the path
  2917. else
  2918.   retNode = n->modify( remainingPath, what, rdata, w, showLabel );
  2919.       }
  2920.       break;
  2921.     case FIND:
  2922.       {
  2923. // if this node equals the starting node for a find_next,
  2924. // then by clearing rdata.previous we flag that we are allowed to return the next match
  2925. if( rdata.previous == this )
  2926.   rdata.previous = NULL;
  2927. Node *n = NULL;
  2928. if( !lastNode )
  2929.   {
  2930.     // if, according to the path, this is not the last node, then just recursively
  2931.     // search for the named node
  2932.     n = _children.find( nodeName );
  2933.     if( !n )
  2934.       break;
  2935.     retNode = n->modify( remainingPath, what, rdata, w, showLabel );
  2936.   }
  2937. else
  2938.   {
  2939.     // otherwise, according to the path, this is the last node (i.e. a leaf).
  2940.     // since only leaves can have multiple identical entries,
  2941.     // try to find the indicated node, accounting for the possibility
  2942.     // that it may not be the one we're after
  2943.     int next = 1;
  2944.     for(;;)
  2945.       {
  2946. // look for the named node
  2947. n = _children.find( nodeName, next++ );
  2948. // if we can't find it, just return, because it's not here
  2949. if( !n )
  2950.   break;
  2951. // we are only allowed to return a match if the previous node is NULL,
  2952. // indicating we have passed the starting node for a find_next
  2953. if( rdata.previous == NULL )
  2954.   {
  2955.     retNode = n;
  2956.     break;
  2957.   }
  2958. // if the found node equals the starting node for a find_next,
  2959. // then by clearing rdata.previous we flag that we are allowed to return the next match
  2960. if( rdata.previous == n )
  2961.   rdata.previous = NULL;
  2962.       }
  2963.   }
  2964.       }
  2965.       break;
  2966.     case FIND_NUMBER:
  2967.       {
  2968. if( lastNode )  // can only match multiple leaves if the path says this is the last node
  2969.   {
  2970.     rdata.counter += _children.findNum( nodeName );
  2971.   }
  2972. else  // otherwise recurse down the remaining path
  2973.   {
  2974.     Node *n = _children.find( nodeName );
  2975.     n->modify( remainingPath, what, rdata, w, showLabel );
  2976.   }
  2977.       }
  2978.       break;
  2979.     }
  2980.   free( nodeName );
  2981.   return retNode;
  2982. }
  2983. void Flu_Tree_Browser :: Node :: widgetCB()
  2984. {
  2985.   if( _widget )
  2986.     {
  2987.       if( _widget->CB )
  2988. _widget->CB( _widget->w, _widget->CBData );
  2989.     }
  2990.   do_callback( FLU_WIDGET_CALLBACK );
  2991. }
  2992. void Flu_Tree_Browser :: Node :: widget( Fl_Widget *w )
  2993. {
  2994.   tree->rdata.forceResize = true;
  2995.   if( _widget )
  2996.     {
  2997.       Fl_Group *p = _widget->w->parent();
  2998.       if( p )
  2999. p->remove( *(_widget->w) );
  3000.       delete _widget->w;
  3001.       delete _widget;
  3002.       _widget = NULL;
  3003.     }
  3004.   if( !w )
  3005.     return;
  3006.   _widget = new WidgetInfo;
  3007.   _widget->w = w;
  3008.   _widget->defaultW = _widget->w->w();
  3009.   _widget->CB = _widget->w->callback();
  3010.   _widget->CBData = _widget->w->user_data();
  3011.   _widget->w->callback( _widgetCB, this );
  3012.   Fl_Group *parent = w->parent();
  3013.   if( parent )
  3014.     parent->remove( *w );
  3015.   tree->_box->add( w );
  3016. }
  3017. void Flu_Tree_Browser :: Node :: branch_icons( Fl_Image *closed, Fl_Image *open )
  3018. {
  3019.   if( is_branch() )
  3020.     {
  3021.       bIcon[0] = closed;
  3022.       bIcon[1] = open;
  3023.       tree->rdata.forceResize = true;
  3024.     }
  3025. }
  3026. void Flu_Tree_Browser :: Node :: collapse_icons( Fl_Image *closed, Fl_Image *open )
  3027. {
  3028.   if( is_branch() )
  3029.     {
  3030.       if( !closed || !open )
  3031. {
  3032.   cIcon[0] = tree->rdata.defaultCollapseIcons[0];
  3033.   cIcon[1] = tree->rdata.defaultCollapseIcons[1];
  3034. }
  3035.       else
  3036. {
  3037.   cIcon[0] = closed;
  3038.   cIcon[1] = open;
  3039. }
  3040.       tree->rdata.forceResize = true;
  3041.     }
  3042. }
  3043. void Flu_Tree_Browser :: Node :: leaf_icon( Fl_Image *icon )
  3044. {
  3045.   if( is_leaf() )
  3046.     {
  3047.       lIcon = icon;
  3048.       tree->rdata.forceResize = true;
  3049.     }
  3050. }
  3051. bool Flu_Tree_Browser :: Node :: is_branch() const
  3052. {
  3053.   if( tree->rdata.autoBranches )
  3054.     return( _children.size() != 0 );
  3055.   else
  3056.     return !CHECK(LEAF);
  3057. }
  3058. bool Flu_Tree_Browser :: Node :: is_leaf() const
  3059. {
  3060.   if( tree->rdata.autoBranches )
  3061.     return( _children.size() == 0 && !is_root() );
  3062.   else
  3063.     return CHECK(LEAF);
  3064. }