Index: src/Fl_Tree.cxx
===================================================================
--- src/Fl_Tree.cxx	(revision 7680)
+++ src/Fl_Tree.cxx	(working copy)
@@ -9,8 +9,6 @@
 #include <FL/Fl_Tree.H>
 #include <FL/Fl_Preferences.H>
 
-#define SCROLL_W 15
-
 //////////////////////
 // Fl_Tree.cxx
 //////////////////////
@@ -47,7 +45,7 @@
 static char **parse_path(const char *path) {
   while ( *path == '/' ) path++;	// skip leading '/' 
   // First pass: identify, null terminate, and count separators
-  int seps = 1;			// separator count (1: first item)
+  int seps = 1;				// separator count (1: first item)
   int arrsize = 1;			// array size (1: first item)
   char *save = strdup(path);		// make copy we can modify
   char *s = save;
@@ -61,7 +59,7 @@
   int t = 0;
   s = save;
   while ( seps-- > 0 ) {
-    if ( *s ) { arr[t++] = s; }	// skips empty fields, eg. '//'
+    if ( *s ) { arr[t++] = s; }		// skips empty fields, eg. '//'
     s += (strlen(s) + 1);
   }
   arr[t] = 0;
@@ -83,14 +81,16 @@
   _root->parent(0);				// we are root of tree
   _root->label("ROOT");
   _item_clicked = 0;
+  _item_focus = 0;
+  _scrollbar_size = 0;
   box(FL_DOWN_BOX);
   color(FL_WHITE);
   when(FL_WHEN_CHANGED);
-  _vscroll = new Fl_Scrollbar(0,0,0,0);	// will be resized by draw()
-  _vscroll->hide();
-  _vscroll->type(FL_VERTICAL);
-  _vscroll->step(1);
-  _vscroll->callback(scroll_cb, (void*)this);
+  vscroll = new Fl_Scrollbar(0,0,0,0);		// will be resized by draw()
+  vscroll->hide();
+  vscroll->type(FL_VERTICAL);
+  vscroll->step(1);
+  vscroll->callback(scroll_cb, (void*)this);
   end();
 }
 
@@ -167,8 +167,10 @@
   // Let group draw box+label but *NOT* children.
   // We handle drawing children ourselves by calling each item's draw()
   //
+  // Handle group's bg
   Fl_Group::draw_box();
   Fl_Group::draw_label();
+  // Handle tree
   if ( ! _root ) return;
   int cx = x() + Fl::box_dx(box());
   int cy = y() + Fl::box_dy(box());
@@ -177,13 +179,15 @@
   // These values are changed during drawing
   // 'Y' will be the lowest point on the tree
   int X = cx + _prefs.marginleft();
-  int Y = cy + _prefs.margintop() - (_vscroll->visible() ? _vscroll->value() : 0);
+  int Y = cy + _prefs.margintop() - (vscroll->visible() ? vscroll->value() : 0);
   int W = cw - _prefs.marginleft();		// - _prefs.marginright();
   int Ysave = Y;
   fl_push_clip(cx,cy,cw,ch);
   {
     fl_font(_prefs.labelfont(), _prefs.labelsize());
-    _root->draw(X, Y, W, this, _prefs);
+    _root->draw(X, Y, W, this,
+                (Fl::focus()==this)?_item_focus:0,	// show focus item ONLY if Fl_Tree has focus
+		_prefs);
   }
   fl_pop_clip();
   
@@ -192,150 +196,294 @@
   int ytoofar = (cy+ch) - Y;				// ytoofar -- scrolled beyond bottom (eg. stow)
   
   //printf("ydiff=%d ch=%d Ysave=%d ytoofar=%d value=%d\n",
-  //int(ydiff),int(ch),int(Ysave),int(ytoofar), int(_vscroll->value()));
+  //int(ydiff),int(ch),int(Ysave),int(ytoofar), int(vscroll->value()));
   
   if ( ytoofar > 0 ) ydiff += ytoofar;
-  if ( Ysave<cy || ydiff > ch || int(_vscroll->value()) > 1 ) {
-    _vscroll->visible();
-    int sx = x()+w()-Fl::box_dx(box())-SCROLL_W;
+  if ( Ysave<cy || ydiff > ch || int(vscroll->value()) > 1 ) {
+    vscroll->visible();
+
+    int scrollsize = _scrollbar_size ? _scrollbar_size : Fl::scrollbar_size();
+    int sx = x()+w()-Fl::box_dx(box())-scrollsize;
     int sy = y()+Fl::box_dy(box());
-    int sw = SCROLL_W;
+    int sw = scrollsize;
     int sh = h()-Fl::box_dh(box());
-    _vscroll->show();
-    _vscroll->range(0.0,ydiff-ch);
-    _vscroll->resize(sx,sy,sw,sh);
-    _vscroll->slider_size(float(ch)/float(ydiff));
+    vscroll->show();
+    vscroll->range(0.0,ydiff-ch);
+    vscroll->resize(sx,sy,sw,sh);
+    vscroll->slider_size(float(ch)/float(ydiff));
   } else {
-    _vscroll->Fl_Slider::value(0);
-    _vscroll->hide();
+    vscroll->Fl_Slider::value(0);
+    vscroll->hide();
   }
   fl_push_clip(cx,cy,cw,ch);
   Fl_Group::draw_children();	// draws any FLTK children set via Fl_Tree::widget()
   fl_pop_clip();
 }
 
+/// Returns the next visible item above (dir==Fl_Up) or below (dir==Fl_Down) \p item.
+/// If \p item is 0, first() (dir==Fl_Up) or last() (dir==FL_Down) is used.
+///
+/// \returns The new item, or 0 if there's no visible items above/below the specified \p item.
+///
+Fl_Tree_Item *Fl_Tree::next_visible_item(Fl_Tree_Item *item, int dir) {
+  if ( ! item ) {				// no start item?
+    item = ( dir == FL_Up ) ? last() : first();	// start at top or bottom
+    if ( ! item ) return(0);
+    if ( item->visible_r() ) return(item);	// return first/last visible item
+  }
+  switch ( dir ) {
+    case FL_Up:   return(item->prev_displayed(_prefs));
+    case FL_Down: return(item->next_displayed(_prefs));
+    default:      return(item->next_displayed(_prefs));
+  }
+}
+
+/// Returns the first selected item in the tree.
+///
+/// Use this to walk the tree looking for all the selected items, eg:
+/// \code
+/// for ( Fl_Tree_Item *item = tree->first_selected_item(); item; item = tree->next_selected_item(item) ) {
+///     printf("Item: %s\n", item->label());
+/// }
+/// \endcode
+///
+/// \returns The next selected item, or 0 if there are no more selected items.
+///     
+Fl_Tree_Item *Fl_Tree::first_selected_item() {
+  return(next_selected_item(0));
+}
+
+/// Returns the next selected item after \p item.
+/// If \p item is 0, search starts at the first item (root).
+///
+/// Use this to walk the tree looking for all the selected items, eg:
+/// \code
+/// for ( Fl_Tree_Item *item = tree->first_selected_item(); item; item = tree->next_selected_item(item) ) {
+///     printf("Item: %s\n", item->label());
+/// }
+/// \endcode
+///
+/// \returns The next selected item, or 0 if there are no more selected items.
+///     
+Fl_Tree_Item *Fl_Tree::next_selected_item(Fl_Tree_Item *item) {
+  if ( ! item ) {
+    if ( ! (item = first()) ) return(0);
+    if ( item->is_selected() ) return(item);
+  }
+  while ( (item = item->next()) )
+    if ( item->is_selected() )
+      return(item);
+  return(0);
+}
+
 /// Standard FLTK event handler for this widget.
+#include <FL/names.h>
 int Fl_Tree::handle(int e) {
+  int ret = 0;
+  // Developer note: Fl_Browser_::handle() used for reference here..
+  // fprintf(stderr, "DEBUG: %s (%d)\n", fl_eventnames[e], e);
+  if (e == FL_ENTER || e == FL_LEAVE) return(1);
+  switch (e) {
+    case FL_FOCUS: {
+      // FLTK tests if we want focus. 
+      //     If a nav key was used to give us focus, and we've got no saved
+      //     focus widget, determine which item gets focus depending on nav key.
+      //
+      if ( ! _item_focus ) {					// no focus established yet?
+	switch (Fl::event_key()) {				// determine if focus was navigated..
+	  case FL_Tab: {					// received focus via TAB?
+	    if ( Fl::event_state(FL_SHIFT) ) {			// SHIFT-TAB similar to FL_Up
+	      set_item_focus(next_visible_item(0, FL_Up));
+	    } else {						// TAB similar to FL_Down
+	      set_item_focus(next_visible_item(0, FL_Down));
+	    }
+	    break;
+	  }
+	  case FL_Left:		// received focus via LEFT or UP?
+	  case FL_Up:
+	  case 0xfe20: { 	// XK_ISO_Left_Tab
+	    set_item_focus(next_visible_item(0, FL_Up));
+	    break;
+	  }
+	  case FL_Right: 	// received focus via RIGHT or DOWN?
+	  case FL_Down:
+	  default: {
+	    set_item_focus(next_visible_item(0, FL_Down));
+	    break;
+	  }
+	}
+      }
+      if ( visible_focus() ) redraw();	// draw focus change
+      return(1);
+    }
+    case FL_UNFOCUS: {		// FLTK telling us some other widget took focus.
+      if ( visible_focus() ) redraw();	// draw focus change
+      return(1);
+    }
+    case FL_KEYBOARD: {		// keyboard shortcut
+      // Do shortcuts first or scrollbar will get them...
+      if (_prefs.selectmode() > FL_TREE_SELECT_NONE ) {
+	if ( !_item_focus ) {
+	  set_item_focus(first());
+	}
+	if ( _item_focus ) {
+	  int ekey = Fl::event_key();
+	  switch (ekey) {
+	    case FL_Enter:	// ENTER: selects current item only
+	    case FL_KP_Enter:
+	      if ( when() & ~FL_WHEN_ENTER_KEY) {
+		select_only(_item_focus);
+		return(1);
+	      }
+	      break;
+	    case ' ':		// toggle selection state
+	      switch ( _prefs.selectmode() ) {
+		case FL_TREE_SELECT_NONE:
+		  break;
+		case FL_TREE_SELECT_SINGLE:
+		  if ( ! _item_focus->is_selected() )		// not selected?
+		    select_only(_item_focus);			// select only this
+		  else
+		    deselect_all();				// select nothing
+		  break;
+		case FL_TREE_SELECT_MULTI:
+		  select_toggle(_item_focus);
+		  break;
+	      }
+	      break;
+	    case FL_Right:  	// open children (if any)
+	    case FL_Left: {	// close children (if any)
+	      if ( _item_focus ) {
+		if ( ekey == FL_Right && _item_focus->is_close() ) {
+		  // Open closed item
+		  _item_focus->open();
+		  redraw();
+		  ret = 1;
+		} else if ( ekey == FL_Left && _item_focus->is_open() ) {
+		  // Close open item
+		  _item_focus->close();
+		  redraw();	
+		  ret = 1;
+		}
+		return(1);
+	      }
+	      break;
+	    }
+	    case FL_Up:		// next item up
+	    case FL_Down: {	// next item down
+	      set_item_focus(next_visible_item(_item_focus, ekey));	// next item up|dn
+	      if ( _item_focus ) {					// item in focus?
+	        // Autoscroll
+		int itemtop = _item_focus->y();
+		int itembot = _item_focus->y()+_item_focus->h();
+		if ( itemtop < y() ) { show_item_top(_item_focus); fprintf(stderr, "ERCODEBUG: SHOW_ITEM_TOP\n"); }
+		if ( itembot > y()+h() ) { show_item_bottom(_item_focus); fprintf(stderr, "ERCODEBUG: SHOW_ITEM_BOT\n"); }
+		// Extend selection
+		if ( _prefs.selectmode() == FL_TREE_SELECT_MULTI &&	// multiselect on?
+		     (Fl::event_state() & FL_SHIFT) &&			// shift key?
+		     ! _item_focus->is_selected() ) {			// not already selected?
+		    select(_item_focus);				// extend selection..
+		}
+		return(1);
+	      }
+	      break;
+	    }
+	  }
+	}
+      }
+      break;
+    }
+  }
+
+  // Let Fl_Group take a shot at handling the event
+  if (Fl_Group::handle(e)) {
+    return(1);			// handled? don't continue below
+  }
+
+  // Handle events the child FLTK widgets didn't need
+
   static Fl_Tree_Item *lastselect = 0;
-  int changed = 0;
-  int ret = Fl_Group::handle(e);
+  // fprintf(stderr, "ERCODEBUG: Fl_Tree::handle(): Event was %s (%d)\n", fl_eventnames[e], e); // DEBUGGING
   if ( ! _root ) return(ret);
   switch ( e ) {
-    case FL_PUSH: {
+    case FL_PUSH: {					// clicked on a tree item?
+      if (Fl::visible_focus() && handle(FL_FOCUS)) {
+        Fl::focus(this);
+      }
       lastselect = 0;
-      item_clicked(0);				// assume no item was clicked
+      item_clicked(0);					// assume no item was clicked
       Fl_Tree_Item *o = _root->find_clicked(_prefs);
-      if ( o ) {
-        ret |= 1;				// handled
-        if ( Fl::event_button() == FL_LEFT_MOUSE ) {
-          // Was collapse icon clicked?
-          if ( o->event_on_collapse_icon(_prefs) ) {
-            o->open_toggle();
-            redraw();
-          }
-          // Item's label clicked?
-          else if ( o->event_on_label(_prefs) && 
-                   (!o->widget() || !Fl::event_inside(o->widget())) &&
-                   callback() &&
-                   (!_vscroll->visible() || !Fl::event_inside(_vscroll)) ) {
-            item_clicked(o);			// save item clicked
-
-            // Handle selection behavior
-            switch ( _prefs.selectmode() ) {
-              case FL_TREE_SELECT_NONE: {	// no selection changes
-                break;
-              }
-              case FL_TREE_SELECT_SINGLE: {
-                changed = select_only(o);
-                break;
-              }
-              case FL_TREE_SELECT_MULTI: {
-                int state = Fl::event_state();
-                if ( state & FL_SHIFT ) {
-                  if ( ! o->is_selected() ) {
-                    o->select();		// add to selection
-                    changed = 1;		// changed
-                  }
-                } else if ( state & FL_CTRL ) {
-                  changed = 1;			// changed
-                  o->select_toggle();		// toggle selection state
-                  lastselect = o;		// save we toggled it (prevents oscillation)
-                } else {
-                  changed = select_only(o);
-                }
-                break;
-              }
-            }
-
-            if ( changed ) {
-              redraw();						// make change(s) visible
-              if ( when() & FL_WHEN_CHANGED ) {
-                set_changed();
-                do_callback((Fl_Widget*)this, user_data());	// item callback
-              }
-            }
-          }
-        }
+      if ( ! o ) break;
+      set_item_focus(o);				// becomes new focus widget
+      redraw();
+      ret |= 1;						// handled
+      if ( Fl::event_button() == FL_LEFT_MOUSE ) {
+	if ( o->event_on_collapse_icon(_prefs) ) {	// collapse icon clicked?
+	  o->open_toggle();
+	} else if ( o->event_on_label(_prefs) && 	// label clicked?
+		 (!o->widget() || !Fl::event_inside(o->widget())) &&		// not inside widget
+		 (!vscroll->visible() || !Fl::event_inside(vscroll)) ) {	// not on scroller
+	  item_clicked(o);				// save item clicked
+	  switch ( _prefs.selectmode() ) {
+	    case FL_TREE_SELECT_NONE:
+	      break;
+	    case FL_TREE_SELECT_SINGLE:
+	      select_only(o);
+	      break;
+	    case FL_TREE_SELECT_MULTI: {
+	      if ( Fl::event_state() & FL_SHIFT ) {		// SHIFT+PUSH?
+	        select(o);					// add to selection
+	      } else if ( Fl::event_state() & FL_CTRL ) {	// CTRL+PUSH?
+		select_toggle(o);				// toggle selection state
+		lastselect = o;					// save toggled item (prevent oscillation)
+	      } else {
+		select_only(o);
+	      }
+	      break;
+	    }
+	  }
+	}
       }
       break;
     }
     case FL_DRAG: {
+      // do the scrolling first:
+      int my = Fl::event_y();
+      if ( my < y() ) {			// above top?
+        position(position()-5);
+      } else if ( my > (y()+h()) ) {	// below bottom?
+        position(position()+5);
+      }
       if ( Fl::event_button() != FL_LEFT_MOUSE ) break;
       Fl_Tree_Item *o = _root->find_clicked(_prefs);
-      if ( o ) {
-        ret |= 1;				// handled
-        // Item's label clicked?
-        if ( o->event_on_label(_prefs) && 
-	  (!o->widget() || !Fl::event_inside(o->widget())) &&
-	  callback() &&
-	  (!_vscroll->visible() || !Fl::event_inside(_vscroll)) ) {
-          item_clicked(o);			// save item clicked
-          // Handle selection behavior
-          switch ( _prefs.selectmode() ) {
-            case FL_TREE_SELECT_NONE: {		// no selection changes
-              break;
-            }
-            case FL_TREE_SELECT_SINGLE: {
-              changed = select_only(o);
-              break;
-            }
-            case FL_TREE_SELECT_MULTI: {
-              int state = Fl::event_state();
-              if ( state & FL_CTRL ) {
-                if ( lastselect != o ) {// not already toggled from last microdrag?
-                  changed = 1;	// changed
-                  o->select_toggle();	// toggle selection
-                  lastselect = o;	// save we toggled it (prevents oscillation)
-                }
-              } else {
-	        if ( ! o->is_selected() ) {
-                  changed = 1;		// changed
-                  o->select();		// select this
-	        }
-              }
-              break;
-            }
-          }
-          if ( changed ) {
-            redraw();			// make change(s) visible
-            if ( when() & FL_WHEN_CHANGED ) {
-              set_changed();
-              do_callback((Fl_Widget*)this, user_data());	// item callback
-            }
-          }
-        }
+      if ( ! o ) break;
+      set_item_focus(o);			// becomes new focus widget
+      redraw();
+      ret |= 1;
+      // Item's label clicked?
+      if ( o->event_on_label(_prefs) && 
+	   (!o->widget() || !Fl::event_inside(o->widget())) &&
+	   (!vscroll->visible() || !Fl::event_inside(vscroll)) ) {
+	item_clicked(o);			// save item clicked
+	// Handle selection behavior
+	switch ( _prefs.selectmode() ) {
+	  case FL_TREE_SELECT_NONE: break;	// no selection changes
+	  case FL_TREE_SELECT_SINGLE:
+	    select_only(o);
+	    break;
+	  case FL_TREE_SELECT_MULTI:
+	    if ( Fl::event_state() & FL_CTRL &&	// CTRL-DRAG: toggle?
+	         lastselect != o ) {		// not already toggled from last microdrag?
+	      select_toggle(o);			// toggle selection
+	      lastselect = o;			// save we toggled it (prevents oscillation)
+	    } else {
+	      select(o);			// select this
+	    }
+	    break;
+	}
       }
       break;
     }
-    case FL_RELEASE: {
-      if ( Fl::event_button() == FL_LEFT_MOUSE ) {
-        ret |= 1;
-        if ( when() & FL_WHEN_RELEASE || ( this->changed() && (when() & FL_WHEN_CHANGED)) ) {
-	  do_callback((Fl_Widget*)this, user_data());       // item callback
-        }
-      }
-      break;
-    }
   }
   return(ret);
 }
@@ -343,24 +491,26 @@
 /// Deselect item and all its children.
 ///     If item is NULL, root() is used.
 ///     Handles calling redraw() if anything was changed.
-///     Returns count of how many items were in the 'selected' state,
-///     ie. how many items were "changed".
+///     Invokes the callback depending on the value of \p docallback.
 ///
-///     \p docallback is an optional paramemter that can either be 0 or 1:
+///     \p docallback is an optional paramemter that can be one of the following:
 ///
-///     - 0 - the callback() is not invoked (default)
+///     - 0 - the callback() is not invoked
 ///     - 1 - the callback() is invoked once if \b any items changed state,
 ///           and item_clicked() will be NULL (since many items could have been changed).
-//
-/// \todo deselect_all()'s docallback should support '2' (invoke callback for each item changed)
+///     - 2 - the callback() is invoked if item changed state and when() is FL_WHEN_CHANGED (default)
 ///
+/// \returns count of how many items were actually changed to the deselected state.
+///
 int Fl_Tree::deselect_all(Fl_Tree_Item *item, int docallback) {
   item = item ? item : root();			// NULL? use root()
   int count = item->deselect_all();
   if ( count ) {
+    set_changed();
+    if ( docallback == 1 || ( (docallback == 2) && (when() & FL_WHEN_CHANGED) ) ) {
+      do_callback_for_item(0);
+    }
     redraw();					// anything changed? cause redraw
-    if ( docallback == 1 )
-      do_callback_for_item(0);
   }
   return(count);
 }
@@ -368,24 +518,26 @@
 /// Select item and all its children.
 ///     If item is NULL, root() is used.
 ///     Handles calling redraw() if anything was changed.
-///     Returns count of how many items were in the 'deselected' state,
-///     ie. how many items were "changed".
+///     Invokes the callback depending on the value of \p docallback.
 ///
-///     \p docallback is an optional paramemter that can either be 0 or 1:
+///     \p docallback is an optional paramemter that can one of the following:
 ///
-///     - 0 - the callback() is not invoked (default)
+///     - 0 - the callback() is not invoked
 ///     - 1 - the callback() is invoked once if \b any items changed state,
 ///           and item_clicked() will be NULL (since many items could have been changed).
+///     - 2 - the callback() is invoked if item changed state and when() is FL_WHEN_CHANGED (default)
 ///
-/// \todo select_all()'s docallback should support '2' (invoke callback for each item changed)
+/// \returns count of how many items were actually changed to the selected state.
 ///
 int Fl_Tree::select_all(Fl_Tree_Item *item, int docallback) {
   item = item ? item : root();			// NULL? use root()
   int count = item->select_all();
   if ( count ) {
+    set_changed();
+    if ( docallback == 1 || ( (docallback == 2) && (when() & FL_WHEN_CHANGED) ) ) {
+      do_callback_for_item(0);
+    }
     redraw();					// anything changed? cause redraw
-    if (docallback == 1)
-      do_callback_for_item(0);
   }
   return(count);
 }
@@ -393,16 +545,20 @@
 /// Select only this item.
 ///     If item is NULL, root() is used.
 ///     Handles calling redraw() if anything was changed.
-///     Returns how many items were changed, if any.
+///     Invokes the callback depending on the value of \p docallback.
 ///
-///     \p docallback is an optional paramemter that can either be 0, 1 or 2:
+///     \p docallback is an optional paramemter that can either be 0, 1, 2, 3:
 ///
-///     - 0 - the callback() is not invoked (default)
+///     - 0 - the callback() is not invoked
 ///     - 1 - the callback() is invoked once if \b any items changed state,
-///          and item_clicked() will be NULL (since many items could have been changed).
-///     - 2 - the callback() is invoked once for \b each item that changed state,
-///          and the callback() can use item_clicked() to determine the item changed.
+///           and item_clicked() will be NULL (since many items could have been changed).
+///     - 2 - the callback() is invoked if one or more items changed state and when()
+///           is FL_WHEN_CHANGED (default)
+///     - 3 - the callback() is invoked once for \b each item that changed state,
+///           and the callback() can use item_clicked() to determine the item changed.
 ///
+/// \returns the number of items whose selection states were changed, if any.
+///
 int Fl_Tree::select_only(Fl_Tree_Item *selitem, int docallback) {
   selitem = selitem ? selitem : root();		// NULL? use root()
   int changed = 0;
@@ -411,22 +567,102 @@
       if ( item->is_selected() ) continue;	// don't count if already selected
       item->select();
       ++changed;
-      if ( docallback == 2 ) do_callback_for_item(item);
+      if ( docallback == 1 || ( (docallback == 2) && (when() & FL_WHEN_CHANGED) ) ) {
+	do_callback_for_item(item);
+      }
     } else {
       if ( item->is_selected() ) {
         item->deselect();
         ++changed;
-        if ( docallback == 2 ) do_callback_for_item(item);
+        if ( docallback == 3 ) do_callback_for_item(item);
       }
     }
   }
   if ( changed ) {
+    set_changed();
     redraw();			// anything changed? redraw
-    if ( docallback == 1 ) do_callback_for_item(0);
   }
   return(changed);
 }
 
+/// Adjust the vertical scroll bar so that \p item is visible
+/// \p yoff pixels from the top of the Fl_Tree widget's display.
+///
+/// For instance, yoff=0 will position the item at the top.
+///
+/// Note that in some cases \p yoff cannot be achieved, due
+/// to the scrollbar's limits. In those cases, the scrollbar's
+/// position will be clipped. (So for instance, you can't show
+/// the first item in the center of the display)
+///
+/// \see show_item_top(), show_item_middle(), show_item_bottom()
+///
+void Fl_Tree::show_item(Fl_Tree_Item *item, int yoff) {
+  if ( ! item ) return;
+  float newval = item->y() - y() - yoff + vscroll->value();
+  if ( newval < vscroll->minimum() ) newval = vscroll->minimum();
+  if ( newval > vscroll->maximum() ) newval = vscroll->maximum();
+  vscroll->value(newval);
+  redraw();
+}
+
+/// Adjust the vertical scrollbar so that \p item is at the top of the display.
+void Fl_Tree::show_item_top(Fl_Tree_Item *item) {
+  item = item ? item : first();
+  if ( ! item ) return;
+  show_item(item, 0);
+}
+
+/// Adjust the vertical scrollbar so that \p item is in the middle of the display.
+void Fl_Tree::show_item_middle(Fl_Tree_Item *item) {
+  item = item ? item : first();
+  if ( ! item ) return;
+  show_item(item, h()/2 - item->h()/2);
+}
+
+/// Adjust the vertical scrollbar so that \p item is at the bottom of the display.
+void Fl_Tree::show_item_bottom(Fl_Tree_Item *item) {
+  item = item ? item : first();
+  if ( ! item ) return;
+  show_item(item, h() - item->h());
+}
+
+/// Gets the vertical scroll position of the list as a pixel position \p pos.
+/// The position returned is how many pixels of the list are scrolled off the top edge
+/// of the screen.  Example: A position of '3' indicates the top 3 pixels of 
+/// the list are scrolled off the top edge of the screen.
+/// \see position(), hposition()
+///
+int Fl_Tree::position() const {
+  return((int)vscroll->value());
+}
+
+///  Sets the vertical scroll position of the list to pixel position \p pos.
+///  The position is how many pixels of the list are scrolled off the top edge
+///  of the screen. Example: A position of '3' scrolls the top three pixels of
+///  the list off the top edge of the screen.
+///  \param[in] pos The vertical position (in pixels) to scroll the browser to.
+///
+void Fl_Tree::position(int pos) {
+  if (pos < 0) pos = 0;
+  if (pos > vscroll->maximum()) pos = vscroll->maximum();
+  if (pos == vscroll->value()) return;
+  vscroll->value(pos);
+}
+
+/// Displays the \p item, scrolling the list as necessary.
+/// \param[in] item The item to be displayed.
+///
+void Fl_Tree::display(Fl_Tree_Item *item) {
+  if ( ! item ) return;
+  int itop = item->y();
+  int ibot = itop + item->h();
+  if ( itop < y() )		// item above top?
+    show_item_top(item);
+  else if ( ibot > y()+h() )	// item below bottom?
+    show_item_bottom(item);
+}
+
 /**
  * Read a preferences database into the tree widget.
  * A preferences database is a hierarchical collection of data which can be
Index: src/Fl_Tree_Item.cxx
===================================================================
--- src/Fl_Tree_Item.cxx	(revision 7680)
+++ src/Fl_Tree_Item.cxx	(working copy)
@@ -473,8 +473,49 @@
   return(0);
 }
 
+static void draw_item_focus(Fl_Boxtype B, Fl_Color C, int X, int Y, int W, int H) {
+  if (!Fl::visible_focus()) return;
+  switch (B) {
+    case FL_DOWN_BOX:
+    case FL_DOWN_FRAME:
+    case FL_THIN_DOWN_BOX:
+    case FL_THIN_DOWN_FRAME:
+      X ++;
+      Y ++;
+    default:
+      break;
+  }
+  fl_color(fl_contrast(FL_BLACK, C));
+
+#if defined(USE_X11) || defined(__APPLE_QUARTZ__)
+  fl_line_style(FL_DOT);
+  fl_rect(X + Fl::box_dx(B), Y + Fl::box_dy(B),
+          W - Fl::box_dw(B) - 1, H - Fl::box_dh(B) - 1);
+  fl_line_style(FL_SOLID);
+#else
+  // Some platforms don't implement dotted line style, so draw
+  // every other pixel around the focus area...
+  //
+  // Also, QuickDraw (MacOS) does not support line styles specifically,
+  // and the hack we use in fl_line_style() will not draw horizontal lines
+  // on odd-numbered rows...
+  int i, xx, yy;
+
+  X += Fl::box_dx(B);
+  Y += Fl::box_dy(B);
+  W -= Fl::box_dw(B) + 2;
+  H -= Fl::box_dh(B) + 2;
+
+  for (xx = 0, i = 1; xx < W; xx ++, i ++) if (i & 1) fl_point(X + xx, Y);
+  for (yy = 0; yy < H; yy ++, i ++) if (i & 1) fl_point(X + W, Y + yy);
+  for (xx = W; xx > 0; xx --, i ++) if (i & 1) fl_point(X + xx, Y + H);
+  for (yy = H; yy > 0; yy --, i ++) if (i & 1) fl_point(X, Y + yy);
+#endif
+}
+
 /// Draw this item and its children.
-void Fl_Tree_Item::draw(int X, int &Y, int W, Fl_Widget *tree, 
+void Fl_Tree_Item::draw(int X, int &Y, int W, Fl_Widget *tree,
+			Fl_Tree_Item *itemfocus,
                         const Fl_Tree_Prefs &prefs, int lastchild) {
   if ( ! _visible ) return; 
   fl_font(_labelfont, _labelsize);
@@ -598,6 +639,10 @@
         fl_draw(_label, X+useroff, Y+H-fl_descent()-1);
       }
     }
+    if ( this == itemfocus && Fl::visible_focus() && Fl::focus() == tree) {
+      // Draw focus box around this item
+      draw_item_focus(FL_NO_BOX,bg,bx+1,by+1,bw-1,bh-1);
+    }
     Y += H;
   }			// end drawthis
   // Draw children
@@ -608,7 +653,7 @@
     int child_y_start = Y;
     for ( int t=0; t<children(); t++ ) {
       int lastchild = ((t+1)==children()) ? 1 : 0;
-      _children[t]->draw(child_x, Y, child_w, tree, prefs, lastchild);
+      _children[t]->draw(child_x, Y, child_w, tree, itemfocus, prefs, lastchild);
     }
     if ( has_children() && is_open() ) {
       Y += prefs.openchild_marginbottom();		// offset below open child tree
@@ -736,6 +781,109 @@
   return(p);
 }
 
+/// Return this item's next sibling.
+///
+/// Moves to the next item below us at the same level (sibling).
+/// Use this to move down the tree without moving deeper into the tree,
+/// effectively skipping over this item's children/descendents.
+/// 
+/// \returns item's next sibling, or 0 if none.
+///
+Fl_Tree_Item *Fl_Tree_Item::next_sibling() {
+  if ( !parent() ) return(0);			// No parent (root)? We have no siblings
+  int index = parent()->find_child(this);	// find our position in parent's child() array
+  if ( index == -1 ) return(0);			// parent doesn't know us? weird
+  if ( (index+1) < parent()->children() )	// is there a next child?
+    return(parent()->child(index+1));		// return next child if there's one below us
+  return(0);					// no siblings below us
+}
+
+/// Return this item's previous sibling.
+///
+/// Moves to the previous item above us at the same level (sibling).
+/// Use this to move up the tree without moving deeper into the tree.
+/// 
+/// \returns This item's previous sibling, or 0 if none.
+///
+Fl_Tree_Item *Fl_Tree_Item::prev_sibling() {
+  if ( !parent() ) return(0);				// No parent (root)? We have no siblings
+  int index = parent()->find_child(this);		// find next position up in parent's child() array
+  if ( index == -1 ) return(0);				// parent doesn't know us? weird
+  if ( index > 0 ) return(parent()->child(index-1));	// return previous child if there's one above us
+  return(0);						// no siblings above us
+}
+
+/// Return the next visible item. (If this item has children and is closed, children are skipped)
+///
+/// This method can be used to walk the tree forward, skipping items
+/// that are not currently visible to the user.
+/// 
+/// \returns the next visible item below us, or 0 if there's no more items.
+///
+Fl_Tree_Item *Fl_Tree_Item::next_displayed(Fl_Tree_Prefs &prefs) {
+  Fl_Tree_Item *c = this;
+  while ( c ) {
+    if ( c->is_root() && !prefs.showroot() ) {		// on root and can't show it?
+      c = c->next();					// skip ahead, try again
+      continue;
+    }
+    if ( c->has_children() && c->is_close() ) {		// item has children and: invisible or closed?
+      // Skip children, take next sibling. If none, try parent's sibling, repeat
+      while ( c ) {
+	Fl_Tree_Item *sib = c->next_sibling();		// get sibling
+	if ( sib ) { c = sib; break; }			// Found? let outer loop test it
+	c = c->parent();				// No sibling? move up tree, try parent's sibling
+      }
+    } else {						// has children and isn't closed, or no children
+      c = c->next();					// use normal 'next'
+    }
+    if ( !c ) return(0);				// no more? done
+    // Check all parents to be sure none are closed.
+    // If closed, move up to that level and repeat until sure none are closed.
+    Fl_Tree_Item *p = c->parent();
+    while (1) {
+      if ( !p || p->is_root() ) return(c);		// hit top? then we're displayed, return c
+      if ( p->is_close() ) c = p;			// found closed parent? make it current
+      p = p->parent();					// continue up tree
+    }
+    if ( c && c->visible() ) return(c);			// item visible? return it
+  }
+  return(0);						// hit end: no more items
+}
+
+/// Return the previous visible item. (If this item above us has children and is closed, its children are skipped)
+///
+/// This method can be used to walk the tree backward, 
+/// skipping items that are not currently visible to the user.
+/// 
+/// \returns the previous visible item above us, or 0 if there's no more items.
+///
+Fl_Tree_Item *Fl_Tree_Item::prev_displayed(Fl_Tree_Prefs &prefs) {
+  Fl_Tree_Item *c = this;
+  while ( c ) {
+    c = c->prev();					// previous item
+    if ( !c ) break;					// no more items? done
+    if ( c->is_root() )					// root
+      return((prefs.showroot()&&c->visible()) ? c : 0);	// return root if visible
+    if ( !c->visible() ) continue;			// item not visible? skip
+    // Check all parents to be sure none are closed.
+    // If closed, move up to that level and repeat until sure none are closed.
+    Fl_Tree_Item *p = c->parent();
+    while (1) {
+      if ( !p || p->is_root() ) return(c);		// hit top? then we're displayed, return c
+      if ( p->is_close() ) c = p;			// found closed parent? make it current
+      p = p->parent();					// continue up tree
+    }
+  }
+  return(0);						// hit end: no more items
+}
+
+int Fl_Tree_Item::visible_r() const {
+  for (const Fl_Tree_Item *p=this; p; p=p->parent())	// move up through parents
+    if (!p->visible() || p->is_close()) return(0);	// any parent not visible or closed?
+  return(1);
+}
+
 //
 // End of "$Id$".
 //
Index: FL/Fl_Tree_Prefs.H
===================================================================
--- FL/Fl_Tree_Prefs.H	(revision 7680)
+++ FL/Fl_Tree_Prefs.H	(working copy)
@@ -69,8 +69,8 @@
 ///
 enum Fl_Tree_Select {
   FL_TREE_SELECT_NONE=0,	///< Nothing selected when items are clicked
-  FL_TREE_SELECT_SINGLE,	///< Single item selected when item is clicked (default)
-  FL_TREE_SELECT_MULTI	///< Multiple items can be selected by clicking with
+  FL_TREE_SELECT_SINGLE=1,	///< Single item selected when item is clicked (default)
+  FL_TREE_SELECT_MULTI=2	///< Multiple items can be selected by clicking with
   ///< SHIFT or CTRL or mouse drags.
 };
 
Index: FL/Fl_Tree.H
===================================================================
--- FL/Fl_Tree.H	(revision 7680)
+++ FL/Fl_Tree.H	(working copy)
@@ -112,8 +112,13 @@
 class Fl_Tree : public Fl_Group {
   Fl_Tree_Item  *_root;					// can be null!
   Fl_Tree_Item  *_item_clicked;
+  Fl_Tree_Item  *_item_focus;				// item that has focus box
   Fl_Tree_Prefs  _prefs;				// all the tree's settings
-  Fl_Scrollbar  *_vscroll;
+  int            _scrollbar_size;			// size of scrollbar trough
+
+public:
+  /// Vertical scrollbar. Public, so that it can be accessed directly.
+  Fl_Scrollbar *vscroll;
   
 public:
   /// Find the item that was clicked.
@@ -132,6 +137,14 @@
     return(_root->find_clicked(_prefs));
   }
 protected:
+  /// Set the item that is currently in focus. Handles calling redraw()
+  /// as needed to update the focus box.
+  void set_item_focus(Fl_Tree_Item *o) {
+    if ( _item_focus != o ) {		// changed?
+      _item_focus = o;			// update
+      if ( visible_focus() ) redraw();	// redraw to update focus box
+    }
+  }
   /// Set the item that was last clicked.
   /// Should only be used by subclasses needing to change this value.
   /// Normally Fl_Tree manages this value.
@@ -145,7 +158,8 @@
     do_callback((Fl_Widget*)this, user_data());
     _item_clicked = save;			// restore item_clicked
   }
-  
+  Fl_Tree_Item *next_visible_item(Fl_Tree_Item *start, int dir);
+
 public:
   Fl_Tree(int X, int Y, int W, int H, const char *L=0);
   ~Fl_Tree();
@@ -267,52 +281,56 @@
   /// This causes the item's children (if any) to be shown.
   /// Handles redrawing if anything was actually changed.
   ///
-  void open(Fl_Tree_Item *item) {
-    if ( ! item->is_open() ) {
-      item->open();
-      redraw();
-    }
+  /// \returns
+  ///     -   1 -- item was opened
+  ///     -   0 -- item was already open, no change
+  ///
+  int open(Fl_Tree_Item *item) {
+    if ( item->is_open() ) return(0);
+    item->open();
+    redraw();
+    return(1);
   }
   /// Opens the item specified by \p path (eg: "Parent/child/item").
   /// This causes the item's children (if any) to be shown.
   /// Handles redrawing if anything was actually changed.
   ///
   /// \returns
-  ///     -   0 : OK
-  ///     -  -1 : item was not found
+  ///     -   1 -- OK: item opened
+  ///     -   0 -- OK: item was already open, no change
+  ///     -  -1 -- ERROR: item was not found
   ///         
   int open(const char *path) {
     Fl_Tree_Item *item = find_item(path);
-    if ( item ) {
-      open(item);
-      return(0);
-    }
-    return(-1);
+    if ( ! item ) return(-1);
+    return(open(item));
   }
   /// Closes the specified \p item.
   /// Handles redrawing if anything was actually changed.
   ///
-  void close(Fl_Tree_Item *item) {
-    if ( ! item->is_close() ) {
-      item->close();
-      redraw();
-    }
+  /// \returns
+  ///     -   1 -- item was closed
+  ///     -   0 -- item was already closed, no change
+  ///
+  int close(Fl_Tree_Item *item) {
+    if ( item->is_close() ) return(0);
+    item->close();
+    redraw();
+    return(1);
   }
   /// Closes the item specified by \p path, eg: "Parent/child/item".
   ///
   /// Handles redrawing if anything was actually changed.
   ///
   /// \returns
-  ///     -   0 -- OK
-  ///     -  -1 -- item was not found
+  ///     -   1 -- OK: item closed
+  ///     -   0 -- OK: item was already closed, no change
+  ///     -  -1 -- ERROR: item was not found
   ///         
   int close(const char *path) {
     Fl_Tree_Item *item = find_item(path);
-    if ( item ) {
-      close(item);
-      return(0);
-    }
-    return(-1);
+    if ( ! item ) return(-1);
+    return(close(item));
   }
   /// See if \p item is open.
   ///
@@ -332,14 +350,14 @@
   /// one of the item's parents might be closed.
   ///
   /// \returns
-  ///     -    1 : item is open
-  ///     -    0 : item is closed
-  ///     -   -1 : item was not found
+  ///     -    1 - OK: item is open
+  ///     -    0 - OK: item is closed
+  ///     -   -1 - ERROR: item was not found
   ///
   int is_open(const char *path) const {
     const Fl_Tree_Item *item = find_item(path);
-    if ( item ) return(item->is_open()?1:0);
-    return(-1);
+    if ( ! item ) return(-1);
+    return(item->is_open()?1:0);
   }
   /// See if the specified \p item is closed.
   /// \returns
@@ -352,108 +370,146 @@
   /// See if item specified by \p path (eg: "Parent/child/item") is closed.
   ///
   /// \returns
-  ///     -   1 : item is closed
-  ///     -   0 : item is open
-  ///     -  -1 : item was not found
+  ///     -   1 - OK: item is closed
+  ///     -   0 - OK: item is open
+  ///     -  -1 - ERROR: item was not found
   ///
   int is_close(const char *path) const {
     const Fl_Tree_Item *item = find_item(path);
-    if ( item ) return(item->is_close()?1:0);
-    return(-1);
+    if ( ! item ) return(-1);
+    return(item->is_close()?1:0);
   }
   
   /////////////////////////
   // Item selection methods
   /////////////////////////
   
+  Fl_Tree_Item *first_selected_item();
+  Fl_Tree_Item *next_selected_item(Fl_Tree_Item *item=0);
+
   /// Select the specified \p item. Use 'deselect()' to de-select it.
   /// Handles redrawing if anything was actually changed.
+  /// Invokes the callback depending on the value of \p docallback.
   ///
   /// \p docallback is an optional paramemter that can either be 0 or 1.
-  ///     - 0 - the callback() is not invoked (default)
+  ///     - 0 - the callback() is not invoked
   ///     - 1 - the callback() is invoked if the item changed state,
   ///           and the callback can use item_clicked() to determine the selected item.
+  ///     - 2 - the callback() is invoked if item changed state and when() is FL_WHEN_CHANGED (default)
   ///
-  void select(Fl_Tree_Item *item, int docallback=0) {
+  /// \param[in] item -- the item to be selected
+  /// \param[in] docallback -- optional argument, can be 0,1,2. See description above.
+  ///
+  /// \returns
+  ///     -   1 - item's state was changed
+  ///     -   0 - item was already selected, no change was made
+  ///
+  int select(Fl_Tree_Item *item, int docallback=2) {
     if ( ! item->is_selected() ) {
       item->select();
-      if ( docallback == 1 ) do_callback_for_item(item);
+      set_changed();
+      if ( docallback == 1 || ( (docallback == 2) && (when() & FL_WHEN_CHANGED) ) ) {
+        do_callback_for_item(item);
+      }
       redraw();
+      return(1);
     }
+    return(0);
   }
   /// Select the item specified by \p path (eg: "Parent/child/item").
   /// Handles redrawing if anything was actually changed.
+  /// Invokes the callback depending on the value of \p docallback.
   ///
   /// \p docallback is an optional paramemter that can either be 0 or 1.
-  ///     - 0 - the callback() is not invoked (default)
+  ///     - 0 - the callback() is not invoked
   ///     - 1 - the callback() is invoked if the item changed state,
   ///           and the callback can use item_clicked() to determine the selected item.
+  ///     - 2 - the callback() is invoked if item changed state and when() is FL_WHEN_CHANGED (default)
   ///
+  /// \param[in] path -- the tree item's pathname (eg. "Flintstones/Fred")
+  /// \param[in] docallback -- optional argument, can be 0,1,2. See description above.
+  ///
   /// \returns
-  ///     -   0 : OK
-  ///     -  -1 : item was not found
+  ///     -   1 : OK: item's state was changed
+  ///     -   0 : OK: item was already selected, no change was made
+  ///     -  -1 : ERROR: item was not found
   ///
-  int select(const char *path, int docallback=0) {
+  int select(const char *path, int docallback=2) {
     Fl_Tree_Item *item = find_item(path);
-    if ( item ) {
-      select(item);
-      if ( docallback == 1 ) do_callback_for_item(item);
-      return(0);
-    }
-    return(-1);
+    if ( ! item ) return(-1);
+    return(select(item, docallback));
   }
   /// Toggle the select state of the specified \p item.
   /// Handles redrawing.
+  /// Invokes the callback depending on the value of \p docallback.
   ///
   /// \p docallback is an optional paramemter that can either be 0 or 1.
   ///     - 0 - the callback() is not invoked (default)
   ///     - 1 - the callback() is invoked,
   ///           and the callback can use item_clicked() to determine the selected item.
+  ///     - 2 - the callback() is invoked if item changed state and when() is FL_WHEN_CHANGED (default)
   ///
-  void select_toggle(Fl_Tree_Item *item, int docallback=0) {
+  void select_toggle(Fl_Tree_Item *item, int docallback=2) {
     item->select_toggle();
-    if ( docallback == 1 ) do_callback_for_item(item);
+    set_changed();
+    if ( docallback == 1 || ( (docallback == 2) && (when() & FL_WHEN_CHANGED) ) ) {
+      do_callback_for_item(item);
+    }
     redraw();
   }
   /// De-select the specified \p item.
   /// Handles redrawing if anything was actually changed.
+  /// Invokes the callback depending on the value of \p docallback.
   ///
   /// \p docallback is an optional paramemter that can either be 0 or 1.
   ///     - 0 - the callback() is not invoked (default)
   ///     - 1 - the callback() is invoked if the item changed state,
-  ///           and the callback can use item_clicked() to determine the selected item.
+  ///           and the callback can use item_clicked() to determine the deselected item.
+  ///     - 2 - the callback() is invoked if item changed state and when() is FL_WHEN_CHANGED (default)
   ///
-  void deselect(Fl_Tree_Item *item, int docallback=0) {
+  /// \returns
+  ///     - 0 - item was already deselected, no change was made
+  ///     - 1 - item's state was changed
+  ///
+  int deselect(Fl_Tree_Item *item, int docallback=2) {
     if ( item->is_selected() ) {
       item->deselect();
-      if ( docallback == 1 ) do_callback_for_item(item);
+      set_changed();
+      if ( docallback == 1 || ( (docallback == 2) && (when() & FL_WHEN_CHANGED) ) ) {
+        do_callback_for_item(item);
+      }
       redraw();
+      return(1);
     }
+    return(0);
   }
   /// De-select an item specified by \p path (eg: "Parent/child/item").
   /// Handles redrawing if anything was actually changed.
+  /// Invokes the callback depending on the value of \p docallback.
   ///
   /// \p docallback is an optional paramemter that can either be 0 or 1.
-  ///     - 0 - the callback() is not invoked (default)
+  ///     - 0 - the callback() is not invoked
   ///     - 1 - the callback() is invoked if the item changed state,
-  ///           and the callback can use item_clicked() to determine the selected item.
+  ///           and the callback can use item_clicked() to determine the deselected item.
+  ///     - 2 - the callback() is invoked if item changed state and when() is FL_WHEN_CHANGED (default)
   ///
+  /// \param[in] path -- the tree item's pathname (eg. "Flintstones/Fred")
+  /// \param[in] docallback -- optional argument, can be 0,1,2. See description above.
+  ///
   ///  \returns
-  ///     -   0 : OK
-  ///     -  -1 : item was not found
+  ///     -   1 - OK: item's state was changed
+  ///     -   0 - OK: item was already deselected, no change was made
+  ///     -  -1 - ERROR: item was not found
   ///
-  int deselect(const char *path, int docallback=0) {
+  int deselect(const char *path, int docallback=2) {
     Fl_Tree_Item *item = find_item(path);
-    if ( item ) {
-      deselect(item, docallback);
-      return(0);
-    }
-    return(-1);
+    if ( ! item ) return(-1);
+    return(deselect(item, docallback));
   }
   
-  int deselect_all(Fl_Tree_Item *item=0, int docallback=0);
-  int select_only(Fl_Tree_Item *selitem, int docallback=0);
-  int select_all(Fl_Tree_Item *item=0, int docallback=0);
+  int deselect_all(Fl_Tree_Item *item=0, int docallback=2);
+  int select_only(Fl_Tree_Item *selitem, int docallback=2);
+  int select_all(Fl_Tree_Item *item=0, int docallback=2);
   
   /// See if the specified \p item is selected.
   /// \return
@@ -472,8 +528,8 @@
   ///
   int is_selected(const char *path) {
     Fl_Tree_Item *item = find_item(path);
-    if ( item ) return(is_selected(item));
-    return(-1);
+    if ( ! item ) return(-1);
+    return(is_selected(item));
   }
   /// Print the tree as 'ascii art' to stdout.
   /// Used mainly for debugging.
@@ -688,7 +744,50 @@
   void selectmode(Fl_Tree_Select val) {
     _prefs.selectmode(val);
   }
+  void show_item(Fl_Tree_Item *item, int yoff=0);
+  void show_item_bottom(Fl_Tree_Item *item);
+  void show_item_middle(Fl_Tree_Item *item);
+  void show_item_top(Fl_Tree_Item *item);
+  int  position() const;
+  void position(int ypos);
+  /**
+    Gets the current size of the scrollbars' troughs, in pixels.
+
+    If this value is zero (default), this widget will use the global
+    Fl::scrollbar_size() value as the scrollbar's width.
   
+    \returns Scrollbar size in pixels, or 0 if the global Fl::scrollsize() is being used.
+    \see Fl::scrollbar_size(int)
+  */
+  int scrollbar_size() const {
+      return(_scrollbar_size);
+  }
+  /**
+    Sets the pixel size of the scrollbars' troughs to the \p size, in pixels.
+
+    Normally you should not need this method, and should use the global
+    Fl::scrollbar_size(int) instead to manage the size of ALL 
+    your widgets' scrollbars. This ensures your application 
+    has a consistent UI, is the default behavior, and is normally
+    what you want.
+
+    Only use THIS method if you really need to override the global
+    scrollbar size. The need for this should be rare.
+    
+    Setting \p size to the special value of 0 causes the widget to
+    track the global Fl::scrollbar_size(), which is the default.
+    
+    \param[in] size Sets the scrollbar size in pixels.\n
+                    If 0 (default), scrollbar size tracks the global Fl::scrollbar_size()
+    \see Fl::scrollbar_size()
+  */
+  void scrollbar_size(int size) {
+      _scrollbar_size = size;
+      int scrollsize = _scrollbar_size ? _scrollbar_size : Fl::scrollbar_size();
+      if ( vscroll->w() != scrollsize ) {
+        vscroll->resize(x()+w()-scrollsize, h(), scrollsize, vscroll->h());
+      }
+  }   
   void load(class Fl_Preferences&);
 };
 
Index: FL/Fl_Tree_Item.H
===================================================================
--- FL/Fl_Tree_Item.H	(revision 7680)
+++ FL/Fl_Tree_Item.H	(working copy)
@@ -83,7 +83,11 @@
   Fl_Tree_Item(const Fl_Tree_Prefs &prefs);	// CTOR
   ~Fl_Tree_Item();				// DTOR
   Fl_Tree_Item(const Fl_Tree_Item *o);		// COPY CTOR
-  void draw(int X, int &Y, int W, Fl_Widget *tree, const Fl_Tree_Prefs &prefs, int lastchild=1);
+  int x() const { return(_xywh[0]); }
+  int y() const { return(_xywh[1]); }
+  int w() const { return(_xywh[2]); }
+  int h() const { return(_xywh[3]); }
+  void draw(int X, int &Y, int W, Fl_Widget *tree, Fl_Tree_Item *itemfocus, const Fl_Tree_Prefs &prefs, int lastchild=1);
   void show_self(const char *indent = "") const;
   void label(const char *val);
   const char *label() const;
@@ -175,6 +179,10 @@
   int depth() const;
   Fl_Tree_Item *prev();
   Fl_Tree_Item *next();
+  Fl_Tree_Item *next_sibling();
+  Fl_Tree_Item *prev_sibling();
+  Fl_Tree_Item *next_displayed(Fl_Tree_Prefs &prefs);
+  Fl_Tree_Item *prev_displayed(Fl_Tree_Prefs &prefs);
   
   /// Return the parent for this item.
   Fl_Tree_Item *parent() {
@@ -293,6 +301,18 @@
   char is_active() const {
     return(_active);
   }
+  /// See if the item is visible.
+  int visible() const {
+    return(_visible ? 1 : 0);
+  }
+  /// Returns if item and all its parents are visible.
+  /// Also takes into consideration if any parent is close()ed.
+  /// \returns
+  ///    1 -- item and its parents are visible/open()
+  ///    0 -- item (or parents) invisible or close()ed.
+  ///
+  int visible_r() const;
+
   /// Set the user icon's image. '0' will disable.
   void usericon(Fl_Image *val) {
     _usericon = val;
Index: test/tree.fl
===================================================================
--- test/tree.fl	(revision 7680)
+++ test/tree.fl	(working copy)
@@ -20,7 +20,7 @@
 decl {\#include <FL/fl_ask.H>} {public global
 } 
 
-decl {\#include <FL/Fl_File_Chooser.H>} {selected public global
+decl {\#include <FL/Fl_File_Chooser.H>} {public global
 } 
 
 decl {\#include <FL/Fl_Preferences.H>} {public global
@@ -108,6 +108,15 @@
 tree->add("Descending/Yyy");
 tree->add("Descending/Ccc");
 
+// Add 500 items in numerical order
+tree->sortorder(FL_TREE_SORT_NONE);
+for ( int t=0; t<500; t++ ) {
+    static char s[80];
+    sprintf(s, "500 Items/item %04d", t);
+    tree->add(s);
+}
+tree->close("500 Items");	// close the 500 items by default
+
 tree->redraw();} {}
 } 
 
@@ -136,7 +145,7 @@
       callback {int val = (int)margintop_slider->value();
 tree->margintop(val);
 tree->redraw();}
-      tooltip {Changes the top margin for the tree widget} xywh {190 414 240 16} type Horizontal labelsize 12 align 4 textsize 12
+      tooltip {Changes the top margin for the tree widget} xywh {190 414 240 16} type Horizontal labelsize 12 align 4 step 0.01 textsize 12
       code0 {o->value(tree->margintop());}
       code1 {o->range(0.0, 100.0);}
       code2 {o->step(1.0);}
@@ -148,7 +157,7 @@
       callback {int val = (int)marginleft_slider->value();
 tree->marginleft(val);
 tree->redraw();}
-      tooltip {Changes the left margin for the tree widget} xywh {190 434 240 16} type Horizontal labelsize 12 align 4 textsize 12
+      tooltip {Changes the left margin for the tree widget} xywh {190 434 240 16} type Horizontal labelsize 12 align 4 step 0.01 textsize 12
       code0 {o->value(tree->marginleft());}
       code1 {o->range(0.0, 100.0);}
       code2 {o->step(1.0);}
@@ -160,7 +169,7 @@
       callback {int val = (int)openchild_marginbottom_slider->value();
 tree->openchild_marginbottom(val);
 tree->redraw();}
-      tooltip {Changes the vertical space below an open child tree} xywh {190 454 240 16} type Horizontal labelsize 12 align 4 textsize 12
+      tooltip {Changes the vertical space below an open child tree} xywh {190 454 240 16} type Horizontal labelsize 12 align 4 step 0.01 textsize 12
       code0 {o->value(tree->openchild_marginbottom());}
       code1 {o->range(0.0, 100.0);}
       code2 {o->step(1.0);}
@@ -189,7 +198,7 @@
 
 tree->redraw();}
       tooltip {Changes the font size of the selected items
-If none selected, all are changed} xywh {190 474 240 16} type Horizontal labelsize 12 align 4 textsize 12
+If none selected, all are changed} xywh {190 474 240 16} type Horizontal labelsize 12 align 4 step 0.01 textsize 12
       code0 {o->value(tree->labelsize());}
       code1 {o->range(5.0, 200.0);}
       code2 {o->step(1.0);}
@@ -199,7 +208,7 @@
       label {Connector width}
       user_data tree
       callback {tree->connectorwidth((int)connectorwidth_slider->value());}
-      tooltip {Tests Fl_Tree::connectorwidth()} xywh {190 494 240 16} type Horizontal labelsize 12 align 4 textsize 12
+      tooltip {Tests Fl_Tree::connectorwidth()} xywh {190 494 240 16} type Horizontal labelsize 12 align 4 step 0.01 textsize 12
       code0 {o->value(tree->connectorwidth());}
       code1 {o->range(1.0, 100.0);}
       code2 {o->step(1.0);}
@@ -256,16 +265,25 @@
     if ( ( i = tree->find_item("Bbb/bgb/222") ) != NULL ) i->usericon(0);
     if ( ( i = tree->find_item("Bbb/bgb/333") ) != NULL ) i->usericon(0);
 }}
-      tooltip {Tests Fl_Tree_Item::usericon()} xywh {145 525 130 16} down_box DOWN_BOX labelsize 11
+      tooltip {Tests Fl_Tree_Item::usericon()} xywh {145 521 130 16} down_box DOWN_BOX labelsize 11
     }
     Fl_Check_Button showroot_radio {
       label {Show root?}
       user_data tree
       callback {int onoff = showroot_radio->value();
 tree->showroot(onoff);}
-      tooltip {Tests Fl_Tree_Item::usericon()} xywh {145 544 130 16} down_box DOWN_BOX labelsize 11
+      tooltip {Tests Fl_Tree_Item::usericon()} xywh {145 538 130 16} down_box DOWN_BOX labelsize 11
       code0 {int onoff = tree->showroot(); showroot_radio->value(onoff);}
     }
+    Fl_Check_Button visiblefocus_checkbox {
+      label {Visible focus?}
+      user_data tree
+      callback {int onoff = visiblefocus_checkbox->value();
+tree->visible_focus(onoff);}
+      tooltip {Toggles the tree's visible_focus()
+This toggles the visible 'focus box'} xywh {145 555 130 16} down_box DOWN_BOX labelsize 11
+      code0 {int onoff = tree->visible_focus(); visiblefocus_checkbox->value(onoff);}
+    }
     Fl_Choice collapseicons_chooser {
       label {Collapse icons}
       callback {static const char *L_open_xpm[] = {
@@ -355,7 +373,7 @@
         tree->showcollapse(0);
         break;
 }} open
-      tooltip {Tests Fl_Tree::openicon() and Fl_Tree::closeicon()} xywh {145 572 110 16} down_box BORDER_BOX labelsize 11 textsize 11
+      tooltip {Tests Fl_Tree::openicon() and Fl_Tree::closeicon()} xywh {145 578 110 16} down_box BORDER_BOX labelsize 11 textsize 11
     } {
       MenuItem {} {
         label Normal
@@ -378,7 +396,7 @@
     case 1: tree->connectorstyle(FL_TREE_CONNECTOR_DOTTED);   break;
     case 2: tree->connectorstyle(FL_TREE_CONNECTOR_SOLID);    break;
 }} open
-      tooltip {Tests connectorstyle() bit flags} xywh {145 592 110 16} down_box BORDER_BOX labelsize 11 textsize 11
+      tooltip {Tests connectorstyle() bit flags} xywh {145 598 110 16} down_box BORDER_BOX labelsize 11 textsize 11
       code0 {switch (tree->connectorstyle()) { case FL_TREE_CONNECTOR_NONE: connectorstyle_chooser->value(0); break; case FL_TREE_CONNECTOR_DOTTED: connectorstyle_chooser->value(1); break; case FL_TREE_CONNECTOR_SOLID: connectorstyle_chooser->value(2); break; }}
     } {
       MenuItem {} {
@@ -424,7 +442,7 @@
 
 tree->redraw();} open
       tooltip {Changes the label color for the selected items
-If no items selected, all are changed} xywh {145 612 110 16} down_box BORDER_BOX labelsize 11 textsize 11
+If no items selected, all are changed} xywh {145 618 110 16} down_box BORDER_BOX labelsize 11 textsize 11
     } {
       MenuItem {} {
         label Black
@@ -452,7 +470,7 @@
     case 2:  tree->selectmode(FL_TREE_SELECT_MULTI);  break; 	// Multi
     default: tree->selectmode(FL_TREE_SELECT_SINGLE); break;	// Single
 }}
-      tooltip {Sets how Fl_Tree handles mouse selection of tree items} xywh {145 632 110 16} down_box BORDER_BOX labelsize 11 textsize 11
+      tooltip {Sets how Fl_Tree handles mouse selection of tree items} xywh {145 638 110 16} down_box BORDER_BOX labelsize 11 textsize 11
       code0 {selectmode_chooser->value(1);}
       code1 {cb_selectmode_chooser(selectmode_chooser, (void*)0);}
     } {
@@ -478,7 +496,7 @@
   case 2:  tree->when(FL_WHEN_NEVER);     break;
   default: tree->when(FL_WHEN_RELEASE);   break;
 }}
-      tooltip {Sets when() the tree's callback is invoked} xywh {145 652 110 16} down_box BORDER_BOX labelsize 11 textsize 11
+      tooltip {Sets when() the tree's callback is invoked} xywh {145 658 110 16} down_box BORDER_BOX labelsize 11 textsize 11
       code0 {whenmode_chooser->value(1);}
       code1 {cb_whenmode_chooser(whenmode_chooser, (void*)0);}
     } {
@@ -495,6 +513,29 @@
         xywh {60 60 36 21} labelsize 11
       }
     }
+    Fl_Box showitem_box {
+      label {show_item()
+}
+      xywh {468 423 60 77} box GTK_DOWN_BOX color 47 labelsize 11 align 1
+    }
+    Fl_Button {} {
+      label Top
+      callback {Fl_Tree_Item *item = tree->next_selected_item();
+tree->show_item_top(item);}
+      tooltip {Scrolls selected item to the top of the display} xywh {478 433 40 16} labelsize 11
+    }
+    Fl_Button {} {
+      label Mid
+      callback {Fl_Tree_Item *item = tree->next_selected_item();
+tree->show_item_middle(item);}
+      tooltip {Scrolls the selected item to the middle of the display} xywh {478 453 40 16} labelsize 11
+    }
+    Fl_Button {} {
+      label Bot
+      callback {Fl_Tree_Item *item = tree->next_selected_item();
+tree->show_item_bottom(item);}
+      tooltip {Scrolls the selected item to the bottom of the display} xywh {478 473 40 16} labelsize 11
+    }
     Fl_Box docallback_box {
       xywh {280 521 285 81} box GTK_DOWN_BOX color 47
     }
@@ -551,6 +592,17 @@
 tree->redraw();}
       tooltip {Toggle the single item /Bbb/child-02} xywh {430 571 115 16} labelsize 11
     }
+    Fl_Button loaddb_button {
+      label {Load Database...}
+      callback {const char *filename = fl_file_chooser("Select a Preferences style Database", "Preferences(*.prefs)", 0L);
+if (filename) {
+  tree->clear();
+  Fl_Preferences prefs(filename, 0L, 0L);
+  tree->load(prefs);
+  tree->redraw();
+}}
+      tooltip {Load the contents of an Fl_Preferences database into the tree view} xywh {380 618 90 16} labelsize 9
+    }
     Fl_Light_Button deactivate_toggle {
       label { Deactivate}
       callback {int onoff = deactivate_toggle->value() ? 0 : 1;
@@ -571,7 +623,7 @@
 
 tree->redraw();}
       tooltip {Toggle the deactivation state of the selected items.
-If none are selected, all are set.} xywh {280 633 90 16} labelsize 11
+If none are selected, all are set.} xywh {280 638 90 16} labelsize 11
     }
     Fl_Light_Button bold_toggle {
       label { Bold Font}
@@ -595,7 +647,7 @@
 
 tree->redraw();}
       tooltip {Toggles bold font for selected items
-If nothing selected, all are changed} xywh {280 652 90 16} labelsize 11
+If nothing selected, all are changed} xywh {280 658 90 16} labelsize 11
     }
     Fl_Button insertabove_button {
       label {Insert Above}
@@ -610,13 +662,23 @@
 }
 
 tree->redraw();}
-      tooltip {Inserts three items above the selected items} xywh {380 632 90 16} labelsize 11
+      tooltip {Inserts three items above the selected items} xywh {380 638 90 16} labelsize 11
     }
     Fl_Button rebuildtree_button {
       label {Rebuild Tree}
       callback {RebuildTree();}
-      tooltip {Rebuilds the tree with defaults} xywh {380 652 90 16} labelsize 11
+      tooltip {Rebuilds the tree with defaults} xywh {380 658 90 16} labelsize 11
     }
+    Fl_Button showselected_button {
+      label {Show Selected}
+      callback {fprintf(stderr, "--- SELECTED ITEMS\\n");
+for ( Fl_Tree_Item *item = tree->first_selected_item();
+      item;
+      item = tree->next_selected_item(item) ) {
+  fprintf(stderr, "\\t%s\\n", item->label() ? item->label() : "???");
+}} selected
+      tooltip {Clears the selected items} xywh {475 618 90 16} labelsize 11
+    }
     Fl_Button clearselected_button {
       label {Clear Selected}
       callback {Fl_Tree_Item *item=tree->first();
@@ -630,32 +692,22 @@
 }
 
 tree->redraw();}
-      tooltip {Clears the selected items} xywh {475 632 90 16} labelsize 11
+      tooltip {Clears the selected items} xywh {475 638 90 16} labelsize 11
     }
     Fl_Button clearall_button {
       label {Clear All}
       callback {tree->clear();
 tree->redraw();}
       tooltip {Clears all items
-Tests Fl_Tree::clear()} xywh {475 652 90 16} labelsize 11
+Tests Fl_Tree::clear()} xywh {475 658 90 16} labelsize 11
     }
-    Fl_Button loaddb_button {
-      label {Load Database...}
-      callback {const char *filename = fl_file_chooser("Select a Preferences style Database", "Preferences(*.prefs)", 0L);
-if (filename) {
-  tree->clear();
-  Fl_Preferences prefs(filename, 0L, 0L);
-  tree->load(prefs);
-  tree->redraw();
-}}
-      tooltip {Load the contents of an Fl_Preferences database into the tree view} xywh {380 612 90 16} labelsize 11
-    }
   }
   code {// Initialize Tree
 tree->root_label("ROOT");
 docallback_radio->value(1);	// enable docallbacks radio button
 RebuildTree();
-tree->show_self();} {}
+/*tree->show_self();*/
+} {}
   code {// FLTK stuff
 Fl::scheme("gtk+");
 window->resizable(window);
Index: test/unittest_scrollbarsize.cxx
===================================================================
--- test/unittest_scrollbarsize.cxx	(revision 7680)
+++ test/unittest_scrollbarsize.cxx	(working copy)
@@ -27,6 +27,7 @@
 
 #include <FL/Fl_Group.H>
 #include <FL/Fl_Browser.H>
+#include <FL/Fl_Tree.H>
 #include <FL/Fl_Value_Slider.H>
 
 //
@@ -34,10 +35,12 @@
 //
 class ScrollBarSizeTest : public Fl_Group {
     Fl_Browser *brow_a, *brow_b, *brow_c;
+    Fl_Tree    *tree_a, *tree_b, *tree_c;
 
     Fl_Browser *makebrowser(int X,int Y,int W,int H,const char*L=0) {
 	Fl_Browser *b = new Fl_Browser(X,Y,W,H,L);
 	b->type(FL_MULTI_BROWSER);
+	b->align(FL_ALIGN_TOP);
 	b->add("Papa");     b->add("Delta"); b->add("Hotel");
         b->add("Long entry will show h-bar");
 	b->add("Charlie");  b->add("Echo");  b->add("Foxtrot");
@@ -59,12 +62,38 @@
 	b->add("Whisky");   b->add("Zulu");
 	return(b);
     }
+    Fl_Tree *maketree(int X,int Y,int W,int H,const char*L=0) {
+	Fl_Tree *b = new Fl_Tree(X,Y,W,H,L);
+	b->type(FL_TREE_SELECT_MULTI);
+	b->align(FL_ALIGN_TOP);
+	b->add("Papa");     b->add("Delta"); b->add("Hotel");
+        b->add("Long entry will show h-bar");
+	b->add("Charlie");  b->add("Echo");  b->add("Foxtrot");
+	b->add("Golf");     b->add("Lima");  b->add("Victor");
+	b->add("Alpha");    b->add("Xray");  b->add("Yankee");
+	b->add("Oscar");    b->add("India"); b->add("Juliet");
+	b->add("Kilo");     b->add("Mike");  b->add("Sierra");
+	b->add("November"); b->add("Tango"); b->add("Quebec");
+	b->add("Bravo");    b->add("Romeo"); b->add("Uniform");
+	b->add("Whisky");   b->add("Zulu");
+	b->add("Papa");     b->add("Delta"); b->add("Hotel");
+	b->add("Charlie");  b->add("Echo");  b->add("Foxtrot");
+	b->add("Golf");     b->add("Lima");  b->add("Victor");
+	b->add("Alpha");    b->add("Xray");  b->add("Yankee");
+	b->add("Oscar");    b->add("India"); b->add("Juliet");
+	b->add("Kilo");     b->add("Mike");  b->add("Sierra");
+	b->add("November"); b->add("Tango"); b->add("Quebec");
+	b->add("Bravo");    b->add("Romeo"); b->add("Uniform");
+	b->add("Whisky");   b->add("Zulu");
+	return(b);
+    }
     void slide_cb2(Fl_Value_Slider *in) {
 	const char *label = in->label();
 	int val = in->value();
 	//fprintf(stderr, "VAL='%d'\n",val);
 	if ( strcmp(label,"A: Scroll Size") == 0 ) {
 	    brow_a->scrollbar_size(val);
+	    tree_a->scrollbar_size(val);
 	} else {
 	    Fl::scrollbar_size(val);
 	}
@@ -82,10 +111,36 @@
     // CTOR
     ScrollBarSizeTest(int X, int Y, int W, int H) : Fl_Group(X,Y,W,H) {
       begin();
-	brow_a = makebrowser(X+ 10,Y+40,100,H-170,"Browser A");
-	brow_b = makebrowser(X+120,Y+40,100,H-170,"Browser B");
-	brow_c = makebrowser(X+240,Y+40,100,H-170,"Browser C");
-        Fl_Value_Slider *slide_glob = new Fl_Value_Slider(X+100,Y+10,100,18,"Global Scroll Size");
+        //      _____________    _______________
+        //     |_____________|  |_______________|
+        //                                                ---   -----  <-- testy
+        //       brow_a      brow_b      brow_c            | 14   | 
+	//     ----------  ----------  ----------         ---     |    <-- browy
+	//     |        |  |        |  |        |          |      |
+	//     |        |  |        |  |        |          |browh |
+	//     |        |  |        |  |        |          |      |
+	//     ----------  ----------  ----------         ---   testh 
+	//                                                 |      |
+        //       tree_a      tree_b      tree_c            | 20   | 
+	//     ----------  ----------  ----------         ---     |    <-- treey
+	//     |        |  |        |  |        |          |      |
+	//     |        |  |        |  |        |          |treeh |
+	//     |        |  |        |  |        |          |      |
+	//     ----------  ----------  ----------         ---  ------
+	//                                     
+        int testy = Y+30;
+        int testh = H-130;
+	int browy = testy+14;
+	int browh = testh/2 - 20;
+	int treey = browy + browh + 20;
+	int treeh = browh;
+	brow_a = makebrowser(X+ 10,browy,100,browh,"Browser A");
+	brow_b = makebrowser(X+120,browy,100,browh,"Browser B");
+	brow_c = makebrowser(X+240,browy,100,browh,"Browser C");
+	tree_a = maketree(X+ 10,treey,100,treeh,"Tree A");
+	tree_b = maketree(X+120,treey,100,treeh,"Tree B");
+	tree_c = maketree(X+240,treey,100,treeh,"Tree C");
+        Fl_Value_Slider *slide_glob = new Fl_Value_Slider(X+100,Y,100,18,"Global Scroll Size");
         slide_glob->value(16);
         slide_glob->type(FL_HORIZONTAL);
         slide_glob->align(FL_ALIGN_LEFT);
@@ -93,7 +148,7 @@
         slide_glob->step(1.0);
         slide_glob->callback(slide_cb, (void*)this);
         slide_glob->labelsize(12);
-        Fl_Value_Slider *slide_browa = new Fl_Value_Slider(X+350,Y+10,100,18,"A: Scroll Size");
+        Fl_Value_Slider *slide_browa = new Fl_Value_Slider(X+350,Y,100,18,"A: Scroll Size");
         slide_browa->value(16);
         slide_browa->type(FL_HORIZONTAL);
         slide_browa->align(FL_ALIGN_LEFT);
