Index: FL/Fl_Tabs.H
===================================================================
--- FL/Fl_Tabs.H	(revision 9857)
+++ FL/Fl_Tabs.H	(working copy)
@@ -49,6 +49,41 @@
   gap is larger. It is easiest to lay this out in fluid, using the
   fluid browser to select each child group and resize them until
   the tabs look the way you want them to.
+
+  As of FLTK 1.3.3, Fl_Tabs() honors the value of when() based on the following
+  table. The default value for when() is FL_WHEN_RELEASE (inherited from Fl_Widget):
+
+  \code
+when() Flags  | Mouse Behavior                                  | Keyboard Behavior
+--------------|-------------------------------------------------|--------------------------------------
+    NEVER     | change: no callback                             | change: no callback
+              | no change: no callback                          | no change: no callback
+--------------|-------------------------------------------------|--------------------------------------
+   CHANGED    | change: callback on PUSH, changed()=true        | change: callback on PUSH, changed()=true
+              | no change: no callback                          | no change: no callback
+--------------|-------------------------------------------------|--------------------------------------
+ NOT_CHANGED  | change: no callback                             | change: no callback
+              | no change: callback on PUSH, changed()=false    | no change: callback on PUSH, changed()=false
+--------------|-------------------------------------------------|--------------------------------------
+   CHANGED +  | change: callback on PUSH, changed()=true        | change: callback on PUSH, changed()=true
+ NOT_CHANGED  | no change: callback on PUSH, changed()=false    | no change: callback on PUSH, changed()=false
+--------------|-------------------------------------------------|--------------------------------------
+   RELEASE    | change: callback on RELEASE, changed()=true     | change: callback on RELEASE, changed()=true
+              | no change: no callback                          | no change: no callback
+--------------|-------------------------------------------------|--------------------------------------
+   RELEASE +  | change: callback on RELEASE, changed()=true     | change: callback on RELEASE, changed()=true
+   CHANGED    | no change: no callback                          | no change: no callback
+--------------|-------------------------------------------------|--------------------------------------
+   RELEASE +  | change: no callback                             | change: no callback
+ NOT_CHANGED  | no change: callback on RELEASE, changed()=false | no change: callback on RELEASE, changed()=false
+--------------|-------------------------------------------------|--------------------------------------
+   RELEASE +  | change: callback on RELEASE, changed()=true     | change: callback on RELEASE, changed()=true
+   CHANGED +  | no change: callback on RELEASE, changed()=false | no change: callback on RELEASE, changed()=false
+ NOT_CHANGED  |                                                 |
+----------------------------------------------------------------+--------------------------------------
+
+  \endcode
+
 */
 class FL_EXPORT Fl_Tabs : public Fl_Group {
   Fl_Widget *value_;
Index: src/Fl_Tabs.cxx
===================================================================
--- src/Fl_Tabs.cxx	(revision 9857)
+++ src/Fl_Tabs.cxx	(working copy)
@@ -170,25 +170,62 @@
     }}
     /* FALLTHROUGH */
   case FL_DRAG:
-  case FL_RELEASE:
-    o = which(Fl::event_x(), Fl::event_y());
+  case FL_RELEASE: {
+    // PUSH, DRAG, RELEASE..
+    int do_cb=0;
+    if ((o = which(Fl::event_x(), Fl::event_y()))) {	// get tab group for tab user is over
+      if (o != value()) set_changed();			// if over tab, handle change
+      else              clear_changed();
+    }
     if (event == FL_RELEASE) {
-      push(0);
-      if (o && Fl::visible_focus() && Fl::focus()!=this) { 
-        Fl::focus(this);
-        redraw_tabs();
+      push(0);						// no longer 'pushed'
+      // Over a tab?
+      if (o) {
+	// Handle taking keyboard focus w/visible focus indication
+	if (Fl::visible_focus() && Fl::focus()!=this) { 
+	  Fl::focus(this);
+	  redraw_tabs();
+	}
+	if (value(o)) {					// commit to value, see if it "changed"..
+	  set_changed();				// it changed
+	  do_cb = 
+	    ( (when() & FL_WHEN_RELEASE) &&		// wants cb on RELEASE and..
+	      (when() & FL_WHEN_CHANGED)		// when changed?
+	    ) || (					// ..or..
+	      (when() == FL_WHEN_RELEASE)		// *only* WHEN_RELEASE specified? (default behavior)
+	    ) ? 1 : 0;
+	} else {
+	  clear_changed();				// no change
+	  do_cb = (when() & FL_WHEN_RELEASE &&		// wants cb when RELEASE and..
+		   when() & FL_WHEN_NOT_CHANGED)?1:0;	// ..when no change occurred?
+	}
       }
-      if (o && value(o)) {
-        Fl_Widget_Tracker wp(o);
-        set_changed();
-	do_callback();
-	if (wp.deleted()) return 1;
-      }
       Fl_Tooltip::current(o);
     } else {
-      push(o);
+      // PUSH or DRAG?
+      //    Be careful; a user can PUSH on a tab, but can abort the change
+      //    if they glide off the tab before a RELEASE. Callbacks on PUSH
+      //    will see a value change, but it may change back on RELEASE if
+      //    the user aborts the change.
+      //
+      push(o);						// DRAG|PUSH -- indicate if still on tab
+      if (o && event == FL_PUSH) {			// still on tab?
+	// See if we should do the callback
+	do_cb = 
+	  ( !(when() & FL_WHEN_RELEASE) &&		// wants callback on PUSH? (ie. NOT RELEASE)
+	    (
+	      ((when() & FL_WHEN_NOT_CHANGED) && !changed()) ||	// want cb if no change and no change occurred?
+	      ((when() & FL_WHEN_CHANGED) && changed())		// want cb if change and change occurred?
+	    )
+	  ) ? 1 : 0;
+      }
     }
-    return 1;
+    if (do_cb) {
+      Fl_Widget_Tracker wp(o);
+      do_callback();
+      if (wp.deleted()) return 1;
+    }
+    return 1; }
   case FL_MOVE: {
     int ret = Fl_Group::handle(event);
     Fl_Widget *o = Fl_Tooltip::current(), *n = o;
@@ -217,23 +254,49 @@
       if (Fl::event() == FL_UNFOCUS) return 0;
       else return 1;
     } else return Fl_Group::handle(event);
+    /* NOTREACHED */
   case FL_KEYBOARD:
+    // NOTE:
+    //  1) During key-repeat, we can receive multiple FL_KEYBOARD (FL_KEYDOWN) events without an FL_KEYUP.
+    //  2) To handle callback on RELEASE (FL_KEYDOWN) correctly, we use push() to keep track of value() changes.
+    //
     switch (Fl::event_key()) {
       case FL_Left:
-        if (child(0)->visible()) return 0;
-	for (i = 1; i < children(); i ++)
-	  if (child(i)->visible()) break;
-	value(child(i - 1));
+        // First push on tab? (ie. non key-repeat?)
+	//    Save current value (so KEYUP can see if changed() during a key-repeat) and clear_changed().
+	//
+        if (!push()) { push(value()); clear_changed(); }// first push on tab? (not key-repeat) save curr value for change
+	if (child(0)->visible()) {			// already on left-most tab?
+	  if (when() & FL_WHEN_NOT_CHANGED)		// want callback if no change?
+	    do_callback();				// do callback
+	  // return 1 to 'handle' the arrow key, or 0 to let
+	  // arrow key be used by fltk for focus navigation.
+	  return (Fl::option(Fl::OPTION_ARROW_FOCUS) ? 0 : 1);
+	}
+	for (i = 1; i < children(); i ++)		// Find currently visible child
+	  if (child(i)->visible()) break;		// found?
+	value(child(i - 1));				// select the tab left of current
 	set_changed();
-	do_callback();
+	if ( (when() & FL_WHEN_CHANGED) &&		// changed: want cb on change and..
+	    !(when() & FL_WHEN_RELEASE))		// ..on PUSH?
+	  do_callback();				// do callback
         return 1;
       case FL_Right:
-        if (child(children() - 1)->visible()) return 0;
-	for (i = 0; i < children(); i ++)
-	  if (child(i)->visible()) break;
-	value(child(i + 1));
+        if (!push()) { push(value()); clear_changed(); }// first push on tab? (not key-repeat)
+	if (child(children() - 1)->visible()) {		// already on right-most tab?
+	  if (when() & FL_WHEN_NOT_CHANGED)		// want callback if no change?
+	    do_callback();				// do callback
+	  // return 1 to 'handle' the arrow key, or 0 to let
+	  // arrow key be used by fltk for focus navigation.
+	  return (Fl::option(Fl::OPTION_ARROW_FOCUS) ? 0 : 1);
+	}
+	for (i = 0; i < children(); i ++)		// Find currently visible child
+	  if (child(i)->visible()) break;		// found?
+	value(child(i + 1));				// select the tab right of current
 	set_changed();
-	do_callback();
+	if ( (when() & FL_WHEN_CHANGED) &&		// change: want callback on change and..
+	    !(when() & FL_WHEN_RELEASE))		// ..on PUSH?
+	  do_callback();				// do callback
         return 1;
       case FL_Down:
         redraw();
@@ -242,6 +305,28 @@
         break;
     }
     return Fl_Group::handle(event);
+  case FL_KEYUP:
+    switch (Fl::event_key()) {
+      case FL_Left:
+      case FL_Right:
+	if (push()) {
+	  // See if value() changed since first KEYDN
+	  if (push() != value()) set_changed();
+	  else clear_changed();
+	  if ( when() & FL_WHEN_RELEASE) { 		// want cb on RELEASE?
+	    if ( (changed() && (			// changed and..
+	           (when() & FL_WHEN_CHANGED) ||	//    want cb on change or..
+		   (when()==FL_WHEN_RELEASE))) ||	//    want cb on RELEASE only (implies change -- legacy default)
+	         (!changed() &&				// ..or, not changed and..
+		   (when() & FL_WHEN_NOT_CHANGED)))	//    want cb on no change?
+	      do_callback();				// do callback
+	  }
+	  push(0);					// key released
+	}
+        return 1;
+        break;
+    }
+    return Fl_Group::handle(event);
   case FL_SHORTCUT:
     for (i = 0; i < children(); ++i) {
       Fl_Widget *c = child(i);
@@ -256,6 +341,7 @@
     return Fl_Group::handle(event);
   case FL_SHOW:
     value(); // update visibilities and fall through
+    /* FALLTHROUGH */
   default:
     return Fl_Group::handle(event);
 
