Browse Source

refine ButtonThrower button-interception behavior

David Rose 24 years ago
parent
commit
0312d083c6

+ 5 - 1
panda/src/putil/modifierButtons.I

@@ -31,7 +31,11 @@ operator = (const ModifierButtons &copy) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ModifierButtons::Equality Operator
 //     Function: ModifierButtons::Equality Operator
 //       Access: Published
 //       Access: Published
-//  Description:
+//  Description: The equality operator is an exact comparision: the
+//               two ModifierButtons are equal if they share the same
+//               button list--indeed, the same pointer--and they all
+//               the buttons have the same state.  Use matches() if a
+//               less exact equality test is needed.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool ModifierButtons::
 INLINE bool ModifierButtons::
 operator == (const ModifierButtons &other) const {
 operator == (const ModifierButtons &other) const {

+ 50 - 0
panda/src/putil/modifierButtons.cxx

@@ -53,6 +53,56 @@ ModifierButtons::
 ~ModifierButtons() {
 ~ModifierButtons() {
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ModifierButtons::matches
+//       Access: Published
+//  Description: Returns true if the set of buttons indicated as down
+//               by this ModifierButtons object is the same set of
+//               buttons indicated as down by the other
+//               ModifierButtons object.  The buttons indicated as up
+//               are not relevant.
+////////////////////////////////////////////////////////////////////
+bool ModifierButtons::
+matches(const ModifierButtons &other) const {
+  if (_button_list == other._button_list) {
+    // If the two objects share the same array, we only need to check
+    // the bitmask.  This is a simple optimization.
+    return (_state == other._state);
+  }
+
+  // The two objects do not share the same array; thus we have to do
+  // this one button at a time.  This is an n-squared operation, but
+  // presumably there will not be hundreds of buttons to compare.
+
+  // First, check that all the buttons indicated as down in our object
+  // are also indicated as down in the other object.
+  int num_down = 0;
+
+  int i;
+  for (i = 0; i < (int)_button_list.size(); i++) {
+    if (is_down(i)) {
+      if (!other.is_down(_button_list[i])) {
+        return false;
+      }
+      num_down++;
+    }
+  }
+
+  // Now make sure the total number of buttons indicated as down in
+  // our object matches the number indicated as down in the other
+  // object.  This ensures there aren't any additional buttons
+  // indicated down in the other object.
+  int num_other_buttons = other.get_num_buttons();
+  int num_other_down = 0;
+  for (i = 0; i < num_other_buttons; i++) {
+    if (other.is_down(i)) {
+      num_other_down++;
+    }
+  }
+
+  return (num_down == num_other_down);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ModifierButtons::add_button
 //     Function: ModifierButtons::add_button
 //       Access: Published
 //       Access: Published

+ 2 - 0
panda/src/putil/modifierButtons.h

@@ -42,6 +42,8 @@ PUBLISHED:
   INLINE bool operator != (const ModifierButtons &other) const;
   INLINE bool operator != (const ModifierButtons &other) const;
   INLINE bool operator < (const ModifierButtons &other) const;
   INLINE bool operator < (const ModifierButtons &other) const;
 
 
+  bool matches(const ModifierButtons &other) const;
+
   bool add_button(ButtonHandle button);
   bool add_button(ButtonHandle button);
   bool has_button(ButtonHandle button) const;
   bool has_button(ButtonHandle button) const;
   bool remove_button(ButtonHandle button);
   bool remove_button(ButtonHandle button);

+ 244 - 57
panda/src/tform/buttonThrower.cxx

@@ -18,11 +18,12 @@
 
 
 #include "buttonThrower.h"
 #include "buttonThrower.h"
 
 
-#include <buttonEventDataTransition.h>
-#include <buttonEventDataTransition.h>
-#include <buttonEvent.h>
-#include <allTransitionsWrapper.h>
-#include <throw_event.h>
+#include "buttonEventDataTransition.h"
+#include "buttonEventDataTransition.h"
+#include "buttonEvent.h"
+#include "allTransitionsWrapper.h"
+#include "throw_event.h"
+#include "indent.h"
 
 
 TypeHandle ButtonThrower::_type_handle;
 TypeHandle ButtonThrower::_type_handle;
 TypeHandle ButtonThrower::_button_events_type;
 TypeHandle ButtonThrower::_button_events_type;
@@ -30,17 +31,27 @@ TypeHandle ButtonThrower::_button_events_type;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ButtonThrower::Constructor
 //     Function: ButtonThrower::Constructor
-//       Access: Public
+//       Access: Published
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 ButtonThrower::
 ButtonThrower::
 ButtonThrower(const string &name) : DataNode(name) {
 ButtonThrower(const string &name) : DataNode(name) {
+  _throw_buttons_active = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonThrower::Destructor
+//       Access: Published, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+ButtonThrower::
+~ButtonThrower() {
 }
 }
 
 
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ButtonThrower::set_prefix
 //     Function: ButtonThrower::set_prefix
-//       Access: Public
+//       Access: Published
 //  Description: Sets the prefix which is prepended to all event names
 //  Description: Sets the prefix which is prepended to all event names
 //               thrown by this object.
 //               thrown by this object.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -52,7 +63,7 @@ set_prefix(const string &prefix) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ButtonThrower::has_prefix
 //     Function: ButtonThrower::has_prefix
-//       Access: Public
+//       Access: Published
 //  Description: Returns true if the ButtonThrower has a prefix set,
 //  Description: Returns true if the ButtonThrower has a prefix set,
 //               false otherwise.
 //               false otherwise.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -63,7 +74,7 @@ has_prefix() const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ButtonThrower::get_prefix
 //     Function: ButtonThrower::get_prefix
-//       Access: Public
+//       Access: Published
 //  Description: Returns the prefix that has been set on this
 //  Description: Returns the prefix that has been set on this
 //               ButtonThrower.  See set_prefix().
 //               ButtonThrower.  See set_prefix().
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -74,7 +85,7 @@ get_prefix() const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ButtonThrower::get_modifier_buttons
 //     Function: ButtonThrower::get_modifier_buttons
-//       Access: Public
+//       Access: Published
 //  Description: Returns the set of ModifierButtons that the
 //  Description: Returns the set of ModifierButtons that the
 //               ButtonThrower will consider important enough to
 //               ButtonThrower will consider important enough to
 //               prepend the event name with.  Normally, this set will
 //               prepend the event name with.  Normally, this set will
@@ -92,7 +103,7 @@ get_modifier_buttons() const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ButtonThrower::set_modifier_buttons
 //     Function: ButtonThrower::set_modifier_buttons
-//       Access: Public
+//       Access: Published
 //  Description: Changes the set of ModifierButtons that the
 //  Description: Changes the set of ModifierButtons that the
 //               ButtonThrower will consider important enough to
 //               ButtonThrower will consider important enough to
 //               prepend the event name with.  Normally, this set will
 //               prepend the event name with.  Normally, this set will
@@ -114,44 +125,202 @@ set_modifier_buttons(const ModifierButtons &mods) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: ButtonThrower::get_throw_buttons
-//       Access: Public
-//  Description: Returns the set of buttons that the ButtonThrower
-//               processes.  See set_throw_buttons().
+//     Function: ButtonThrower::set_throw_buttons_active
+//       Access: Published
+//  Description: Sets the flag that indicates whether the
+//               ButtonThrower will only process events for the
+//               explicitly named buttons or not.  Normally this is
+//               false, meaning all buttons are processed; set it true
+//               to indicate that only some buttons should be
+//               processed.  See add_throw_button().
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-const ModifierButtons &ButtonThrower::
-get_throw_buttons() const {
-  return _throw_buttons;
+void ButtonThrower::
+set_throw_buttons_active(bool flag) {
+  _throw_buttons_active = flag;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: ButtonThrower::set_throw_buttons
-//       Access: Public
-//  Description: Changes the set of buttons that the ButtonThrower
-//               processes.  This is a ModifierButtons object that is
-//               used only for its ability to manage a list of
-//               buttons; the up/down state of the buttons within the
-//               ModifierButtons object is not used.
+//     Function: ButtonThrower::get_throw_buttons_active
+//       Access: Published
+//  Description: Returns the flag that indicates whether the
+//               ButtonThrower will only process events for the
+//               explicitly named buttons or not.  See
+//               set_throw_buttons_active().
+////////////////////////////////////////////////////////////////////
+bool ButtonThrower::
+get_throw_buttons_active() const {
+  return _throw_buttons_active;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonThrower::add_throw_button
+//       Access: Published
+//  Description: Adds a new button to the set of buttons that the
+//               ButtonThrower explicitly processes.
 //
 //
-//               If this is an empty set (which is the default), the
-//               ButtonThrower will process all buttons.  Otherwise,
-//               if the set is nonempty, it restricts the
-//               ButtonThrower to only throw events for the indicated
-//               buttons; buttons not on the list will be ignored by
-//               this object and passed on downstream to the child
-//               node(s) in the data graph.  A button that *is* on the
-//               list will be processed by the ButtonThrower and not
-//               passed on to the child node(s).
+//               If set_throw_buttons_active is false (which is the
+//               default), the ButtonThrower will process all buttons.
+//               Otherwise, the ButtonThrower will only process events
+//               for the button(s) explicitly named by this function;
+//               buttons not on the list will be ignored by this
+//               object and passed on downstream to the child node(s)
+//               in the data graph.  A button that *is* on the list
+//               will be processed by the ButtonThrower and not passed
+//               on to the child node(s).
+//
+//               The return value is true if the button is added, or
+//               false if it was already in the set.
+////////////////////////////////////////////////////////////////////
+bool ButtonThrower::
+add_throw_button(const ModifierButtons &mods, const ButtonHandle &button) {
+  ThrowButtonDef &def = _throw_buttons[button];
+
+  // This is a vector of ModifierButtons for which the indicated
+  // button is handled.  Make sure the current ModifierButtons object
+  // is not already on the list.
+  ThrowButtonDef::iterator di;
+  for (di = def.begin(); di != def.end(); ++di) {
+    if (mods.matches(*di)) {
+      return false;
+    }
+  }
+
+  def.push_back(mods);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonThrower::remove_throw_button
+//       Access: Published
+//  Description: Removes the indicated button from the set of buttons
+//               that the ButtonThrower explicitly processes.  See
+//               add_throw_button().
+//
+//               The return value is true if the button is removed, or
+//               false if it was not on the set.
+////////////////////////////////////////////////////////////////////
+bool ButtonThrower::
+remove_throw_button(const ModifierButtons &mods, const ButtonHandle &button) {
+  ThrowButtons::iterator ti = _throw_buttons.find(button);
+  if (ti == _throw_buttons.end()) {
+    // No buttons of this kind are in the set.
+    return false;
+  }
+
+  ThrowButtonDef &def = (*ti).second;
+
+  // This is a vector of ModifierButtons for which the indicated
+  // button is handled.
+  ThrowButtonDef::iterator di;
+  for (di = def.begin(); di != def.end(); ++di) {
+    if (mods.matches(*di)) {
+      def.erase(di);
+      if (def.empty()) {
+        _throw_buttons.erase(ti);
+      }
+      return true;
+    }
+  }
+
+  // The indicated ModifierButtons are not applied to this button in
+  // the set.
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonThrower::has_throw_button
+//       Access: Published
+//  Description: Returns true if the indicated button is on the set of
+//               buttons that will be processed by the ButtonThrower,
+//               false otherwise.  See add_throw_button().
+////////////////////////////////////////////////////////////////////
+bool ButtonThrower::
+has_throw_button(const ModifierButtons &mods, const ButtonHandle &button) const {
+  ThrowButtons::const_iterator ti = _throw_buttons.find(button);
+  if (ti == _throw_buttons.end()) {
+    // No buttons of this kind are in the set.
+    return false;
+  }
+
+  const ThrowButtonDef &def = (*ti).second;
+
+  // This is a vector of ModifierButtons for which the indicated
+  // button is handled.
+  ThrowButtonDef::const_iterator di;
+  for (di = def.begin(); di != def.end(); ++di) {
+    if (mods.matches(*di)) {
+      return true;
+    }
+  }
+
+  // The indicated ModifierButtons are not applied to this button in
+  // the set.
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonThrower::has_throw_button
+//       Access: Published
+//  Description: Returns true if the indicated button, in conjunction
+//               with any nonspecified modifier buttons, is on the set
+//               of buttons that will be processed by the
+//               ButtonThrower.  That is to say, returns true if this
+//               button was ever passed as the second parameter
+//               add_throw_button(), regardless of what the first
+//               parameter was.
+////////////////////////////////////////////////////////////////////
+bool ButtonThrower::
+has_throw_button(const ButtonHandle &button) const {
+  ThrowButtons::const_iterator ti = _throw_buttons.find(button);
+  if (ti == _throw_buttons.end()) {
+    // No buttons of this kind are in the set.
+    return false;
+  }
+
+  const ThrowButtonDef &def = (*ti).second;
+  return !def.empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonThrower::clear_throw_buttons
+//       Access: Published
+//  Description: Empties the set of buttons that were added via
+//               add_throw_button().  See add_throw_button().
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void ButtonThrower::
 void ButtonThrower::
-set_throw_buttons(const ModifierButtons &throw_buttons) {
-  _throw_buttons = throw_buttons;
+clear_throw_buttons() {
+  _throw_buttons.clear();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonThrower::write
+//       Access: Public, Virtual
+//  Description: Throw all events for button events found in the data
+//               element.
+////////////////////////////////////////////////////////////////////
+void ButtonThrower::
+write(ostream &out, int indent_level) const {
+  DataNode::write(out, indent_level);
+  if (_throw_buttons_active) {
+    indent(out, indent_level)
+      << "Processing keys:\n";
+    // Write the list of buttons that we're processing too.
+    ThrowButtons::const_iterator ti;
+    for (ti = _throw_buttons.begin(); ti != _throw_buttons.end(); ++ti) {
+      ButtonHandle button = (*ti).first;
+      const ThrowButtonDef &def = (*ti).second;
+      ThrowButtonDef::const_iterator di;
+      for (di = def.begin(); di != def.end(); ++di) {
+        indent(out, indent_level + 2)
+          << (*di).get_prefix() << button.get_name() << "\n";
+      }
+    }
+  }    
+}
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ButtonThrower::transmit_data
 //     Function: ButtonThrower::transmit_data
-//       Access: Public
+//       Access: Public, Virtual
 //  Description: Throw all events for button events found in the data
 //  Description: Throw all events for button events found in the data
 //               element.
 //               element.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -164,31 +333,49 @@ transmit_data(AllTransitionsWrapper &data) {
     ButtonEventDataTransition::const_iterator bi;
     ButtonEventDataTransition::const_iterator bi;
     for (bi = b->begin(); bi != b->end(); ++bi) {
     for (bi = b->begin(); bi != b->end(); ++bi) {
       const ButtonEvent &be = (*bi);
       const ButtonEvent &be = (*bi);
-      if (_throw_buttons.get_num_buttons() == 0 ||
-          _throw_buttons.has_button(be._button)) {
-        // Process this button.
-        string event_name = _prefix + be._button.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.
-            event_name = _mods.get_prefix() + event_name;
-          }
+      string event_name = be._button.get_name();
+
+      if (be._down) {
+        // Button 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.
+          event_name = _mods.get_prefix() + event_name;
+        }
+
+        if (!_throw_buttons_active || has_throw_button(_mods, be._button)) {
+          // Process this button.
+          throw_event(_prefix + event_name);
           
           
         } else {
         } else {
-          _mods.button_up(be._button);
-          event_name += "-up";
+          // Don't process this button; instead, pass it down to future
+          // generations.
+          if (new_b == (ButtonEventDataTransition *)NULL) {
+            new_b = new ButtonEventDataTransition;
+          }
+          new_b->push_back(be);
         }
         }
-
-        throw_event(event_name);
-
+          
       } else {
       } else {
-        // Don't process this button; instead, pass it down to future
-        // generations.
-        if (new_b == (ButtonEventDataTransition *)NULL) {
-          new_b = new ButtonEventDataTransition;
+        // Button up.
+        _mods.button_up(be._button);
+
+        // We always throw button "up" events if we have any
+        // definition for the button at all, regardless of the state
+        // of the modifier keys.
+        if (!_throw_buttons_active || has_throw_button(be._button)) {
+          throw_event(_prefix + event_name + "-up");
+        }
+        if (_throw_buttons_active) {
+          // Now pass the event on to future generations.  We always
+          // pass "up" events, even if we are intercepting this
+          // particular button; unless we're processing all buttons in
+          // which case it doesn't matter.
+          if (new_b == (ButtonEventDataTransition *)NULL) {
+            new_b = new ButtonEventDataTransition;
+          }
+          new_b->push_back(be);
         }
         }
-        new_b->push_back(be);
       }
       }
     }
     }
   }
   }

+ 23 - 8
panda/src/tform/buttonThrower.h

@@ -19,10 +19,12 @@
 #ifndef BUTTONTHROWER_H
 #ifndef BUTTONTHROWER_H
 #define BUTTONTHROWER_H
 #define BUTTONTHROWER_H
 
 
-#include <pandabase.h>
+#include "pandabase.h"
 
 
-#include <dataNode.h>
-#include <modifierButtons.h>
+#include "dataNode.h"
+#include "modifierButtons.h"
+#include "pvector.h"
+#include "pmap.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : ButtonThrower
 //       Class : ButtonThrower
@@ -40,6 +42,7 @@
 class EXPCL_PANDA ButtonThrower : public DataNode {
 class EXPCL_PANDA ButtonThrower : public DataNode {
 PUBLISHED:
 PUBLISHED:
   ButtonThrower(const string &name = "");
   ButtonThrower(const string &name = "");
+  ~ButtonThrower();
 
 
   void set_prefix(const string &prefix);
   void set_prefix(const string &prefix);
   bool has_prefix() const;
   bool has_prefix() const;
@@ -48,19 +51,31 @@ PUBLISHED:
   const ModifierButtons &get_modifier_buttons() const;
   const ModifierButtons &get_modifier_buttons() const;
   void set_modifier_buttons(const ModifierButtons &mods);
   void set_modifier_buttons(const ModifierButtons &mods);
 
 
-  const ModifierButtons &get_throw_buttons() const;
-  void set_throw_buttons(const ModifierButtons &throw_buttons);
+  void set_throw_buttons_active(bool flag);
+  bool get_throw_buttons_active() const;
 
 
-protected:
+  bool add_throw_button(const ModifierButtons &mods, const ButtonHandle &button);
+  bool remove_throw_button(const ModifierButtons &mods, const ButtonHandle &button);
+  bool has_throw_button(const ModifierButtons &mods, const ButtonHandle &button) const;
+  bool has_throw_button(const ButtonHandle &button) const;
+  void clear_throw_buttons();
+
+public:
+  virtual void write(ostream &out, int indent_level = 0) const;
+
+private:
   string _prefix;
   string _prefix;
   ModifierButtons _mods;
   ModifierButtons _mods;
-  ModifierButtons _throw_buttons;
+
+  typedef pvector<ModifierButtons> ThrowButtonDef;
+  typedef pmap<ButtonHandle, ThrowButtonDef> ThrowButtons;
+  ThrowButtons _throw_buttons;
+  bool _throw_buttons_active;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 // From parent class DataNode
 // From parent class DataNode
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 public:
 public:
-
   virtual void
   virtual void
   transmit_data(AllTransitionsWrapper &data);
   transmit_data(AllTransitionsWrapper &data);