Selaa lähdekoodia

*** empty log message ***

David Rose 25 vuotta sitten
vanhempi
sitoutus
979b4096e6
54 muutettua tiedostoa jossa 1303 lisäystä ja 757 poistoa
  1. 1 0
      panda/src/device/clientButtonDevice.h
  2. 0 10
      panda/src/device/mouse.cxx
  3. 0 3
      panda/src/device/mouse.h
  4. 5 7
      panda/src/dgraph/Sources.pp
  5. 48 1
      panda/src/dgraph/buttonEventDataAttribute.cxx
  6. 5 0
      panda/src/dgraph/buttonEventDataAttribute.h
  7. 0 6
      panda/src/dgraph/config_dgraph.cxx
  8. 3 79
      panda/src/dgraph/dataGraphTraversal.cxx
  9. 2 36
      panda/src/dgraph/dataGraphTraversal.h
  10. 46 0
      panda/src/dgraph/dataGraphTraverser.I
  11. 223 0
      panda/src/dgraph/dataGraphTraverser.cxx
  12. 55 0
      panda/src/dgraph/dataGraphTraverser.h
  13. 17 0
      panda/src/dgraph/dataNode.cxx
  14. 3 0
      panda/src/dgraph/dataNode.h
  15. 25 28
      panda/src/dgraph/describe_data_verbose.cxx
  16. 0 56
      panda/src/dgraph/modifierButtonDataAttribute.I
  17. 0 78
      panda/src/dgraph/modifierButtonDataAttribute.cxx
  18. 0 68
      panda/src/dgraph/modifierButtonDataAttribute.h
  19. 0 34
      panda/src/dgraph/modifierButtonDataTransition.I
  20. 0 80
      panda/src/dgraph/modifierButtonDataTransition.cxx
  21. 0 54
      panda/src/dgraph/modifierButtonDataTransition.h
  22. 0 28
      panda/src/display/graphicsWindow.I
  23. 0 3
      panda/src/display/graphicsWindow.h
  24. 0 25
      panda/src/display/graphicsWindowInputDevice.I
  25. 2 16
      panda/src/display/graphicsWindowInputDevice.cxx
  26. 0 4
      panda/src/display/graphicsWindowInputDevice.h
  27. 4 1
      panda/src/framework/framework.cxx
  28. 14 0
      panda/src/graph/nodeAttribute.cxx
  29. 2 0
      panda/src/graph/nodeAttribute.h
  30. 29 0
      panda/src/graph/nodeAttributes.cxx
  31. 3 1
      panda/src/graph/nodeAttributes.h
  32. 6 6
      panda/src/graph/nodeTransitionCache.cxx
  33. 3 3
      panda/src/graph/nodeTransitions.cxx
  34. 48 4
      panda/src/graph/setTransitionHelpers.I
  35. 3 23
      panda/src/putil/buttonEvent.I
  36. 0 1
      panda/src/putil/buttonEvent.cxx
  37. 1 5
      panda/src/putil/buttonEvent.h
  38. 26 0
      panda/src/putil/modifierButtons.I
  39. 12 6
      panda/src/putil/modifierButtons.cxx
  40. 6 3
      panda/src/putil/modifierButtons.h
  41. 3 1
      panda/src/tform/Sources.pp
  42. 24 15
      panda/src/tform/buttonThrower.cxx
  43. 3 0
      panda/src/tform/config_tform.cxx
  44. 126 0
      panda/src/tform/dataValve.I
  45. 369 0
      panda/src/tform/dataValve.cxx
  46. 124 0
      panda/src/tform/dataValve.h
  47. 10 26
      panda/src/tform/driveInterface.cxx
  48. 4 1
      panda/src/tform/driveInterface.h
  49. 0 7
      panda/src/tform/mouseWatcher.cxx
  50. 0 1
      panda/src/tform/mouseWatcher.h
  51. 16 15
      panda/src/tform/planarSlider.cxx
  52. 3 1
      panda/src/tform/planarSlider.h
  53. 24 20
      panda/src/tform/trackball.cxx
  54. 5 1
      panda/src/tform/trackball.h

+ 1 - 0
panda/src/device/clientButtonDevice.h

@@ -13,6 +13,7 @@
 #include <buttonHandle.h>
 #include <buttonEvent.h>
 #include <buttonEventDataAttribute.h>
+#include <pointerTo.h>
 
 ////////////////////////////////////////////////////////////////////
 //       Class : ClientButtonDevice

+ 0 - 10
panda/src/device/mouse.cxx

@@ -18,7 +18,6 @@
 ////////////////////////////////////////////////////////////////////
 TypeHandle MouseAndKeyboard::_type_handle;
 
-TypeHandle MouseAndKeyboard::_mods_type;
 TypeHandle MouseAndKeyboard::_pixel_xyz_type;
 TypeHandle MouseAndKeyboard::_xyz_type;
 TypeHandle MouseAndKeyboard::_button_events_type;
@@ -35,17 +34,14 @@ MouseAndKeyboard(GraphicsWindow *window, int device, const string& name) :
   _window(window),
   _device(device)
 {
-  _mods = new ModifierButtonDataAttribute();
   _pixel_xyz = new Vec3DataAttribute(LPoint3f(0, 0, 0));
   _xyz = new Vec3DataAttribute(LPoint3f(0, 0, 0));
   _button_events = new ButtonEventDataAttribute();
 
-  _got_mouse_attrib.set_attribute(_mods_type, _mods);
   _got_mouse_attrib.set_attribute(_pixel_xyz_type, _pixel_xyz);
   _got_mouse_attrib.set_attribute(_xyz_type, _xyz);
   _got_mouse_attrib.set_attribute(_button_events_type, _button_events);
 
-  _no_mouse_attrib.set_attribute(_mods_type, _mods);
   _no_mouse_attrib.set_attribute(_button_events_type, _button_events);
 }
 
@@ -63,9 +59,6 @@ transmit_data(NodeAttributes &data) {
     _button_events->push_back(be);
   }
 
-  // Get modifier buttons
-  _mods->set_mods(_window->get_modifier_buttons(_device));
-
   if (_window->has_pointer(_device)) {
     const MouseData &mdata = _window->get_mouse_data(_device);
 
@@ -105,9 +98,6 @@ init_type() {
   register_type(_type_handle, "MouseAndKeyboard",
 		DataNode::get_class_type());
 
-  ModifierButtonDataTransition::init_type();
-  register_data_transition(_mods_type, "ModifierButtons",
-			   ModifierButtonDataTransition::get_class_type());
   Vec3DataTransition::init_type();
   register_data_transition(_pixel_xyz_type, "PixelXYZ",
 			   Vec3DataTransition::get_class_type());

+ 0 - 3
panda/src/device/mouse.h

@@ -12,8 +12,6 @@
 #include <pandabase.h>
 
 #include <dataNode.h>
-#include <modifierButtonDataTransition.h>
-#include <modifierButtonDataAttribute.h>
 #include <vec3DataTransition.h>
 #include <vec3DataAttribute.h>
 #include <buttonEventDataTransition.h>
@@ -57,7 +55,6 @@ public:
 public:
   NodeAttributes _got_mouse_attrib;
   NodeAttributes _no_mouse_attrib;
-  PT(ModifierButtonDataAttribute) _mods;
   PT(Vec3DataAttribute) _pixel_xyz;
   PT(Vec3DataAttribute) _xyz;
   PT(ButtonEventDataAttribute) _button_events;

+ 5 - 7
panda/src/dgraph/Sources.pp

@@ -11,7 +11,9 @@
     buttonEventDataAttribute.h buttonEventDataTransition.I \
     buttonEventDataTransition.cxx buttonEventDataTransition.h \
     config_dgraph.cxx config_dgraph.h dataGraphTraversal.cxx \
-    dataGraphTraversal.h dataNode.cxx dataNode.h dataRelation.I \
+    dataGraphTraversal.h \
+    dataGraphTraverser.I dataGraphTraverser.cxx dataGraphTraverser.h \
+    dataNode.cxx dataNode.h dataRelation.I \
     dataRelation.N dataRelation.cxx dataRelation.h \
     describe_data_verbose.cxx describe_data_verbose.h \
     doubleDataAttribute.I doubleDataAttribute.cxx doubleDataAttribute.h \
@@ -24,9 +26,6 @@
     intDataTransition.h matrixDataAttribute.I matrixDataAttribute.cxx \
     matrixDataAttribute.h matrixDataTransition.I \
     matrixDataTransition.cxx matrixDataTransition.h \
-    modifierButtonDataAttribute.I modifierButtonDataAttribute.cxx \
-    modifierButtonDataAttribute.h modifierButtonDataTransition.I \
-    modifierButtonDataTransition.cxx modifierButtonDataTransition.h \
     vec3DataAttribute.I vec3DataAttribute.cxx vec3DataAttribute.h \
     vec3DataTransition.I vec3DataTransition.cxx vec3DataTransition.h \
     vec4DataAttribute.I vec4DataAttribute.cxx vec4DataAttribute.h \
@@ -35,7 +34,8 @@
   #define INSTALL_HEADERS \
     buttonEventDataAttribute.I buttonEventDataAttribute.h \
     buttonEventDataTransition.I buttonEventDataTransition.h \
-    dataGraphTraversal.h dataNode.h dataRelation.I dataRelation.h \
+    dataGraphTraversal.h \
+    dataNode.h dataRelation.I dataRelation.h \
     describe_data_verbose.h doubleDataAttribute.I doubleDataAttribute.h \
     doubleDataTransition.I doubleDataTransition.h \
     doublePtrDataAttribute.I doublePtrDataAttribute.h \
@@ -43,8 +43,6 @@
     intDataAttribute.I intDataAttribute.h intDataTransition.I \
     intDataTransition.h matrixDataAttribute.I matrixDataAttribute.h \
     matrixDataTransition.I matrixDataTransition.h \
-    modifierButtonDataAttribute.I modifierButtonDataAttribute.h \
-    modifierButtonDataTransition.I modifierButtonDataTransition.h \
     numericDataAttribute.I numericDataAttribute.h \
     numericDataTransition.I numericDataTransition.h \
     pointerDataAttribute.I pointerDataAttribute.h \

+ 48 - 1
panda/src/dgraph/buttonEventDataAttribute.cxx

@@ -7,12 +7,27 @@
 #include "buttonEventDataTransition.h"
 
 #include <indent.h>
-
+#include <modifierButtons.h>
 #include <algorithm>
 
 TypeHandle ButtonEventDataAttribute::_type_handle;
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonEventDataAttribute::update_mods
+//       Access: Public
+//  Description: Updates the indicated ModifierButtons object with all
+//               of the button up/down transitions indicated in the
+//               queue.
+////////////////////////////////////////////////////////////////////
+void ButtonEventDataAttribute::
+update_mods(ModifierButtons &mods) const {
+  Buttons::const_iterator bi;
+  for (bi = _buttons.begin(); bi != _buttons.end(); ++bi) {
+    mods.add_event(*bi);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ButtonEventDataAttribute::make_copy
 //       Access: Public, Virtual
@@ -43,6 +58,38 @@ get_handle() const {
   return ButtonEventDataTransition::get_class_type();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonEventDataAttribute::merge
+//       Access: Public, Virtual
+//  Description: Attempts to merge this attribute with the other one,
+//               if that makes sense to do.  Returns a new
+//               NodeAttribute pointer that represents the merge, or
+//               if the merge is not possible, returns the "other"
+//               pointer unchanged (which is the result of the merge).
+////////////////////////////////////////////////////////////////////
+NodeAttribute *ButtonEventDataAttribute::
+merge(const NodeAttribute *other) const {
+  const ButtonEventDataAttribute *oa;
+  DCAST_INTO_R(oa, other, NULL);
+
+  if (_buttons.empty()) {
+    return (ButtonEventDataAttribute *)oa;
+
+  } else if (oa->_buttons.empty()) {
+    return (ButtonEventDataAttribute *)this;
+
+  } else {
+    // We have to create a new data attribute that includes both sets
+    // of buttons.
+    ButtonEventDataAttribute *new_attrib = 
+      new ButtonEventDataAttribute(*this);
+
+    new_attrib->_buttons.insert(new_attrib->_buttons.end(),
+				oa->_buttons.begin(), oa->_buttons.end());
+    return new_attrib;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ButtonEventDataAttribute::output
 //       Access: Public, Virtual

+ 5 - 0
panda/src/dgraph/buttonEventDataAttribute.h

@@ -14,6 +14,7 @@
 #include <vector>
 
 class ButtonEventDataTransition;
+class ModifierButtons;
 
 ////////////////////////////////////////////////////////////////////
 // 	 Class : ButtonEventDataAttribute
@@ -42,12 +43,16 @@ public:
   INLINE void clear();
   INLINE void push_back(const ButtonEvent &event);
 
+  void update_mods(ModifierButtons &mods) const;
+
 public:
   virtual NodeAttribute *make_copy() const;
   virtual NodeAttribute *make_initial() const;
   
   virtual TypeHandle get_handle() const;
 
+  virtual NodeAttribute *merge(const NodeAttribute *other) const;
+
   virtual void output(ostream &out) const;
   virtual void write(ostream &out, int indent_level = 0) const;
 

+ 0 - 6
panda/src/dgraph/config_dgraph.cxx

@@ -16,9 +16,6 @@
 #include "matrixDataAttribute.h"
 #include "buttonEventDataTransition.h"
 #include "buttonEventDataAttribute.h"
-#include "modifierButtonDataTransition.h"
-#include "modifierButtonDataAttribute.h"
-#include "dataGraphTraversal.h"
 
 #include <dconfig.h>
 
@@ -38,9 +35,6 @@ ConfigureFn(config_dgraph) {
   MatrixDataAttribute::init_type();
   ButtonEventDataTransition::init_type();
   ButtonEventDataAttribute::init_type();
-  ModifierButtonDataTransition::init_type();
-  ModifierButtonDataAttribute::init_type();
-  DataGraphTraversal::init_spam_flag();
 
   DataRelation::register_with_factory();
 }

+ 3 - 79
panda/src/dgraph/dataGraphTraversal.cxx

@@ -4,82 +4,7 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "dataGraphTraversal.h"
-#include "dataNode.h"
-#include "dataRelation.h"
-#include "intDataTransition.h"
-#include "intDataAttribute.h"
-#include "config_dgraph.h"
-#include "describe_data_verbose.h"
-
-#include <renderRelation.h>
-#include <dftraverser.h>
-#include <node.h>
-#include <typeHandle.h>
-
-
-// Here's the static member of DataGraphTraversal.
-TypeHandle DataGraphTraversal::_spam_flag_type;
-
-
-  
-////////////////////////////////////////////////////////////////////
-//     Function: DataGraphTraversal::init_spam_flag
-//       Access: Public, Static
-//  Description: This function is called during static
-//               initialization and registers a convenient type handle
-//               for the spam flag.
-////////////////////////////////////////////////////////////////////
-void DataGraphTraversal::
-init_spam_flag() {
-  // We shouldn't try to call this function more than once.  This
-  // function is only to be called during static initialization.
-  nassertv(_spam_flag_type == TypeHandle::none());
-  IntDataTransition::init_type();
-  register_type(_spam_flag_type, "SpamFlagTransition", 
-		IntDataTransition::get_class_type());
-}
-  
-////////////////////////////////////////////////////////////////////
-//     Function: DataGraphVisitor::reached_node
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-bool DataGraphVisitor::
-reached_node(Node *node, AllAttributesWrapper &state, NullLevelState &) {
-  // Is the spam flag set?
-  bool has_spam_flag = 
-    state.has_attribute(DataGraphTraversal::_spam_flag_type);
-
-  if (has_spam_flag && dgraph_cat.is_info()) {
-    dgraph_cat.info() << "Sending into " << *node << " {\n";
-    describe_data_verbose(dgraph_cat.info(false), state.get_attributes(), 2);
-    dgraph_cat.info(false) << "}\n";
-  }
-
-  if (node->is_of_type(DataNode::get_class_type())) {
-    DataNode *dnode = DCAST(DataNode, node);
-
-    // Now let the data node process the data itself.
-    dnode->transmit_data(state.get_attributes());
-
-    if (dnode->get_spam_mode() && !has_spam_flag) {
-      // If the node we pass through has the spam flag set, then we'll
-      // set it on our current state, and insist that it gets through
-      // to all our children.
-      state.set_attribute(DataGraphTraversal::_spam_flag_type, 
-			  new IntDataAttribute);
-      has_spam_flag = true;
-    }
-  }
-
-  if (has_spam_flag && !node->_children.empty() && 
-      dgraph_cat.is_info()) {
-    dgraph_cat.info() << "Receiving from " << *node << " {\n";
-    describe_data_verbose(dgraph_cat.info(false), state.get_attributes(), 2);
-    dgraph_cat.info(false) << "}\n";
-  }
-  return true;
-}
+#include "dataGraphTraverser.h"
 
   
 ////////////////////////////////////////////////////////////////////
@@ -90,7 +15,6 @@ reached_node(Node *node, AllAttributesWrapper &state, NullLevelState &) {
 //               downwards.
 ////////////////////////////////////////////////////////////////////
 void traverse_data_graph(Node *root) {
-  DataGraphVisitor dgv;
-  df_traverse(root, dgv, AllAttributesWrapper(), 
-	      NullLevelState(), DataRelation::get_class_type());
+  DataGraphTraverser dgt;
+  dgt.traverse(root);
 }

+ 2 - 36
panda/src/dgraph/dataGraphTraversal.h

@@ -17,46 +17,12 @@
 // This is the main interface to the data graph.  Traversing the data
 // graph transmits data down the graph--reading data from devices and
 // sending it to the DataNodes that want it.
+//
 ////////////////////////////////////////////////////////////////////
 
 #include <pandabase.h>
 
-#include <traverserVisitor.h>
-#include <allTransitionsWrapper.h>
-#include <allAttributesWrapper.h>
-#include <nullLevelState.h>
-
-
-////////////////////////////////////////////////////////////////////
-// 	 Class : DataGraphTraversal
-// Description : This is mainly a holder for the static
-//               _spam_flag_type TypeHandle, which is a registered
-//               NumericDataAttribute that keeps track of the state of
-//               the spam flag during data graph traversal.  The class
-//               otherwise doesn't do anything useful, but see the
-//               traverse_data_graph() function, below.
-////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA DataGraphTraversal {
-public:
-  static void init_spam_flag();
-
-  static TypeHandle _spam_flag_type;
-};
-
-
-////////////////////////////////////////////////////////////////////
-// 	 Class : DataGraphVisitor
-// Description : The TraverserVisitor to handle data graph traversals.
-//               This manages the spam flag, and may eventually handle
-//               merging of data from different parents into a single
-//               node.
-////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA DataGraphVisitor : 
-  public TraverserVisitor<AllTransitionsWrapper, NullLevelState> {
-public:
-  bool reached_node(Node *node, AllAttributesWrapper &state, NullLevelState &);
-};
-
+class Node;
  
 ////////////////////////////////////////////////////////////////////
 //     Function: traverse_data_graph

+ 46 - 0
panda/src/dgraph/dataGraphTraverser.I

@@ -0,0 +1,46 @@
+// Filename: dataGraphTraverser.I
+// Created by:  drose (05Feb01)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataGraphTraverser::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE DataGraphTraverser::
+DataGraphTraverser() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataGraphTraverser::traverse
+//       Access: Public
+//  Description: Fires off the traversal, beginning at the indicated
+//               node.
+////////////////////////////////////////////////////////////////////
+INLINE void DataGraphTraverser::
+traverse(Node *node) {
+  NodeAttributes empty;
+  r_traverse(node, empty, false);
+  resume_all();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataGraphTraverser::traverse_below
+//       Access: Public
+//  Description: Fires off the traversal, beginning *below* the
+//               indicated node.  The transmit_data() function is not
+//               called on the starting node; instead, the indicated
+//               data attributes are taken as the output of that node.
+//
+//               If the node defines transmit_data_per_child(), that
+//               function is still called, once for each child as
+//               usual.
+////////////////////////////////////////////////////////////////////
+INLINE void DataGraphTraverser::
+traverse_below(Node *node, const NodeAttributes &data) {
+  NodeAttributes copy = data;
+  r_traverse_below(node, copy, false);
+  resume_all();
+}

+ 223 - 0
panda/src/dgraph/dataGraphTraverser.cxx

@@ -0,0 +1,223 @@
+// Filename: dataGraphTraverser.cxx
+// Created by:  drose (05Feb01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "dataGraphTraverser.h"
+#include "dataRelation.h"
+#include "dataNode.h"
+#include "config_dgraph.h"
+#include "describe_data_verbose.h"
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataGraphTraverser::r_traverse
+//       Access: Private
+//  Description: The recursive implementation of traverse().
+////////////////////////////////////////////////////////////////////
+void DataGraphTraverser::
+r_traverse(Node *node, NodeAttributes &data, bool has_spam_mode) {
+  DataNode *data_node = (DataNode *)NULL;
+  if (node->is_of_type(DataNode::get_class_type())) {
+    DCAST_INTO_V(data_node, node);
+
+#ifndef NDEBUG
+    has_spam_mode = has_spam_mode || data_node->get_spam_mode();
+#endif
+  }
+
+  int num_parents = node->get_num_parents(DataRelation::get_class_type());
+
+  if (num_parents > 1) {
+    // This node is multiply parented, so we must save it for later.
+    save(node, data, has_spam_mode, num_parents);
+
+  } else {
+
+    // Now ask the node to transmit its overall data.
+    if (data_node != (DataNode *)NULL) {
+#ifndef NDEBUG
+      if (has_spam_mode && dgraph_cat.is_info()) {
+	dgraph_cat.info() << "Sending into " << *node << " {\n";
+	describe_data_verbose(dgraph_cat.info(false), data, 2);
+	dgraph_cat.info(false) << "}\n";
+	
+      } else if (dgraph_cat.is_spam()) {
+	dgraph_cat.spam() << "Sending into " << *node << " {\n";
+	describe_data_verbose(dgraph_cat.spam(false), data, 2);
+	dgraph_cat.spam(false) << "}\n";
+      }
+#endif
+
+      data_node->transmit_data(data);
+    }
+
+    r_traverse_below(node, data, has_spam_mode);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataGraphTraverser::r_traverse_below
+//       Access: Private
+//  Description: Resumes the traversal below the indicated node, given
+//               the output from a previous call to
+//               Node::transmit_data().
+////////////////////////////////////////////////////////////////////
+void DataGraphTraverser::
+r_traverse_below(Node *node, NodeAttributes &data, bool has_spam_mode) {
+  DataNode *data_node = (DataNode *)NULL;
+  if (node->is_of_type(DataNode::get_class_type())) {
+    DCAST_INTO_V(data_node, node);
+
+#ifndef NDEBUG
+    has_spam_mode = has_spam_mode || data_node->get_spam_mode();
+#endif
+  }
+
+  int num_children = node->get_num_children(DataRelation::get_class_type());
+
+  if (num_children > 0) {
+    // For the first n - 1 children we need to make a copy of our
+    // NodeAttributes for each one--this allows our children to modify
+    // the set freely without affecting its siblings.
+  
+    for (int i = 0; i < num_children - 1; i++) {
+      NodeAttributes copy = data;
+      if (data_node != (DataNode *)NULL) {
+	data_node->transmit_data_per_child(copy, i);
+      }
+      
+      NodeRelation *arc = 
+	node->get_child(DataRelation::get_class_type(), i);
+      r_traverse(arc->get_child(), copy, has_spam_mode);
+    }
+    
+    // For the last child, we don't have to bother making a copy since
+    // no one cares any more.  This is a slight optimization.
+    if (data_node != (DataNode *)NULL) {
+      data_node->transmit_data_per_child(data, num_children - 1);
+    }
+    
+    NodeRelation *arc = 
+      node->get_child(DataRelation::get_class_type(), num_children - 1);
+
+    if (dgraph_cat.is_spam()) {
+      dgraph_cat.spam() << "Traversing " << *arc << "\n";
+    }
+
+    r_traverse(arc->get_child(), data, has_spam_mode);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataGraphTraverser::save
+//       Access: Private
+//  Description: Saves the current state for later, until we have
+//               extracted the data from all the parents of this node.
+//               When the last parent is accounted for, resumes the
+//               traversal for this node and removes it from the saved
+//               queue.
+////////////////////////////////////////////////////////////////////
+void DataGraphTraverser::
+save(Node *node, const NodeAttributes &data, bool has_spam_mode,
+     int num_parents) {
+   
+  States::iterator si;
+  si = _saved_states.find(node);
+
+  if (si != _saved_states.end()) {
+    SavedState &state = (*si).second;
+    state._data.merge_from(state._data, data);
+    state._has_spam_mode = state._has_spam_mode || has_spam_mode;
+    state._num_parents_so_far++;
+
+    if (dgraph_cat.is_spam()) {
+      dgraph_cat.spam()
+	<< "Encountered " << *node << " with " << num_parents
+	<< " parents; " << state._num_parents_so_far
+	<< " processed so far.\n";
+    }
+
+    nassertv(state._num_parents_so_far <= num_parents);
+    if (state._num_parents_so_far == num_parents) {
+      // All parents are accounted for, so resume traversal!
+      resume(node, state);
+      _saved_states.erase(si);
+    }
+    return;
+  }
+
+  // This node has not been encountered before, so save an entry for
+  // it.
+  if (dgraph_cat.is_spam()) {
+    dgraph_cat.spam()
+      << "Encountered " << *node << " with " << num_parents
+      << " parents.\n";
+  }
+
+  SavedState &state = _saved_states[node];
+  state._data = data;
+  state._has_spam_mode = has_spam_mode;
+  state._num_parents_so_far = 1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataGraphTraverser::resume
+//       Access: Private
+//  Description: Resumes traversal from a previously-saved state.
+////////////////////////////////////////////////////////////////////
+void DataGraphTraverser::
+resume(Node *node, DataGraphTraverser::SavedState &state) {
+  DataNode *data_node = (DataNode *)NULL;
+  if (node->is_of_type(DataNode::get_class_type())) {
+    DCAST_INTO_V(data_node, node);
+
+#ifndef NDEBUG
+    if (state._has_spam_mode && dgraph_cat.is_info()) {
+      dgraph_cat.info() << "Sending into " << *node << " {\n";
+      describe_data_verbose(dgraph_cat.info(false), state._data, 2);
+      dgraph_cat.info(false) << "}\n";
+      
+    } else if (dgraph_cat.is_spam()) {
+      dgraph_cat.spam() << "Sending into " << *node << " {\n";
+      describe_data_verbose(dgraph_cat.spam(false), state._data, 2);
+      dgraph_cat.spam(false) << "}\n";
+    }
+#endif
+    data_node->transmit_data(state._data);
+  }
+
+  r_traverse_below(node, state._data, state._has_spam_mode);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataGraphTraverser::resume_all
+//       Access: Private
+//  Description: Called after the traversal has completed, this
+//               resumes all of the unresumed saved states.  There
+//               shouldn't actually be any such things, unless a node
+//               is multiply parented to a node outside of the data
+//               graph.
+////////////////////////////////////////////////////////////////////
+void DataGraphTraverser::
+resume_all() {
+  while (!_saved_states.empty()) {
+    States::iterator si = _saved_states.begin();
+    Node *node = (*si).first;
+    SavedState &state = (*si).second;
+
+    dgraph_cat.warning()
+      << *node << " is parented both inside and outside of the data graph:\n";
+    int num_parents = node->get_num_parents(DataRelation::get_class_type());
+    for (int i = 0; i < num_parents; i++) {
+      NodeRelation *arc = node->get_parent(DataRelation::get_class_type(), i);
+      dgraph_cat.warning(false)
+	<< "  " << *arc << "\n";
+    }
+
+    resume(node, state);
+    _saved_states.erase(si);
+  }
+}
+

+ 55 - 0
panda/src/dgraph/dataGraphTraverser.h

@@ -0,0 +1,55 @@
+// Filename: dataGraphTraverser.h
+// Created by:  drose (05Feb01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef DATAGRAPHTRAVERSER_H
+#define DATAGRAPHTRAVERSER_H
+
+#include <pandabase.h>
+
+#include <nodeAttributes.h>
+
+class Node;
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : DataGraphTraverser
+// Description : This class manages the traversal of the data graph,
+//               so that all data attributes generated by nodes in the
+//               graph are transmitted to nodes below them.
+//
+//               A bit of logic is employed to ensure that if a node
+//               is assigned to two parents, the data from both
+//               parents is merged before passing it to the child.
+//
+//               This class need not be exported from PANDA.DLL.
+////////////////////////////////////////////////////////////////////
+class DataGraphTraverser {
+public:
+  INLINE DataGraphTraverser();
+
+  INLINE void traverse(Node *node);
+  INLINE void traverse_below(Node *node, const NodeAttributes &data);
+
+private:
+  class SavedState {
+  public:
+    NodeAttributes _data;
+    bool _has_spam_mode;
+    int _num_parents_so_far;
+  };
+
+  void r_traverse(Node *node, NodeAttributes &data, bool has_spam_mode);
+  void r_traverse_below(Node *node, NodeAttributes &data, bool has_spam_mode);
+  void save(Node *node, const NodeAttributes &data, bool has_spam_mode,
+	    int num_parents);
+  void resume(Node *node, SavedState &state);
+  void resume_all();
+
+  typedef map<Node *, SavedState> States;
+  States _saved_states;
+};
+
+#include "dataGraphTraverser.I"
+
+#endif

+ 17 - 0
panda/src/dgraph/dataNode.cxx

@@ -16,6 +16,23 @@ DataNode::
 DataNode(const string &name) : NamedNode(name) {
   _spam_mode = false;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataNode::transmit_data_per_child
+//       Access: Public, Virtual
+//  Description: Should be overridden in a derived class that wants to
+//               send a different data stream to each child.
+//               Normally, a node only overrides transmit_data(),
+//               which takes a set of input data attributes and
+//               generates a set of output data attributes.  A node
+//               may also override transmit_data_per_child(), which is
+//               called after transmit_data(), once per child; this
+//               function may be used to send individual data
+//               attributes to each child.
+////////////////////////////////////////////////////////////////////
+void DataNode::
+transmit_data_per_child(NodeAttributes &, int) {
+}
   
 ////////////////////////////////////////////////////////////////////
 //     Function: DataNode::set_spam_mode

+ 3 - 0
panda/src/dgraph/dataNode.h

@@ -70,6 +70,9 @@ public:
   virtual void
   transmit_data(NodeAttributes &data)=0;
 
+  virtual void
+  transmit_data_per_child(NodeAttributes &data, int child_index);
+
 PUBLISHED:
   void set_spam_mode(bool flag);
   bool get_spam_mode() const;

+ 25 - 28
panda/src/dgraph/describe_data_verbose.cxx

@@ -26,35 +26,32 @@ describe_data_verbose(ostream &out, const NodeAttributes &state,
   NodeAttributes::const_iterator nai;
 
   for (nai = state.begin(); nai != state.end(); ++nai) {
-    // We'll exclude the spam flag.  No one wants to see that.
-    TypeHandle type = (*nai).first;
-    if (type != DataGraphTraversal::_spam_flag_type) {
-      const PT(NodeAttribute) &attrib = (*nai).second;
-      if (attrib != (NodeAttribute *)NULL) {
-	// Now extract the type name out of the type flag.
-	string actual_name = type.get_name();
-	string::size_type underscore = actual_name.rfind('_');
-	string name;
-	if (underscore == string::npos) {
-	  // There was no underscore, so this name wasn't created via
-	  // register_data_transition().  Huh.  Well, that's legitimate
-	  // (if unexpected), so just print the whole name.
-	  name = actual_name;
-	} else {
-	  // Assume that, if the name was created via
-	  // register_data_transition(), the original name was the part
-	  // before the last underscore.
-	  name = actual_name.substr(0, underscore);
-	}
-	
-	indent(out, indent_level) << name;
-	if ((int)name.length() < data_indent_level) {
-	  indent(out, data_indent_level - name.length());
-	} else {
-	  out << "  ";
-	}
-	out << *attrib << "\n";
+    const PT(NodeAttribute) &attrib = (*nai).second;
+    if (attrib != (NodeAttribute *)NULL) {
+      // Now extract the type name out of the type flag.
+      TypeHandle type = (*nai).first;
+      string actual_name = type.get_name();
+      string::size_type underscore = actual_name.rfind('_');
+      string name;
+      if (underscore == string::npos) {
+	// There was no underscore, so this name wasn't created via
+	// register_data_transition().  Huh.  Well, that's legitimate
+	// (if unexpected), so just print the whole name.
+	name = actual_name;
+      } else {
+	// Assume that, if the name was created via
+	// register_data_transition(), the original name was the part
+	// before the last underscore.
+	name = actual_name.substr(0, underscore);
       }
+      
+      indent(out, indent_level) << name;
+      if ((int)name.length() < data_indent_level) {
+	indent(out, data_indent_level - name.length());
+      } else {
+	out << "  ";
+      }
+      out << *attrib << "\n";
     }
   }
 }

+ 0 - 56
panda/src/dgraph/modifierButtonDataAttribute.I

@@ -1,56 +0,0 @@
-// Filename: modifierButtonDataAttribute.I
-// Created by:  drose (27Mar00)
-// 
-////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////
-//     Function: ModifierButtonDataAttribute::Constructor
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE ModifierButtonDataAttribute::
-ModifierButtonDataAttribute() {
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ModifierButtonDataAttribute::Copy Constructor
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE ModifierButtonDataAttribute::
-ModifierButtonDataAttribute(const ModifierButtonDataAttribute &copy) :
-  NodeAttribute(copy),
-  _mods(copy._mods)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ModifierButtonDataAttribute::Copy Assignment Operator
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE void ModifierButtonDataAttribute::
-operator = (const ModifierButtonDataAttribute &copy) {
-  NodeAttribute::operator = (copy);
-  _mods = copy._mods;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ModifierButtonDataAttribute::set_mods
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE void ModifierButtonDataAttribute::
-set_mods(const ModifierButtons &mods) {
-  _mods = mods;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ModifierButtonDataAttribute::set_mods
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE const ModifierButtons &ModifierButtonDataAttribute::
-get_mods() const {
-  return _mods;
-}

+ 0 - 78
panda/src/dgraph/modifierButtonDataAttribute.cxx

@@ -1,78 +0,0 @@
-// Filename: modifierButtonDataAttribute.cxx
-// Created by:  drose (27Mar00)
-// 
-////////////////////////////////////////////////////////////////////
-
-#include "modifierButtonDataAttribute.h"
-#include "modifierButtonDataTransition.h"
-
-#include <indent.h>
-
-TypeHandle ModifierButtonDataAttribute::_type_handle;
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ModifierButtonDataAttribute::make_copy
-//       Access: Public, Virtual
-//  Description: 
-////////////////////////////////////////////////////////////////////
-NodeAttribute *ModifierButtonDataAttribute::
-make_copy() const {
-  return new ModifierButtonDataAttribute(*this);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ModifierButtonDataAttribute::make_initial
-//       Access: Public, Virtual
-//  Description: 
-////////////////////////////////////////////////////////////////////
-NodeAttribute *ModifierButtonDataAttribute::
-make_initial() const {
-  return new ModifierButtonDataAttribute;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ModifierButtonDataAttribute::get_handle
-//       Access: Public, Virtual
-//  Description: 
-////////////////////////////////////////////////////////////////////
-TypeHandle ModifierButtonDataAttribute::
-get_handle() const {
-  return ModifierButtonDataTransition::get_class_type();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ModifierButtonDataAttribute::output
-//       Access: Public, Virtual
-//  Description: 
-////////////////////////////////////////////////////////////////////
-void ModifierButtonDataAttribute::
-output(ostream &out) const {
-  out << _mods;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ModifierButtonDataAttribute::write
-//       Access: Public, Virtual
-//  Description: 
-////////////////////////////////////////////////////////////////////
-void ModifierButtonDataAttribute::
-write(ostream &out, int indent_level) const {
-  indent(out, indent_level) << _mods << "\n";
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ModifierButtonDataAttribute::internal_compare_to
-//       Access: Protected, Virtual
-//  Description: 
-////////////////////////////////////////////////////////////////////
-int ModifierButtonDataAttribute::
-internal_compare_to(const NodeAttribute *other) const {
-  const ModifierButtonDataAttribute *ot;
-  DCAST_INTO_R(ot, other, false);
-
-  if (_mods != ot->_mods) {
-    return (_mods < ot->_mods) ? -1 : 1;
-  }
-  return 0;
-}

+ 0 - 68
panda/src/dgraph/modifierButtonDataAttribute.h

@@ -1,68 +0,0 @@
-// Filename: modifierButtonDataAttribute.h
-// Created by:  drose (27Mar00)
-// 
-////////////////////////////////////////////////////////////////////
-
-#ifndef MODIFIERBUTTONDATAATTRIBUTE_H
-#define MODIFIERBUTTONDATAATTRIBUTE_H
-
-#include <pandabase.h>
-
-#include <nodeAttribute.h>
-#include <modifierButtons.h>
-
-class ModifierButtonDataTransition;
-
-////////////////////////////////////////////////////////////////////
-// 	 Class : ModifierButtonDataAttribute
-// Description : This data graph attribute stores the current state of
-//               some various modifier buttons (e.g. shift, control,
-//               alt, and the mouse buttons), as generated for
-//               instance by a GraphicsWindow.
-////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA ModifierButtonDataAttribute : public NodeAttribute {
-public:
-  INLINE ModifierButtonDataAttribute();
-  INLINE ModifierButtonDataAttribute(const ModifierButtonDataAttribute &copy);
-  INLINE void operator = (const ModifierButtonDataAttribute &copy);
-
-public:
-  INLINE void set_mods(const ModifierButtons &mods);
-  INLINE const ModifierButtons &get_mods() const;
-
-  virtual NodeAttribute *make_copy() const;
-  virtual NodeAttribute *make_initial() const;
-  
-  virtual TypeHandle get_handle() const;
-
-  virtual void output(ostream &out) const;
-  virtual void write(ostream &out, int indent_level = 0) const;
-
-protected:
-  virtual int internal_compare_to(const NodeAttribute *other) const;
-
-private:
-  ModifierButtons _mods;
-
-public:
-  virtual TypeHandle get_type() const {
-    return get_class_type();
-  }
-  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
-  static TypeHandle get_class_type() {
-    return _type_handle;
-  }
-  static void init_type() {
-    NodeAttribute::init_type();
-    register_type(_type_handle, "ModifierButtonDataAttribute",
-		  NodeAttribute::get_class_type());
-  }
-
-private:
-  static TypeHandle _type_handle;
-friend class ModifierButtonDataTransition;
-};
-
-#include "modifierButtonDataAttribute.I"
-
-#endif

+ 0 - 34
panda/src/dgraph/modifierButtonDataTransition.I

@@ -1,34 +0,0 @@
-// Filename: modifierButtonDataTransition.I
-// Created by:  drose (27Mar00)
-// 
-////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////
-//     Function: ModifierButtonDataTransition::Constructor
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE ModifierButtonDataTransition::
-ModifierButtonDataTransition() {
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ModifierButtonDataTransition::Copy Constructor
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE ModifierButtonDataTransition::
-ModifierButtonDataTransition(const ModifierButtonDataTransition &copy) :
-  NodeTransition(copy)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ModifierButtonDataTransition::Copy Assignment Operator
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE void ModifierButtonDataTransition::
-operator = (const ModifierButtonDataTransition &copy) {
-  NodeTransition::operator = (copy);
-}

+ 0 - 80
panda/src/dgraph/modifierButtonDataTransition.cxx

@@ -1,80 +0,0 @@
-// Filename: modifierButtonDataTransition.cxx
-// Created by:  drose (01Mar00)
-// 
-////////////////////////////////////////////////////////////////////
-
-#include "modifierButtonDataTransition.h"
-#include "modifierButtonDataAttribute.h"
-
-TypeHandle ModifierButtonDataTransition::_type_handle;
-
-////////////////////////////////////////////////////////////////////
-//     Function: ModifierButtonDataTransition::make_copy
-//       Access: Public, Virtual
-//  Description: 
-////////////////////////////////////////////////////////////////////
-NodeTransition *ModifierButtonDataTransition::
-make_copy() const {
-  return new ModifierButtonDataTransition(*this);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ModifierButtonDataTransition::make_attrib
-//       Access: Public, Virtual
-//  Description: 
-////////////////////////////////////////////////////////////////////
-NodeAttribute *ModifierButtonDataTransition::
-make_attrib() const {
-  return new ModifierButtonDataAttribute;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ModifierButtonDataTransition::compose
-//       Access: Public, Virtual
-//  Description: Returns a new transition that corresponds to the
-//               composition of this transition with the second
-//               transition (which must be of an equivalent type).
-//               This may return the same pointer as either source
-//               transition.  Applying the transition returned from
-//               this function to an attribute attribute will produce
-//               the same effect as applying each transition
-//               separately.
-////////////////////////////////////////////////////////////////////
-NodeTransition *ModifierButtonDataTransition::
-compose(const NodeTransition *) const {
-  return NULL;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ModifierButtonDataTransition::invert
-//       Access: Public, Virtual
-//  Description: 
-////////////////////////////////////////////////////////////////////
-NodeTransition *ModifierButtonDataTransition::
-invert() const {
-  return NULL;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ModifierButtonDataTransition::apply
-//       Access: Public, Virtual
-//  Description: Returns a new attribute (or possibly the same
-//               attribute) that represents the effect of applying this
-//               indicated transition to the indicated attribute.  The
-//               source attribute may be NULL, indicating the initial
-//               attribute.
-////////////////////////////////////////////////////////////////////
-NodeAttribute *ModifierButtonDataTransition::
-apply(const NodeAttribute *attrib) const {
-  return (NodeAttribute *)attrib;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ModifierButtonDataTransition::internal_compare_to
-//       Access: Protected, Virtual
-//  Description: 
-////////////////////////////////////////////////////////////////////
-int ModifierButtonDataTransition::
-internal_compare_to(const NodeTransition *) const {
-  return 0;
-}

+ 0 - 54
panda/src/dgraph/modifierButtonDataTransition.h

@@ -1,54 +0,0 @@
-// Filename: modifierButtonDataTransition.h
-// Created by:  drose (01Mar00)
-// 
-////////////////////////////////////////////////////////////////////
-
-#ifndef MODIFIERBUTTONDATATRANSITION_H
-#define MODIFIERBUTTONDATATRANSITION_H
-
-#include <pandabase.h>
-
-#include <nodeTransition.h>
-
-////////////////////////////////////////////////////////////////////
-// 	 Class : ModifierButtonDataTransition
-// Description : 
-////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA ModifierButtonDataTransition : public NodeTransition {
-public:
-  INLINE ModifierButtonDataTransition();
-  INLINE ModifierButtonDataTransition(const ModifierButtonDataTransition &copy);
-  INLINE void operator = (const ModifierButtonDataTransition &copy);
-
-public:
-  virtual NodeTransition *make_copy() const;
-  virtual NodeAttribute *make_attrib() const;
-
-  virtual NodeTransition *compose(const NodeTransition *other) const;
-  virtual NodeTransition *invert() const;
-  virtual NodeAttribute *apply(const NodeAttribute *attrib) const;
-
-protected:
-  virtual int internal_compare_to(const NodeTransition *other) const;
-
-public:
-  virtual TypeHandle get_type() const {
-    return get_class_type();
-  }
-  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
-  static TypeHandle get_class_type() {
-    return _type_handle;
-  }
-  static void init_type() {
-    NodeTransition::init_type();
-    register_type(_type_handle, "ModifierButtonDataTransition",
-		  NodeTransition::get_class_type());
-  }
-
-private:
-  static TypeHandle _type_handle;
-};
-
-#include "modifierButtonDataTransition.I"
-
-#endif

+ 0 - 28
panda/src/display/graphicsWindow.I

@@ -134,34 +134,6 @@ has_keyboard(int device) const {
   return _input_devices[device].has_keyboard();
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindow::get_modifier_buttons
-//       Access: Public
-//  Description: Returns the set of ModifierButtons currently being
-//               monitored for the nth input device.
-////////////////////////////////////////////////////////////////////
-INLINE const ModifierButtons &GraphicsWindow::
-get_modifier_buttons(int device) const {
-  nassertr(device >= 0 && device < (int)_input_devices.size(), 
-	   *(new ModifierButtons));
-  return _input_devices[device].get_modifier_buttons();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindow::set_modifier_buttons
-//       Access: Public
-//  Description: Changes the set of ModifierButtons that will be
-//               monitored for the nth input device.  It is
-//               recommended that you first retrieve the existing set
-//               via get_modifier_buttons(), so that any current
-//               button state will not be lost.
-////////////////////////////////////////////////////////////////////
-INLINE void GraphicsWindow::
-set_modifier_buttons(int device, const ModifierButtons &mods) {
-  nassertv(device >= 0 && device < (int)_input_devices.size());
-  _input_devices[device].set_modifier_buttons(mods);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsWindow::get_mouse_data
 //       Access: Public

+ 0 - 3
panda/src/display/graphicsWindow.h

@@ -133,9 +133,6 @@ PUBLISHED:
   INLINE bool has_pointer(int device) const;
   INLINE bool has_keyboard(int device) const;
 
-  INLINE const ModifierButtons &get_modifier_buttons(int device) const;
-  INLINE void set_modifier_buttons(int device, const ModifierButtons &mods);
-
 public:
   INLINE const MouseData &get_mouse_data(int device) const;
   INLINE bool has_button_event(int device) const;

+ 0 - 25
panda/src/display/graphicsWindowInputDevice.I

@@ -44,31 +44,6 @@ get_mouse_data() const {
   return _mouse_data;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::get_modifier_buttons
-//       Access: Public
-//  Description: Returns the set of ModifierButtons currently being
-//               monitored for the nth input device.
-////////////////////////////////////////////////////////////////////
-INLINE const ModifierButtons &GraphicsWindowInputDevice::
-get_modifier_buttons() const {
-  return _mods;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::set_modifier_buttons
-//       Access: Public
-//  Description: Changes the set of ModifierButtons that will be
-//               monitored for the nth input device.  It is
-//               recommended that you first retrieve the existing set
-//               via get_modifier_buttons(), so that any current
-//               button state will not be lost.
-////////////////////////////////////////////////////////////////////
-INLINE void GraphicsWindowInputDevice::
-set_modifier_buttons(const ModifierButtons &mods) {
-  _mods = mods;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsWindowInputDevice::set_pointer_in_window
 //       Access: Public

+ 2 - 16
panda/src/display/graphicsWindowInputDevice.cxx

@@ -29,16 +29,6 @@ GraphicsWindowInputDevice(const string &name, int flags) :
   _name(name),
   _flags(flags)
 {
-  // We'll define the mouse buttons and the traditional modifier keys
-  // as the default modifiers.  Individual GraphicsWindows can change
-  // this if they want.
-
-  _mods.add_button(MouseButton::one());
-  _mods.add_button(MouseButton::two());
-  _mods.add_button(MouseButton::three());
-  _mods.add_button(KeyboardButton::shift());
-  _mods.add_button(KeyboardButton::control());
-  _mods.add_button(KeyboardButton::alt());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -85,7 +75,6 @@ GraphicsWindowInputDevice(const GraphicsWindowInputDevice &copy) :
   _name(copy._name),
   _flags(copy._flags),
   _mouse_data(copy._mouse_data),
-  _mods(copy._mods),
   _button_events(copy._button_events)
 {
 }
@@ -100,7 +89,6 @@ operator = (const GraphicsWindowInputDevice &copy) {
   _name = copy._name;
   _flags = copy._flags;
   _mouse_data = copy._mouse_data;
-  _mods = copy._mods;
   _button_events = copy._button_events;
 }
 
@@ -147,8 +135,7 @@ get_button_event() {
 ////////////////////////////////////////////////////////////////////
 void GraphicsWindowInputDevice::
 button_down(ButtonHandle button) {
-  _button_events.push_back(ButtonEvent(button, true, _mods));
-  _mods.button_down(button);
+  _button_events.push_back(ButtonEvent(button, true));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -158,6 +145,5 @@ button_down(ButtonHandle button) {
 ////////////////////////////////////////////////////////////////////
 void GraphicsWindowInputDevice::
 button_up(ButtonHandle button) {
-  _mods.button_up(button);
-  _button_events.push_back(ButtonEvent(button, false, _mods));
+  _button_events.push_back(ButtonEvent(button, false));
 }

+ 0 - 4
panda/src/display/graphicsWindowInputDevice.h

@@ -10,7 +10,6 @@
 
 #include <buttonEvent.h>
 #include <mouseData.h>
-#include <modifierButtons.h>
 
 #include <deque>
 #include <vector>
@@ -41,8 +40,6 @@ public:
   INLINE bool has_keyboard() const;
 
   INLINE const MouseData &get_mouse_data() const;
-  INLINE const ModifierButtons &get_modifier_buttons() const;
-  INLINE void set_modifier_buttons(const ModifierButtons &mods);
 
   bool has_button_event() const;
   ButtonEvent get_button_event();
@@ -73,7 +70,6 @@ private:
   string _name;
   int _flags;
   MouseData _mouse_data;
-  ModifierButtons _mods;
   ButtonEvents _button_events;
 };
 

+ 4 - 1
panda/src/framework/framework.cxx

@@ -1048,7 +1048,10 @@ int framework_main(int argc, char *argv[]) {
   PT(Transform2SG) tball2cam = new Transform2SG("tball2cam");
   tball2cam->set_arc(arc1);
   new DataRelation(trackball, tball2cam);
-  new DataRelation(drive_interface, tball2cam);
+
+  PT(Transform2SG) drive2cam = new Transform2SG("drive2cam");
+  drive2cam->set_arc(arc1);
+  new DataRelation(drive_interface, drive2cam);
 
   // Create a ButtonThrower to throw events from the keyboard.
   PT(ButtonThrower) et = new ButtonThrower("kb-events");

+ 14 - 0
panda/src/graph/nodeAttribute.cxx

@@ -9,6 +9,20 @@
 
 TypeHandle NodeAttribute::_type_handle;
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodeAttribute::merge
+//       Access: Public, Virtual
+//  Description: Attempts to merge this attribute with the other one,
+//               if that makes sense to do.  Returns a new
+//               NodeAttribute pointer that represents the merge, or
+//               if the merge is not possible, returns the "other"
+//               pointer unchanged (which is the result of the merge).
+////////////////////////////////////////////////////////////////////
+NodeAttribute *NodeAttribute::
+merge(const NodeAttribute *other) const {
+  return (NodeAttribute *)other;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: NodeAttribute::output
 //       Access: Public, Virtual

+ 2 - 0
panda/src/graph/nodeAttribute.h

@@ -66,6 +66,8 @@ public:
 
   virtual TypeHandle get_handle() const=0;
 
+  virtual NodeAttribute *merge(const NodeAttribute *other) const;
+
 PUBLISHED:
   virtual void output(ostream &out) const;
   virtual void write(ostream &out, int indent_level = 0) const;

+ 29 - 0
panda/src/graph/nodeAttributes.cxx

@@ -221,6 +221,35 @@ apply_from(const NodeAttributes &other, const NodeTransitionCache &trans) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodeAttributes::merge_from
+//       Access: Public
+//  Description: Computes the union of the two NodeAttributes sets and
+//               stores the result in this object (which may be the
+//               same object as either of the two sources).  If a
+//               given attribute is set in both sources (and cannot be
+//               union sensibly), the one from the second source
+//               overrides.
+////////////////////////////////////////////////////////////////////
+void NodeAttributes::
+merge_from(const NodeAttributes &a, const NodeAttributes &b) {
+  if (&a == this && b.is_empty()) {
+    // Trivial do-nothing case.
+
+  } else if (&b == this && a.is_empty()) {
+    // Trivial do-nothing case.
+
+  } else {
+    Attributes temp;
+
+    tmap_merge_union(a._attributes.begin(), a._attributes.end(),
+		     b._attributes.begin(), b._attributes.end(),
+		     inserter(temp, temp.begin()));
+    
+    _attributes.swap(temp);
+  }
+}
+
 
 ////////////////////////////////////////////////////////////////////
 //     Function: NodeAttributes::output

+ 3 - 1
panda/src/graph/nodeAttributes.h

@@ -68,7 +68,9 @@ public:
   void apply_from(const NodeAttributes &other, 
 		  const NodeTransitionCache &trans);
 
-public:
+  void merge_from(const NodeAttributes &a, const NodeAttributes &b);
+
+PUBLISHED:
   void output(ostream &out) const;
   void write(ostream &out, int indent_level = 0) const;
 

+ 6 - 6
panda/src/graph/nodeTransitionCache.cxx

@@ -252,9 +252,9 @@ store_to(const NodeTransitionCache *a, NodeRelation *arc,
 
   // And now actually add them.
   NodeTransitions temp;
-  tmap_union(nt._transitions.begin(), nt._transitions.end(),
-	     a->_cache.begin(), a->_cache.end(),
-	     inserter(temp._transitions, temp._transitions.begin()));
+  tmap_override_union(nt._transitions.begin(), nt._transitions.end(),
+		      a->_cache.begin(), a->_cache.end(),
+		      inserter(temp._transitions, temp._transitions.begin()));
   nt._transitions.swap(temp._transitions);
 }
 
@@ -281,9 +281,9 @@ c_union(const NodeTransitionCache *a, const NodeTransitionCache *b) {
     // Neither is empty.  Build and return a new list.
     NodeTransitionCache *result = new NodeTransitionCache;
 
-    tmap_union(a->_cache.begin(), a->_cache.end(),
-	       b->_cache.begin(), b->_cache.end(),
-	       inserter(result->_cache, result->_cache.begin()));
+    tmap_override_union(a->_cache.begin(), a->_cache.end(),
+			b->_cache.begin(), b->_cache.end(),
+			inserter(result->_cache, result->_cache.begin()));
 
     return result;
   }

+ 3 - 3
panda/src/graph/nodeTransitions.cxx

@@ -170,9 +170,9 @@ clear() {
 void NodeTransitions::
 copy_transitions_from(const NodeTransitions &other) {
   Transitions temp;
-  tmap_union(_transitions.begin(), _transitions.end(),
-	     other._transitions.begin(), other._transitions.end(),
-	     inserter(temp, temp.begin()));
+  tmap_override_union(_transitions.begin(), _transitions.end(),
+		      other._transitions.begin(), other._transitions.end(),
+		      inserter(temp, temp.begin()));
   _transitions.swap(temp);
 }
 

+ 48 - 4
panda/src/graph/setTransitionHelpers.I

@@ -37,7 +37,7 @@ tmap_get_interest(InputIterator1 first1, InputIterator1 last1,
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: tmap_union
+//     Function: tmap_override_union
 //  Description: Accepts two NodeTransition or NodeAttribute maps, and
 //               builds a new map which is the union of the first two.
 //               If an element appears in both maps, the entry from
@@ -45,9 +45,9 @@ tmap_get_interest(InputIterator1 first1, InputIterator1 last1,
 ////////////////////////////////////////////////////////////////////
 template<class InputIterator1, class InputIterator2, class OutputIterator>
 OutputIterator
-tmap_union(InputIterator1 first1, InputIterator1 last1,
-	   InputIterator2 first2, InputIterator2 last2,
-	   OutputIterator result) {
+tmap_override_union(InputIterator1 first1, InputIterator1 last1,
+		    InputIterator2 first2, InputIterator2 last2,
+		    OutputIterator result) {
   while (first1 != last1 && first2 != last2) {
     if ((*first1).first < (*first2).first) {
       *result = *first1;
@@ -79,6 +79,50 @@ tmap_union(InputIterator1 first1, InputIterator1 last1,
   return result;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: tmap_merge_union
+//  Description: Accepts two NodeAttribute maps, and builds a new map
+//               which is the union of the first two.  If an element
+//               appears in both maps, the two are merged, preferring
+//               the second.
+////////////////////////////////////////////////////////////////////
+template<class InputIterator1, class InputIterator2, class OutputIterator>
+OutputIterator
+tmap_merge_union(InputIterator1 first1, InputIterator1 last1,
+		 InputIterator2 first2, InputIterator2 last2,
+		 OutputIterator result) {
+  while (first1 != last1 && first2 != last2) {
+    if ((*first1).first < (*first2).first) {
+      *result = *first1;
+      ++first1;
+      ++result;
+    } else if ((*first2).first < (*first1).first) {
+      *result = *first2;
+      ++first2;
+      ++result;
+    } else {
+      PT(NodeAttribute) c = (*first1).second->merge((*first2).second);
+      *result = pair<TypeHandle, PT(NodeAttribute)>((*first1).first, c);
+      ++first1;
+      ++first2;
+      ++result;
+    }
+  }
+
+  while (first1 != last1) {
+    *result = *first1;
+    ++first1;
+    ++result;
+  }
+
+  while (first2 != last2) { 
+    *result = *first2;
+    ++first2;
+    ++result;
+  }
+  return result;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: tmap_arc_union
 //  Description: As above, but updates the transitions on the way to

+ 3 - 23
panda/src/putil/buttonEvent.I

@@ -28,19 +28,6 @@ ButtonEvent(ButtonHandle button, bool down) :
 {
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: ButtonEvent::Constructor
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE ButtonEvent::
-ButtonEvent(ButtonHandle button, bool down, ModifierButtons mods) :
-  _button(button),
-  _down(down),
-  _mods(mods)
-{
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: ButtonEvent::Copy Constructor
 //       Access: Public
@@ -49,8 +36,7 @@ ButtonEvent(ButtonHandle button, bool down, ModifierButtons mods) :
 INLINE ButtonEvent::
 ButtonEvent(const ButtonEvent &copy) :
   _button(copy._button),
-  _down(copy._down),
-  _mods(copy._mods)
+  _down(copy._down)
 {
 }
 
@@ -63,7 +49,6 @@ INLINE void ButtonEvent::
 operator = (const ButtonEvent &copy) {
   _button = copy._button;
   _down = copy._down;
-  ((ModifierButtons &)_mods) = copy._mods;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -74,8 +59,7 @@ operator = (const ButtonEvent &copy) {
 INLINE bool ButtonEvent::
 operator == (const ButtonEvent &other) const {
   return (_button == other._button &&
-	  _down == other._down &&
-	  _mods == other._mods);
+	  _down == other._down);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -99,9 +83,5 @@ operator < (const ButtonEvent &other) const {
     return _button < other._button;
   }
 
-  if (_down != other._down) {
-    return _down < other._down;
-  }
-
-  return _mods < other._mods;
+  return _down < other._down;
 }

+ 0 - 1
panda/src/putil/buttonEvent.cxx

@@ -18,5 +18,4 @@ output(ostream &out) const {
   } else {
     out << " up";
   }
-  out << " with " << _mods;
 }

+ 1 - 5
panda/src/putil/buttonEvent.h

@@ -9,19 +9,16 @@
 #include <pandabase.h>
 
 #include "buttonHandle.h"
-#include "modifierButtons.h"
 
 ////////////////////////////////////////////////////////////////////
 // 	 Class : ButtonEvent
 // Description : Records a transition of one button from up to down or
-//               vice-versa, as well as an optional recording of some
-//               ModifierButtons that were being tracked at the time.
+//               vice-versa.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA ButtonEvent {
 public:
   INLINE ButtonEvent();
   INLINE ButtonEvent(ButtonHandle button, bool down);
-  INLINE ButtonEvent(ButtonHandle button, bool down, ModifierButtons mods);
   INLINE ButtonEvent(const ButtonEvent &copy);
   INLINE void operator = (const ButtonEvent &copy);
 
@@ -33,7 +30,6 @@ public:
 
   ButtonHandle _button;
   bool _down;
-  const ModifierButtons _mods;
 };
 
 INLINE ostream &operator << (ostream &out, const ButtonEvent &be) {

+ 26 - 0
panda/src/putil/modifierButtons.I

@@ -75,6 +75,21 @@ get_button(int index) const {
   return _button_list[index];
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: ModifierButtons::add_event
+//       Access: Public
+//  Description: Calls button_down() or button_up(), as appropriate,
+//               according to the indicated ButtonEvent.
+////////////////////////////////////////////////////////////////////
+INLINE bool ModifierButtons::
+add_event(const ButtonEvent &event) {
+  if (event._down) {
+    return button_down(event._button);
+  } else {
+    return button_up(event._button);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ModifierButtons::is_down
 //       Access: Public
@@ -86,3 +101,14 @@ is_down(int index) const {
   nassertr(index >= 0 && index < (int)_button_list.size(), false);
   return ((_state & ((BitmaskType)1 << index)) != 0);
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: ModifierButtons::is_any_down
+//       Access: Public
+//  Description: Returns true if any of the tracked button are known
+//               to be down, or false if all of them are up.
+////////////////////////////////////////////////////////////////////
+INLINE bool ModifierButtons::
+is_any_down() const {
+  return _state != 0;
+}

+ 12 - 6
panda/src/putil/modifierButtons.cxx

@@ -129,16 +129,19 @@ remove_button(ButtonHandle button) {
 //               If the given button is one of the buttons that is
 //               currently being monitored, this will update the
 //               internal state appropriately; otherwise, it will do
-//               nothing.
+//               nothing.  Returns true if the button is one that was
+//               monitored, or false otherwise.
 ////////////////////////////////////////////////////////////////////
-void ModifierButtons::
+bool ModifierButtons::
 button_down(ButtonHandle button) {
   for (int i = 0; i < (int)_button_list.size(); i++) {
     if (button == _button_list[i]) {
       _state |= ((BitmaskType)1 << i);
-      return;
+      return true;
     }
   }
+
+  return false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -148,16 +151,19 @@ button_down(ButtonHandle button) {
 //               If the given button is one of the buttons that is
 //               currently being monitored, this will update the
 //               internal state appropriately; otherwise, it will do
-//               nothing.
+//               nothing.  Returns true if the button is one that was
+//               monitored, or false otherwise.
 ////////////////////////////////////////////////////////////////////
-void ModifierButtons::
+bool ModifierButtons::
 button_up(ButtonHandle button) {
   for (int i = 0; i < (int)_button_list.size(); i++) {
     if (button == _button_list[i]) {
       _state &= ~((BitmaskType)1 << i);
-      return;
+      return true;
     }
   }
+
+  return false;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 6 - 3
panda/src/putil/modifierButtons.h

@@ -10,6 +10,7 @@
 
 #include "buttonHandle.h"
 #include "pointerToArray.h"
+#include "buttonEvent.h"
 
 ////////////////////////////////////////////////////////////////////
 // 	 Class : ModifierButtons
@@ -18,7 +19,7 @@
 //               known to be down or up.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA ModifierButtons {
-public:
+PUBLISHED:
   ModifierButtons();
   ModifierButtons(const ModifierButtons &copy);
   ~ModifierButtons();
@@ -35,11 +36,13 @@ public:
   INLINE int get_num_buttons() const;
   INLINE ButtonHandle get_button(int index) const;
 
-  void button_down(ButtonHandle button);
-  void button_up(ButtonHandle button);
+  bool button_down(ButtonHandle button);
+  bool button_up(ButtonHandle button);
+  INLINE bool add_event(const ButtonEvent &event);
 
   bool is_down(ButtonHandle button) const;
   INLINE bool is_down(int index) const;
+  INLINE bool is_any_down() const;
 
   void output(ostream &out) const;
   void write(ostream &out) const;

+ 3 - 1
panda/src/tform/Sources.pp

@@ -9,6 +9,7 @@
 
   #define SOURCES \
     buttonThrower.cxx buttonThrower.h config_tform.cxx config_tform.h \
+    dataValve.I dataValve.cxx dataValve.h \
     driveInterface.cxx driveInterface.h mouseWatcher.I mouseWatcher.cxx \
     mouseWatcher.h mouseWatcherRegion.I mouseWatcherRegion.cxx \
     mouseWatcherRegion.h planarSlider.cxx planarSlider.h trackball.cxx \
@@ -16,7 +17,8 @@
     transform2sg.cxx transform2sg.h
 
   #define INSTALL_HEADERS \
-    buttonThrower.h driveInterface.h mouseWatcher.I mouseWatcher.h \
+    buttonThrower.h dataValve.I dataValve.h \
+    driveInterface.h mouseWatcher.I mouseWatcher.h \
     mouseWatcherRegion.I mouseWatcherRegion.h planarSlider.h \
     trackball.h transform2sg.h
 

+ 24 - 15
panda/src/tform/buttonThrower.cxx

@@ -86,9 +86,14 @@ get_modifier_buttons() const {
 //               be empty, and the ButtonThrower will therefore ignore
 //               all ModifierButtons attached to the key events, but
 //               if one or more buttons have been added to this set,
-//               and those modifier buttons are set on the button
-//               event, then the event name will be prepended with the
-//               names of the modifier buttons.
+//               then the event name will be prepended with the names
+//               of the modifier buttons.
+//
+//               It is recommended that you change this setting by
+//               first calling get_modifier_buttons(), making
+//               adjustments, and passing the new value to
+//               set_modifier_buttons().  This way the current state
+//               of the modifier buttons will not be lost.
 ////////////////////////////////////////////////////////////////////
 void ButtonThrower::
 set_modifier_buttons(const ModifierButtons &mods) {
@@ -110,20 +115,24 @@ transmit_data(NodeAttributes &data) {
     for (bi = b->begin(); bi != b->end(); ++bi) {
       const ButtonEvent &be = (*bi);
       string event_name = _prefix + be._button.get_name();
-      if (!be._down) {
-	event_name += "-up";
-	
-      } else {
-	// We only prepend modifier names on the button-down events.
-	string prepend;
-	
-	for (int i = 0; i < _mods.get_num_buttons(); i++) {
-	  ButtonHandle modifier = _mods.get_button(i);
-	  if (be._mods.is_down(modifier)) {
-	    prepend += modifier.get_name() + "-";
+      if (be._down) {
+	if (!_mods.button_down(be._button)) {
+	  // We only prepend modifier names on the button-down events,
+	  // and only for buttons which are not themselves modifiers.
+	  string prepend;
+	  
+	  for (int i = 0; i < _mods.get_num_buttons(); i++) {
+	    ButtonHandle modifier = _mods.get_button(i);
+	    if (_mods.is_down(modifier)) {
+	      prepend += modifier.get_name() + "-";
+	    }
 	  }
+	  event_name = prepend + event_name;
 	}
-	event_name = prepend + event_name;
+
+      } else {
+	_mods.button_up(be._button);
+	event_name += "-up";
       }
       
       throw_event(event_name);

+ 3 - 0
panda/src/tform/config_tform.cxx

@@ -4,6 +4,8 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "config_tform.h"
+
+#include "dataValve.h"
 #include "driveInterface.h"
 #include "buttonThrower.h"
 #include "mouseWatcher.h"
@@ -30,6 +32,7 @@ const double drive_horizontal_ramp_up_time = config_tform.GetDouble("drive-horiz
 const double drive_horizontal_ramp_down_time = config_tform.GetDouble("drive-horizontal-ramp-down-time", 0.0);
 
 ConfigureFn(config_tform) {
+  DataValve::init_type();
   DriveInterface::init_type();
   ButtonThrower::init_type();
   MouseWatcher::init_type();

+ 126 - 0
panda/src/tform/dataValve.I

@@ -0,0 +1,126 @@
+// Filename: dataValve.I
+// Created by:  drose (05Feb01)
+// 
+////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::Control::Constructor
+//       Access: Published
+//  Description: A unique Control can be assigned to each child of the
+//               DataValve, or the same Control may be shared between
+//               different children as desired.  Each Control may be
+//               explicitly set on or off to enable or disable all
+//               data flow to that child, respectively; it may also be
+//               set to a "buttons" state, in which it enables or
+//               disables data flow to the child based on the current
+//               state of the modifier buttons as held by the user.
+//
+//               In addition to being assigned to control all data to
+//               a particular child, a particular Control may be set
+//               to control only data of a particular type to a
+//               particular child.  See set_control() and
+//               set_fine_control() on DataValve.
+////////////////////////////////////////////////////////////////////
+INLINE DataValve::Control::
+Control() {
+  _state = S_on;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::Control::set_on
+//       Access: Published
+//  Description: Sets the state on this particular Control to be on
+//               until further notice.  Child nodes guarded by this
+//               Control will receive data.
+////////////////////////////////////////////////////////////////////
+INLINE void DataValve::Control::
+set_on() {
+  _state = S_on;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::Control::set_off
+//       Access: Published
+//  Description: Sets the state on this particular Control to be off
+//               until further notice.  Child nodes guarded by this
+//               Control will not receive data.
+////////////////////////////////////////////////////////////////////
+INLINE void DataValve::Control::
+set_off() {
+  _state = S_off;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::Control::set_buttons
+//       Access: Published
+//  Description: Sets the state on this particular Control to be
+//               dependent on the current set of buttons being held
+//               down by the user.  The ModifierButtons object here
+//               must exactly match the ModifierButtons object set on
+//               the DataValve in order and number of buttons.
+//
+//               The best way to generate the ModifierButtons list is
+//               to create a new ModifierButtons based on the value
+//               returned by DataValve::get_modifier_buttons(), then
+//               explicitly call button_up() and button_down() on each
+//               button that you want to be up or down.
+////////////////////////////////////////////////////////////////////
+INLINE void DataValve::Control::
+set_buttons(const ModifierButtons &mods) {
+  _state = S_buttons;
+  _mods = mods;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::set_default_control
+//       Access: Public
+//  Description: Sets the Control that will apply to each child that
+//               does not have a particular control set via
+//               set_control().
+////////////////////////////////////////////////////////////////////
+INLINE void DataValve::
+set_default_control(DataValve::Control *control) {
+  _default_control = control;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::get_default_control
+//       Access: Public
+//  Description: Returns the Control that will apply to each child
+//               that does not have a particular control set via
+//               set_control().
+////////////////////////////////////////////////////////////////////
+INLINE DataValve::Control *DataValve::
+get_default_control() const {
+  return _default_control;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::set_modifier_buttons
+//       Access: Public
+//  Description: Specifies the set of buttons that the DataValve will
+//               monitor as modifier buttons.  The exact same set of
+//               ModifierButtons must be specified on the DataValve as
+//               well as on all of its Controls in order to
+//               successfully use modifier buttons to switch the
+//               valves automatically.
+//
+//               This need not match the set of ModifierButtons set on
+//               the window, but it may be less confusing if it does.
+////////////////////////////////////////////////////////////////////
+INLINE void DataValve::
+set_modifier_buttons(const ModifierButtons &mods) {
+  _mods = mods;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::get_modifier_buttons
+//       Access: Public
+//  Description: Returns the set of ModifierButtons currently being
+//               monitored.  See set_modifier_buttons().
+////////////////////////////////////////////////////////////////////
+INLINE const ModifierButtons &DataValve::
+get_modifier_buttons() const {
+  return _mods;
+}

+ 369 - 0
panda/src/tform/dataValve.cxx

@@ -0,0 +1,369 @@
+// Filename: dataValve.cxx
+// Created by:  drose (05Feb01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "dataValve.h"
+
+#include <dataRelation.h>
+#include <buttonEventDataAttribute.h>
+#include <buttonEventDataTransition.h>
+#include <nodeAttributes.h>
+
+TypeHandle DataValve::_type_handle;
+
+TypeHandle DataValve::_button_events_type;
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::Control::is_on
+//       Access: Published
+//  Description: Returns true if the indicated Control is currently
+//               on, given the particular DataNode it applies to.
+//               (The DataNode is necessary to determine the current
+//               state of the ModiferButtons.
+////////////////////////////////////////////////////////////////////
+bool DataValve::Control::
+is_on(const DataValve &valve) const {
+  switch (_state) {
+  case S_on:
+    return true;
+
+  case S_off:
+    return false;
+
+  case S_buttons:
+    return _mods == valve.get_modifier_buttons();
+  }
+
+  // Invalid state.
+  nassertr(false, false);
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+DataValve::
+DataValve(const string &name) : DataNode(name) {
+  _default_control = new Control;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::Destructor
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+DataValve::
+~DataValve() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::set_control
+//       Access: Published
+//  Description: Sets an overall Control on the indicated child node.
+//               The nth child of this node, zero-based, will receive
+//               data only while the indicated Control is on.  This
+//               affects all types of data that is not specifically
+//               mentioned by set_fine_control().
+////////////////////////////////////////////////////////////////////
+void DataValve::
+set_control(int child_index, Control *control) {
+  ensure_child_index(child_index);
+  nassertv(child_index >= 0 && child_index < (int)_controls.size());
+  _controls[child_index]._control = control;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::clear_control
+//       Access: Published
+//  Description: Removes the overall Control, as well as all fine
+//               controls, from the indicated child node.  The
+//               indicated child will now be affected only by the
+//               default control.  See set_control() and
+//               set_fine_control().
+////////////////////////////////////////////////////////////////////
+void DataValve::
+clear_control(int child_index) {
+  nassertv(child_index >= 0);
+  if (child_index < (int)_controls.size()) {
+    _controls[child_index]._control.clear();
+    _controls[child_index]._fine_controls.clear();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::has_control
+//       Access: Published
+//  Description: Returns true if the indicated child has a particular
+//               Control set, or if it has *any* fine controls set.
+//               If this returns false, the child is controlled only
+//               by the default control.  See set_control() and
+//               set_fine_control().
+////////////////////////////////////////////////////////////////////
+bool DataValve::
+has_control(int child_index) const {
+  nassertr(child_index >= 0, false);
+  if (child_index < (int)_controls.size()) {
+    return
+      !_controls[child_index]._control.is_null() ||
+      !_controls[child_index]._fine_controls.empty();
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::get_control
+//       Access: Published
+//  Description: Returns the control that has been set for the
+//               indicated child, or NULL if no control has been set
+//               (which implies the default control is in affect for
+//               the indicated child).  Note that it is possible for
+//               this function to return NULL even if has_control(),
+//               above, returned true, since a fine control may have
+//               been set without setting a particular general
+//               control.
+////////////////////////////////////////////////////////////////////
+DataValve::Control *DataValve::
+get_control(int child_index) const {
+  nassertr(child_index >= 0, NULL);
+  if (child_index < (int)_controls.size()) {
+    return _controls[child_index]._control;
+  }
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::set_fine_control
+//       Access: Published
+//  Description: Sets the "fine" control for the indicated child index
+//               and the indicated data type.  This overrides the
+//               control set by set_control() for this child, but only
+//               for data of this type.  This can be used to send
+//               data of different types to different children.
+////////////////////////////////////////////////////////////////////
+void DataValve::
+set_fine_control(int child_index, TypeHandle data_type, Control *control) {
+  ensure_child_index(child_index);
+  nassertv(child_index >= 0 && child_index < (int)_controls.size());
+  _controls[child_index]._fine_controls[data_type] = control;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::clear_fine_control
+//       Access: Published
+//  Description: Removes the particular fine control set by
+//               set_fine_control() for the indicated child index and
+//               data type.  Data of this type will now be controlled
+//               by the Control indicated by set_control() for this
+//               child, or by set_default_control() if nothing is
+//               indicated by set_control().
+////////////////////////////////////////////////////////////////////
+void DataValve::
+clear_fine_control(int child_index, TypeHandle data_type) {
+  nassertv(child_index >= 0);
+  if (child_index < (int)_controls.size()) {
+    _controls[child_index]._fine_controls.erase(data_type);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::has_fine_control
+//       Access: Published
+//  Description: Returns true if the indicated child has a particular
+//               fine control set for the indicated data type, or
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+bool DataValve::
+has_fine_control(int child_index, TypeHandle data_type) const {
+  nassertr(child_index >= 0, false);
+  if (child_index < (int)_controls.size()) {
+    return _controls[child_index]._fine_controls.count(data_type) != 0;
+  }
+  return false;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::get_fine_control
+//       Access: Published
+//  Description: Returns the fine control that has been set for the
+//               indicated child and data type, or NULL if no such
+//               fine control has been set.
+////////////////////////////////////////////////////////////////////
+DataValve::Control *DataValve::
+get_fine_control(int child_index, TypeHandle data_type) const {
+  nassertr(child_index >= 0, NULL);
+  if (child_index < (int)_controls.size()) {
+    FineControls::const_iterator fci = 
+      _controls[child_index]._fine_controls.find(data_type);
+    if (fci != _controls[child_index]._fine_controls.end()) {
+      return (*fci).second;
+    }
+  }
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::write
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void DataValve::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level)
+    << "DataValve " << get_name() << ": (default control is "
+    << (_default_control->is_on(*this) ? "on" : "off") << ")\n";
+
+  size_t i;
+  for (i = 0; i < _controls.size(); i++) {
+    if (has_control(i)) {
+      const Child &child = _controls[i];
+      indent(out, indent_level + 2)
+	<< "Child " << i << ": ";
+
+      if (child._control.is_null()) {
+	out << "(default)";
+      } else {
+	out << (child._control->is_on(*this) ? "on" : "off");
+      }
+      out << "\n";
+      
+      FineControls::const_iterator fci;
+      for (fci = child._fine_controls.begin(); 
+	   fci != child._fine_controls.end();
+	   ++fci) {
+	indent(out, indent_level + 4)
+	  << (*fci).first << " "
+	  << ((*fci).second->is_on(*this) ? "on" : "off")
+	  << "\n";
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::ensure_child_index
+//       Access: Private
+//  Description: Guarantees that we have at least enough children in
+//               the _control vector to describe the indicated
+//               child_index.
+////////////////////////////////////////////////////////////////////
+void DataValve::
+ensure_child_index(int child_index) {
+  _controls.reserve(child_index + 1);
+  while ((int)_controls.size() <= child_index) {
+    _controls.push_back(Child());
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::transmit_data
+//       Access: Public, Virtual
+//  Description: Called once before transmit_data_per_child() is
+//               called for each child, this gives the node a chance
+//               to process its inputs.
+////////////////////////////////////////////////////////////////////
+void DataValve::
+transmit_data(NodeAttributes &data) {
+  // Update our modifier buttons during the overall pass.
+  const ButtonEventDataAttribute *b;
+  if (get_attribute_into(b, data, _button_events_type)) {
+    b->update_mods(_mods);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::transmit_data_per_child
+//       Access: Public, Virtual
+//  Description: Should be overridden in a derived class that wants to
+//               send a different data stream to each child.
+//               Normally, a node only overrides transmit_data(),
+//               which takes a set of input data attributes and
+//               generates a set of output data attributes.  A node
+//               may also override transmit_data_per_child(), which is
+//               called after transmit_data(), once per child; this
+//               function may be used to send individual data
+//               attributes to each child.
+////////////////////////////////////////////////////////////////////
+void DataValve::
+transmit_data_per_child(NodeAttributes &data, int child_index) {
+  // Consider which data this child wants to see.
+  static const FineControls empty_controls;
+
+  Control *control = _default_control;
+  const FineControls *fine_controls = &empty_controls;
+
+  if (child_index < (int)_controls.size()) {
+    if (!_controls[child_index]._control.is_null()) {
+      control = _controls[child_index]._control;
+    }
+    fine_controls = &_controls[child_index]._fine_controls;
+  }
+
+  if (control->is_on(*this)) {
+    if (fine_controls->empty()) {
+      // The control is on, and we have no further fine-tuning.  All
+      // data goes to the indicated node.
+      return;
+
+    } else {
+      // The control is on, and we have some fine-tuning.  Any data
+      // type explicitly "off" should be yanked.
+      FineControls::const_iterator fci;
+      for (fci = fine_controls->begin(); fci != fine_controls->end(); ++fci) {
+	TypeHandle data_type = (*fci).first;
+	Control *fcontrol = (*fci).second;
+
+	if (!fcontrol->is_on(*this)) {
+	  data.clear_attribute(data_type);
+	}
+      }
+    }
+
+  } else {
+    if (fine_controls->empty()) {
+      // The control is off, and we have no further fine-tuning.  All
+      // data gets eliminated.
+      data.clear();
+
+    } else {
+      // The control is off, and we have some fine-tuning.  Any data
+      // type explicitly "on" should be preserved.
+      NodeAttributes temp;
+
+      FineControls::const_iterator fci;
+      for (fci = fine_controls->begin(); fci != fine_controls->end(); ++fci) {
+	TypeHandle data_type = (*fci).first;
+	Control *fcontrol = (*fci).second;
+
+	if (fcontrol->is_on(*this)) {
+	  NodeAttribute *attrib = data.get_attribute(data_type);
+	  if (attrib != (NodeAttribute *)NULL) {
+	    temp.set_attribute(data_type, attrib);
+	  }
+	}
+      }
+
+      data = temp;
+    }
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataValve::init_type
+//       Access: Public, Static
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DataValve::
+init_type() {
+  DataNode::init_type();
+  register_type(_type_handle, "DataValve",
+		DataNode::get_class_type());
+
+  ButtonEventDataTransition::init_type();
+  register_data_transition(_button_events_type, "ButtonEvents",
+			   ButtonEventDataTransition::get_class_type());
+}

+ 124 - 0
panda/src/tform/dataValve.h

@@ -0,0 +1,124 @@
+// Filename: dataValve.h
+// Created by:  drose (05Feb01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef DATAVALVE_H
+#define DATAVALVE_H
+
+#include <pandabase.h>
+
+#include <dataNode.h>
+#include <modifierButtons.h>
+#include <referenceCount.h>
+#include <pointerTo.h>
+
+////////////////////////////////////////////////////////////////////
+//       Class : DataValve
+// Description : This DataNode can be used to selectively control
+//               which child node or nodes receive certain data graph
+//               events, based on the type of data and possibly on the
+//               combination of modifier buttons being held down by
+//               the user.
+//
+//               It's particularly useful when used in conjunction
+//               with one or more Trackball interfaces, for instance,
+//               to move around various different objects according to
+//               the key combination being held down.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA DataValve : public DataNode {
+PUBLISHED:
+  DataValve(const string &name = "");
+  virtual ~DataValve();
+
+  // The Control is used extensively within the DataValve class to
+  // manage the data flow; see the comments on the Control
+  // constructor.
+  class EXPCL_PANDA Control : public ReferenceCount {
+  PUBLISHED:
+    INLINE Control();
+
+    INLINE void set_on();
+    INLINE void set_off();
+    INLINE void set_buttons(const ModifierButtons &mods);
+
+    bool is_on(const DataValve &valve) const;
+
+  private:
+    enum State {
+      S_on,
+      S_off,
+      S_buttons
+    };
+    State _state;
+    ModifierButtons _mods;
+  };
+
+  INLINE void set_default_control(Control *control);
+  INLINE Control *get_default_control() const;
+
+  void set_control(int child_index, Control *control);
+  void clear_control(int child_index);
+  bool has_control(int child_index) const;
+  Control *get_control(int child_index) const;
+
+  void set_fine_control(int child_index, TypeHandle data_type, Control *control);
+  void clear_fine_control(int child_index, TypeHandle data_type);
+  bool has_fine_control(int child_index, TypeHandle data_type) const;
+  Control *get_fine_control(int child_index, TypeHandle data_type) const;
+
+  INLINE void set_modifier_buttons(const ModifierButtons &mods);
+  INLINE const ModifierButtons &get_modifier_buttons() const;
+
+public:
+  virtual void write(ostream &out, int indent_level = 0) const;
+
+private:
+  void ensure_child_index(int child_index);
+
+  ModifierButtons _mods;
+  PT(Control) _default_control;
+
+  typedef map<TypeHandle, PT(Control)> FineControls;
+
+  class Child {
+  public:
+    PT(Control) _control;
+    FineControls _fine_controls;
+  };
+
+  typedef vector<Child> Controls;
+  Controls _controls;
+
+
+////////////////////////////////////////////////////////////////////
+// From parent class DataNode
+////////////////////////////////////////////////////////////////////
+public:
+  virtual void
+  transmit_data(NodeAttributes &data);
+
+  virtual void
+  transmit_data_per_child(NodeAttributes &data, int child_index);
+
+  // inputs
+  static TypeHandle _button_events_type;
+
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type();
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "dataValve.I"
+
+#endif

+ 10 - 26
panda/src/tform/driveInterface.cxx

@@ -13,18 +13,14 @@
 #include <modifierButtons.h>
 #include <buttonEventDataTransition.h>
 #include <buttonEventDataAttribute.h>
-#include <modifierButtonDataTransition.h>
-#include <modifierButtonDataAttribute.h>
 #include <keyboardButton.h>
 #include <mouseButton.h>
-#include <dataGraphTraversal.h>
+#include <dataGraphTraverser.h>
 #include <allAttributesWrapper.h>
-#include <dftraverser.h>
 #include <dataRelation.h>
 
 TypeHandle DriveInterface::_type_handle;
 
-TypeHandle DriveInterface::_mods_type;
 TypeHandle DriveInterface::_xyz_type;
 TypeHandle DriveInterface::_button_events_type;
 TypeHandle DriveInterface::_transform_type;
@@ -126,6 +122,10 @@ DriveInterface(const string &name) : DataNode(name) {
 
   _cs = default_coordinate_system;
 
+  _mods.add_button(MouseButton::one());
+  _mods.add_button(MouseButton::two());
+  _mods.add_button(MouseButton::three());
+
   _transform = new MatrixDataAttribute;
   _transform->set_value(_mat);
   _attrib.set_attribute(_transform_type, _transform);
@@ -652,13 +652,9 @@ get_mat() const {
 void DriveInterface::
 force_dgraph() {
   _transform->set_value(_mat);
-  int num_children = get_num_children(DataRelation::get_class_type());
-  for (int i = 0; i < num_children; i++) {
-    DataGraphVisitor dgv;
-    df_traverse(get_child(DataRelation::get_class_type(), i),
-		dgv, AllAttributesWrapper(_attrib), 
-		NullLevelState(), DataRelation::get_class_type());
-  }
+
+  DataGraphTraverser dgt;
+  dgt.traverse_below(this, _attrib);
 }
 
 
@@ -858,6 +854,7 @@ transmit_data(NodeAttributes &data) {
     ButtonEventDataAttribute::const_iterator bi;
     for (bi = b->begin(); bi != b->end(); ++bi) {
       const ButtonEvent &be = (*bi);
+      _mods.add_event(be);
 
       if (be._button == KeyboardButton::up()) {
 	_up_arrow.set_key(be._down);
@@ -872,7 +869,6 @@ transmit_data(NodeAttributes &data) {
   }
 
   // Now look for mouse activity.
-  bool any_button = false;
   double x = 0.0;
   double y = 0.0;
 
@@ -881,18 +877,9 @@ transmit_data(NodeAttributes &data) {
     LVecBase3f p = xyz->get_value();
     x = p[0];
     y = p[1];
-
-    const ModifierButtonDataAttribute *button;
-    if (get_attribute_into(button, data, _mods_type)) {
-      ModifierButtons mods = button->get_mods();
-      any_button = 
-	mods.is_down(MouseButton::one()) ||
-	mods.is_down(MouseButton::two()) ||
-	mods.is_down(MouseButton::three());
-    }
   }
 
-  apply(x, y, any_button);
+  apply(x, y, _mods.is_any_down());
   _transform->set_value(_mat);
 
   // Now send our matrix down the pipe.
@@ -911,9 +898,6 @@ init_type() {
   register_type(_type_handle, "DriveInterface",
 		DataNode::get_class_type());
 
-  ModifierButtonDataTransition::init_type();
-  register_data_transition(_mods_type, "ModifierButtons",
-			   ModifierButtonDataTransition::get_class_type());
   Vec3DataTransition::init_type();
   register_data_transition(_xyz_type, "XYZ",
 			   Vec3DataTransition::get_class_type());

+ 4 - 1
panda/src/tform/driveInterface.h

@@ -14,6 +14,7 @@
 #include <matrixDataTransition.h>
 #include <matrixDataAttribute.h>
 #include <nodeAttributes.h>
+#include <modifierButtons.h>
 #include <luse.h>
 #include <lmatrix.h>
 
@@ -122,6 +123,9 @@ private:
   bool _is_force_roll;
   CoordinateSystem _cs;
 
+  // Remember which mouse buttons are being held down.
+  ModifierButtons _mods;
+
   // Remember which arrow keys are being held down and which aren't,
   // and at what point they last changed state.
   class KeyHeld {
@@ -152,7 +156,6 @@ public:
   PT(MatrixDataAttribute) _transform;
 
   // inputs
-  static TypeHandle _mods_type;
   static TypeHandle _xyz_type;
   static TypeHandle _button_events_type;
 

+ 0 - 7
panda/src/tform/mouseWatcher.cxx

@@ -8,11 +8,8 @@
 
 #include <mouse.h>
 #include <mouseData.h>
-#include <modifierButtons.h>
 #include <buttonEventDataTransition.h>
 #include <buttonEventDataAttribute.h>
-#include <modifierButtonDataTransition.h>
-#include <modifierButtonDataAttribute.h>
 #include <keyboardButton.h>
 #include <mouseButton.h>
 #include <throw_event.h>
@@ -21,7 +18,6 @@
 
 TypeHandle MouseWatcher::_type_handle;
 
-TypeHandle MouseWatcher::_mods_type;
 TypeHandle MouseWatcher::_xyz_type;
 TypeHandle MouseWatcher::_button_events_type;
 
@@ -313,9 +309,6 @@ init_type() {
   register_type(_type_handle, "MouseWatcher",
 		DataNode::get_class_type());
 
-  ModifierButtonDataTransition::init_type();
-  register_data_transition(_mods_type, "ModifierButtons",
-			   ModifierButtonDataTransition::get_class_type());
   Vec3DataTransition::init_type();
   register_data_transition(_xyz_type, "XYZ",
 			   Vec3DataTransition::get_class_type());

+ 0 - 1
panda/src/tform/mouseWatcher.h

@@ -110,7 +110,6 @@ public:
   transmit_data(NodeAttributes &data);
 
   // inputs & outputs 
-  static TypeHandle _mods_type;
   static TypeHandle _xyz_type;
   static TypeHandle _button_events_type;
 

+ 16 - 15
panda/src/tform/planarSlider.cxx

@@ -9,14 +9,15 @@
 #include <mouse.h>
 #include <mouseData.h>
 #include <modifierButtons.h>
-#include <modifierButtonDataTransition.h>
+#include <buttonEventDataTransition.h>
+#include <buttonEventDataAttribute.h>
 #include <mouseButton.h>
 #include <get_rel_pos.h>
 
 TypeHandle PlanarSlider::_type_handle;
 
-TypeHandle PlanarSlider::_mods_type;
 TypeHandle PlanarSlider::_xyz_type;
+TypeHandle PlanarSlider::_button_events_type;
 TypeHandle PlanarSlider::_transform_type;
 
 
@@ -34,6 +35,8 @@ PlanarSlider(const string &name) : DataNode(name) {
 
   _transform = new MatrixDataAttribute;
   _attrib.set_attribute(_transform_type, _transform);
+
+  _mods.add_button(MouseButton::one());
 }
 
 
@@ -146,19 +149,17 @@ get_mouse_pos() const {
 ////////////////////////////////////////////////////////////////////
 void PlanarSlider::
 transmit_data(NodeAttributes &data) {
-  const NodeAttribute *button = data.get_attribute(_mods_type);
+  // First, update our modifier buttons.
+  const ButtonEventDataAttribute *b;
+  if (get_attribute_into(b, data, _button_events_type)) {
+    b->update_mods(_mods);
+  }
+
+  // Now look for a mouse position.
   const NodeAttribute *xyz = data.get_attribute(_xyz_type);
   
   if (xyz != (NodeAttribute *)NULL) {
-    bool is_down = false;
-
-    if (button != (NodeAttribute *)NULL) {
-      ModifierButtons mods = 
-	DCAST(ModifierButtonDataAttribute, button)->get_mods();
-      is_down = mods.is_down(MouseButton::one());
-    }
-
-    if (is_down) {
+    if (_mods.is_any_down()) {
       LVecBase3f p = DCAST(Vec3DataAttribute, xyz)->get_value();
       set_mouse_pos(LPoint2f(p[0], p[1]));
     }
@@ -180,13 +181,13 @@ init_type() {
   register_type(_type_handle, "PlanarSlider",
 		DataNode::get_class_type());
 
-  ModifierButtonDataTransition::init_type();
-  register_data_transition(_mods_type, "ModifierButtons",
-			   ModifierButtonDataTransition::get_class_type());
   Vec3DataTransition::init_type();
   register_data_transition(_xyz_type, "XYZ",
 			   Vec3DataTransition::get_class_type());
   MatrixDataTransition::init_type();
   register_data_transition(_transform_type, "Transform",
 			   MatrixDataTransition::get_class_type());
+  ButtonEventDataTransition::init_type();
+  register_data_transition(_button_events_type, "ButtonEvents",
+			   ButtonEventDataTransition::get_class_type());
 }

+ 3 - 1
panda/src/tform/planarSlider.h

@@ -16,6 +16,7 @@
 #include <nodeAttributes.h>
 #include <luse.h>
 #include <lmatrix.h>
+#include <modifierButtons.h>
 
 
 ////////////////////////////////////////////////////////////////////
@@ -46,6 +47,7 @@ private:
   CoordinateSystem _cs;
   LMatrix4f _transform_mat, _inverse_mat;
   LPoint2f _mouse_pos;
+  ModifierButtons _mods;
 
 ////////////////////////////////////////////////////////////////////
 // From parent class DataNode
@@ -59,8 +61,8 @@ public:
   PT(MatrixDataAttribute) _transform;
 
   // inputs
-  static TypeHandle _mods_type;
   static TypeHandle _xyz_type;
+  static TypeHandle _button_events_type;
 
   // outputs
   static TypeHandle _transform_type;

+ 24 - 20
panda/src/tform/trackball.cxx

@@ -10,15 +10,14 @@
 #include <mouseData.h>
 #include <modifierButtons.h>
 #include <buttonEventDataTransition.h>
-#include <modifierButtonDataTransition.h>
-#include <modifierButtonDataAttribute.h>
+#include <buttonEventDataAttribute.h>
 #include <mouseButton.h>
 #include <get_rel_pos.h>
 
 TypeHandle Trackball::_type_handle;
 
-TypeHandle Trackball::_mods_type;
 TypeHandle Trackball::_pixel_xyz_type;
+TypeHandle Trackball::_button_events_type;
 TypeHandle Trackball::_transform_type;
 
 #define B1_MASK 1
@@ -46,6 +45,10 @@ Trackball(const string &name) : DataNode(name) {
   _rel_to = NULL;
   _cs = default_coordinate_system;
 
+  _mods.add_button(MouseButton::one());
+  _mods.add_button(MouseButton::two());
+  _mods.add_button(MouseButton::three());
+
   _transform = new MatrixDataAttribute;
   _transform->set_value(LMatrix4f::ident_mat());
   _attrib.set_attribute(_transform_type, _transform);
@@ -471,7 +474,13 @@ recompute() {
 ////////////////////////////////////////////////////////////////////
 void Trackball::
 transmit_data(NodeAttributes &data) {
-  const NodeAttribute *button = data.get_attribute(_mods_type);
+  // First, update our modifier buttons.
+  const ButtonEventDataAttribute *b;
+  if (get_attribute_into(b, data, _button_events_type)) {
+    b->update_mods(_mods);
+  }
+
+  // Now, check for mouse motion.
   const NodeAttribute *pixel_xyz = data.get_attribute(_pixel_xyz_type);
   
   if (pixel_xyz != (NodeAttribute *)NULL) {
@@ -480,19 +489,14 @@ transmit_data(NodeAttributes &data) {
     float this_y = p[1];
     int this_button = 0;
 
-    if (button != (NodeAttribute *)NULL) {
-      ModifierButtons mods = 
-	DCAST(ModifierButtonDataAttribute, button)->get_mods();
-
-      if (mods.is_down(MouseButton::one())) {
-	this_button |= B1_MASK;
-      }
-      if (mods.is_down(MouseButton::two())) {
-	this_button |= B2_MASK;
-      }
-      if (mods.is_down(MouseButton::three())) {
-	this_button |= B3_MASK;
-      }
+    if (_mods.is_down(MouseButton::one())) {
+      this_button |= B1_MASK;
+    }
+    if (_mods.is_down(MouseButton::two())) {
+      this_button |= B2_MASK;
+    }
+    if (_mods.is_down(MouseButton::three())) {
+      this_button |= B3_MASK;
     }
 
     float x = this_x - _lastx;
@@ -521,12 +525,12 @@ init_type() {
   register_type(_type_handle, "Trackball",
 		DataNode::get_class_type());
 
-  ModifierButtonDataTransition::init_type();
-  register_data_transition(_mods_type, "ModifierButtons",
-			   ModifierButtonDataTransition::get_class_type());
   Vec3DataTransition::init_type();
   register_data_transition(_pixel_xyz_type, "PixelXYZ",
 			   Vec3DataTransition::get_class_type());
+  ButtonEventDataTransition::init_type();
+  register_data_transition(_button_events_type, "ButtonEvents",
+			   ButtonEventDataTransition::get_class_type());
   MatrixDataTransition::init_type();
   register_data_transition(_transform_type, "Transform",
 			   MatrixDataTransition::get_class_type());

+ 5 - 1
panda/src/tform/trackball.h

@@ -14,6 +14,7 @@
 #include <matrixDataTransition.h>
 #include <matrixDataAttribute.h>
 #include <nodeAttributes.h>
+#include <modifierButtons.h>
 #include <luse.h>
 #include <lmatrix.h>
 
@@ -103,6 +104,9 @@ private:
   const Node *_rel_to;
   CoordinateSystem _cs;
 
+  // Remember which mouse buttons are being held down.
+  ModifierButtons _mods;
+
 
 ////////////////////////////////////////////////////////////////////
 // From parent class DataNode
@@ -116,8 +120,8 @@ public:
   PT(MatrixDataAttribute) _transform;
 
   // inputs
-  static TypeHandle _mods_type;
   static TypeHandle _pixel_xyz_type;
+  static TypeHandle _button_events_type;
 
   // outputs
   static TypeHandle _transform_type;