Index: Fl_Tree.cxx
===================================================================
--- Fl_Tree.cxx	(revision 7680)
+++ Fl_Tree.cxx	(working copy)
@@ -83,6 +83,7 @@
   _root->parent(0);				// we are root of tree
   _root->label("ROOT");
   _item_clicked = 0;
+  _item_focus = 0;
   box(FL_DOWN_BOX);
   color(FL_WHITE);
   when(FL_WHEN_CHANGED);
@@ -183,7 +184,9 @@
   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();
   
@@ -214,18 +217,131 @@
   fl_pop_clip();
 }
 
+Fl_Tree_Item *Fl_Tree::next_visible_item(Fl_Tree_Item *start, int dir) {
+  Fl_Tree_Item *item = start;
+  if ( ! item ) {				// no start item?
+    item = ( dir == FL_Up ) ? last() : first();	// start at top or bottom
+    if ( item->visible() ) return(item);	// show first 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));
+  }
+}
+
 /// Standard FLTK event handler for this widget.
+#include <FL/names.h>
 int Fl_Tree::handle(int e) {
   static Fl_Tree_Item *lastselect = 0;
   int changed = 0;
-  int ret = Fl_Group::handle(e);
+  int ret = 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_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 (visible_focus()) {
+	  switch (Fl::event_key()) {
+	    case FL_Tab:
+	      if ( _item_focus == 0 )
+		if ( Fl::event_state(FL_SHIFT) )		// Shift-Tab? UP
+		  _item_focus = next_visible_item(0, FL_Up);
+		else						// Tab? DOWN
+		  _item_focus = next_visible_item(0, FL_Down);
+	      break;
+
+	    case 0xfe20: 	// XK_ISO_Left_Tab
+	    case FL_Left:
+	    case FL_Up:
+	      if ( _item_focus == 0 )
+	        _item_focus = next_visible_item(0, FL_Up);
+	      break;
+
+	    case FL_Right: 
+	    case FL_Down:
+	    default:
+	      if ( !_item_focus ) {
+	        _item_focus = next_visible_item(0, FL_Down);
+	      }
+	      break;
+	  }
+        }
+	redraw();
+	return(1);
+      }
+      return(ret);
+
+    case FL_UNFOCUS:
+      // FLTK telling us some other widget took focus.
+      //    Redraw so that our focus box disappears.
+      //
+      Fl_Group::handle(e);					// let group have event
+      if (children() > 0 && Fl::visible_focus()) redraw();	// redraw self (clears focus box)
+      return(1);
+
+    case FL_KEYBOARD: {
+      // fprintf(stderr, "ERCODEBUG: KEYBOARD\n");
+      int eventkey = Fl::event_key();
+      if ( Fl::visible_focus() ) {
+	switch (eventkey) {
+	  case ' ': {						// space == toggle selection
+	    if ( _item_focus ) {
+	      switch ( _prefs.selectmode() ) {
+		case FL_TREE_SELECT_NONE: break;		// no selection changes
+		case FL_TREE_SELECT_SINGLE:
+		  if ( _item_focus->is_selected() ) {
+		    _item_focus->deselect_all();
+		  } else {
+		    select_only(_item_focus, 1);
+		  }
+		  redraw();
+		  break;
+		case FL_TREE_SELECT_MULTI:
+		  select_toggle(_item_focus,1);
+		  redraw();
+		  break;
+	      }
+	    }
+	    break;
+	  }
+	  case FL_Right: {	// open children (if any)
+	    if ( _item_focus ) { _item_focus->open(); redraw(); ret = 1; }
+	    break;
+	  }
+	  case FL_Left: {	// close children (if any)
+	    if ( _item_focus ) { _item_focus->close(); redraw(); ret = 1; }
+	    break;
+	  }
+	  case FL_Up:		// next item up
+	  case FL_Down: {	// next item down
+	    Fl_Tree_Item *newitem = next_visible_item(_item_focus, eventkey);	// next item up|dn
+	    if ( newitem ) {							// any found?
+	      _item_focus = newitem;						// new item becomes new focus widget
+	      if ( _prefs.selectmode() == FL_TREE_SELECT_MULTI &&		// multiselect?
+	           (Fl::event_state() & FL_SHIFT) &&				// Shift-Up?
+		    ! newitem->is_selected() ) {				// not already selected?
+		select(newitem, 1);						// extend selection..
+	      }
+	      redraw();								// redraw focus box/changes
+	    }
+	    ret = 1;
+	    break;
+	  }
+	}
+      }
+      break;
+    }
     case FL_PUSH: {
       lastselect = 0;
       item_clicked(0);				// assume no item was clicked
       Fl_Tree_Item *o = _root->find_clicked(_prefs);
       if ( o ) {
+        _item_focus = o;			// becomes new focus widget
         ret |= 1;				// handled
         if ( Fl::event_button() == FL_LEFT_MOUSE ) {
           // Was collapse icon clicked?
@@ -283,6 +399,7 @@
       if ( Fl::event_button() != FL_LEFT_MOUSE ) break;
       Fl_Tree_Item *o = _root->find_clicked(_prefs);
       if ( o ) {
+        _item_focus = o;			// becomes new focus widget
         ret |= 1;				// handled
         // Item's label clicked?
         if ( o->event_on_label(_prefs) && 
Index: Fl_Tree_Item.cxx
===================================================================
--- Fl_Tree_Item.cxx	(revision 7680)
+++ Fl_Tree_Item.cxx	(working copy)
@@ -473,8 +473,49 @@
   return(0);
 }
 
+static void draw_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_focus(tree->box(),bg,X+useroff-6,Y-3,W-useroff,H+3);
+    }
     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$".
 //
