瀏覽代碼

Fix MouseRecorder & let PandaNode inherit from TypedWritableReferenceCount

rdb 11 年之前
父節點
當前提交
0a64bdb33b

+ 14 - 1
dtool/src/dtoolbase/dtoolbase_cc.h

@@ -35,6 +35,8 @@ using namespace std;
 #define TYPENAME typename
 #define CONSTEXPR
 #define NOEXCEPT noexcept
+#define FINAL
+#define OVERRIDE
 
 #define EXPORT_TEMPLATE_CLASS(expcl, exptp, classname)
 
@@ -121,6 +123,7 @@ typedef ios::seekdir ios_seekdir;
 #define INLINE inline
 #endif
 
+// Determine the availability of C++11 features.
 #if defined(__has_extension) // Clang magic.
 #  if __has_extension(cxx_constexpr)
 #    define CONSTEXPR constexpr
@@ -135,20 +138,30 @@ typedef ios::seekdir ios_seekdir;
 #  if __has_extension(cxx_rvalue_references) && (__cplusplus >= 201103L)
 #    define USE_MOVE_SEMANTICS
 #  endif
+#  if __has_extension(cxx_override_control)
+#    define FINAL final
+#    define OVERRIDE override
+#  endif
 #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)) && (__cplusplus >= 201103L)
 // noexcept was introduced in GCC 4.6, constexpr in GCC 4.7, rvalue refs in
 // GCC 4.3.  However, GCC only started defining __cplusplus properly in 4.7.
 #  define CONSTEXPR constexpr
 #  define NOEXCEPT noexcept
 #  define USE_MOVE_SEMANTICS
+#  define FINAL final
+#  define OVERRIDE override
 #elif defined(_MSC_VER) && _MSC_VER >= 1600
-// MSVC 2010 has move semantics.
+// MSVC 2010 has move semantics.  Not much else.
 #  define CONSTEXPR INLINE
 #  define NOEXCEPT throw()
 #  define USE_MOVE_SEMANTICS
+#  define FINAL
+#  define OVERRIDE
 #else
 #  define CONSTEXPR INLINE
 #  define NOEXCEPT
+#  define FINAL
+#  define OVERRIDE
 #endif
 
 #if defined(WIN32_VC) && !defined(LINK_ALL_STATIC) && defined(EXPORT_TEMPLATES)

+ 4 - 5
panda/src/bullet/bulletContactCallbacks.h

@@ -25,7 +25,6 @@
 #include "event.h"
 #include "eventQueue.h"
 #include "eventParameter.h"
-#include "eventStorePandaNode.h"
 #include "pandaNode.h"
 
 struct UserPersitentData {
@@ -77,8 +76,8 @@ contact_added_callback(btManifoldPoint &cp,
     if (bullet_enable_contact_events) {
 
       Event *event = new Event("bullet-contact-added");
-      event->add_parameter(EventParameter(new EventStorePandaNode(node0)));
-      event->add_parameter(EventParameter(new EventStorePandaNode(node1)));
+      event->add_parameter(EventParameter(node0));
+      event->add_parameter(EventParameter(node1));
 
       EventQueue::get_global_event_queue()->queue_event(event);
     }
@@ -137,8 +136,8 @@ contact_destroyed_callback(void *userPersistentData) {
   if (bullet_enable_contact_events) {
 
     Event *event = new Event("bullet-contact-destroyed");
-    event->add_parameter(EventParameter(new EventStorePandaNode(data->node0)));
-    event->add_parameter(EventParameter(new EventStorePandaNode(data->node1)));
+    event->add_parameter(EventParameter(data->node0));
+    event->add_parameter(EventParameter(data->node1));
 
     EventQueue::get_global_event_queue()->queue_event(event);
   }

+ 4 - 8
panda/src/pgraph/light.h

@@ -39,11 +39,7 @@ class GraphicsStateGuardianBase;
 //               arbitrary point to define the coordinate system of
 //               effect.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA_PGRAPH Light : virtual public ReferenceCount {
-  // We inherit from ReferenceCount instead of TypedReferenceCount so
-  // that LightNode does not inherit from TypedObject twice.  Note
-  // that we also inherit virtually from ReferenceCount for the same
-  // reason.
+class EXPCL_PANDA_PGRAPH Light {
 PUBLISHED:
   INLINE Light();
   INLINE Light(const Light &copy);
@@ -66,7 +62,7 @@ public:
                     int light_id)=0;
 
   virtual bool get_vector_to_light(LVector3 &result,
-                                   const LPoint3 &from_object_point, 
+                                   const LPoint3 &from_object_point,
                                    const LMatrix4 &to_object_space);
 
   GeomNode *get_viz();
@@ -76,7 +72,7 @@ public:
 protected:
   virtual void fill_viz_geom(GeomNode *viz_geom);
   INLINE void mark_viz_stale();
-  
+
   // This enumerated class defines the relative class priority of
   // different kinds of lights.  This hierarchy is only used to
   // resolve multiple lights of the same priority specified by
@@ -135,7 +131,7 @@ public:
   virtual TypeHandle get_type() const {
     return get_class_type();
   }
-  
+
 private:
   static TypeHandle _type_handle;
 };

+ 1 - 13
panda/src/pgraph/pandaNode.cxx

@@ -142,8 +142,7 @@ PandaNode::
 ////////////////////////////////////////////////////////////////////
 PandaNode::
 PandaNode(const PandaNode &copy) :
-  ReferenceCount(copy),
-  TypedWritable(copy),
+  TypedWritableReferenceCount(copy),
   Namable(copy),
   _paths_lock("PandaNode::_paths_lock"),
   _dirty_prev_transform(false)
@@ -209,17 +208,6 @@ operator = (const PandaNode &copy) {
   nassertv(false);
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::as_reference_count
-//       Access: Public, Virtual
-//  Description: Returns the pointer cast to a ReferenceCount pointer,
-//               if it is in fact of that type.
-////////////////////////////////////////////////////////////////////
-ReferenceCount *PandaNode::
-as_reference_count() {
-  return this;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::dupe_for_flatten
 //       Access: Public, Virtual

+ 2 - 4
panda/src/pgraph/pandaNode.h

@@ -69,9 +69,8 @@ class GraphicsStateGuardianBase;
 //               is the base class of all specialized nodes, and also
 //               serves as a generic node with no special properties.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA_PGRAPH PandaNode : public TypedWritable, public Namable,
-                              public LinkedListNode,
-                              virtual public ReferenceCount {
+class EXPCL_PANDA_PGRAPH PandaNode : public TypedWritableReferenceCount,
+                                     public Namable, public LinkedListNode {
 PUBLISHED:
   PandaNode(const string &name);
   virtual ~PandaNode();
@@ -84,7 +83,6 @@ private:
   void operator = (const PandaNode &copy);
 
 public:
-  virtual ReferenceCount *as_reference_count();
   virtual PandaNode *dupe_for_flatten() const;
 
   virtual bool safe_to_flatten() const;

+ 10 - 6
panda/src/recorder/mouseRecorder.cxx

@@ -26,14 +26,16 @@ TypeHandle MouseRecorder::_type_handle;
 //  Description:
 ////////////////////////////////////////////////////////////////////
 MouseRecorder::
-MouseRecorder(const string &name) : 
-  DataNode(name) 
+MouseRecorder(const string &name) :
+  DataNode(name)
 {
   _pixel_xy_input = define_input("pixel_xy", EventStoreVec2::get_class_type());
+  _pixel_size_input = define_input("pixel_size", EventStoreVec2::get_class_type());
   _xy_input = define_input("xy", EventStoreVec2::get_class_type());
   _button_events_input = define_input("button_events", ButtonEventList::get_class_type());
 
   _pixel_xy_output = define_output("pixel_xy", EventStoreVec2::get_class_type());
+  _pixel_size_output = define_output("pixel_size", EventStoreVec2::get_class_type());
   _xy_output = define_output("xy", EventStoreVec2::get_class_type());
   _button_events_output = define_output("button_events", ButtonEventList::get_class_type());
 
@@ -127,7 +129,7 @@ write(ostream &out, int indent_level) const {
 //               calls.
 ////////////////////////////////////////////////////////////////////
 void MouseRecorder::
-do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &input, 
+do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &input,
                  DataNodeTransmit &output) {
   bool has_mouse = false;
   LPoint2 mouse_xy;
@@ -157,7 +159,7 @@ do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &input,
       mouse_pixel_xy = xy->get_value();
       has_mouse = true;
     }
-    
+
     // Look for button events.
     if (input.has_data(_button_events_input)) {
       const ButtonEventList *button_events;
@@ -165,7 +167,7 @@ do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &input,
       _live_button_events->add_events(*button_events);
     }
   }
-    
+
   // Now rebuild the output data for our children.
 
   if (has_mouse) {
@@ -187,8 +189,10 @@ do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &input,
     _mouse_pixel_xy = mouse_pixel_xy;
     _save_button_events->add_events(*_live_button_events);
   }
-}
 
+  // We always pass the pixel_size data through.
+  output.set_data(_pixel_size_output, input.get_data(_pixel_size_input));
+}
 
 ////////////////////////////////////////////////////////////////////
 //     Function: MouseRecorder::register_with_read_factory

+ 5 - 1
panda/src/recorder/mouseRecorder.h

@@ -57,11 +57,13 @@ protected:
 private:
   // inputs
   int _pixel_xy_input;
+  int _pixel_size_input;
   int _xy_input;
   int _button_events_input;
 
   // outputs
   int _pixel_xy_output;
+  int _pixel_size_output;
   int _xy_output;
   int _button_events_output;
 
@@ -79,6 +81,9 @@ public:
   virtual void write_datagram(BamWriter *manager, Datagram &dg);
   virtual void write_recorder(BamWriter *manager, Datagram &dg);
 
+  INLINE virtual void ref() const FINAL { ReferenceCount::ref(); };
+  INLINE virtual bool unref() const FINAL { return ReferenceCount::unref(); };
+
 protected:
   static TypedWritable *make_from_bam(const FactoryParams &params);
   static RecorderBase *make_recorder(const FactoryParams &params);
@@ -106,4 +111,3 @@ private:
 };
 
 #endif
-

+ 13 - 3
panda/src/recorder/recorderBase.h

@@ -46,8 +46,13 @@ class TypedWritable;
 //               write_recorder() to do exactly the same thing as
 //               write_datagram(), or they may choose to write
 //               something slightly different.
+//
+//               Most types of recorders should derive from Recorder,
+//               as it derives from ReferenceCount, except for
+//               MouseRecorder, which would otherwise doubly inherit
+//               from ReferenceCount.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA_RECORDER RecorderBase : virtual public ReferenceCount {
+class EXPCL_PANDA_RECORDER RecorderBase {
 protected:
   RecorderBase();
 
@@ -63,6 +68,11 @@ public:
 
   virtual void write_recorder(BamWriter *manager, Datagram &dg);
 
+  // We can't let RecorderBase inherit from ReferenceCount, so we
+  // define these so we can still manage the reference count.
+  virtual void ref() const=0;
+  virtual bool unref() const=0;
+
 protected:
   void fillin_recorder(DatagramIterator &scan, BamReader *manager);
 
@@ -72,7 +82,7 @@ private:
     F_playing   = 0x0002,
   };
   short _flags;
-  
+
 public:
   static TypeHandle get_class_type() {
     return _type_handle;
@@ -90,9 +100,9 @@ private:
   static TypeHandle _type_handle;
 
   friend class RecorderController;
+  friend class RecorderTable;
 };
 
 #include "recorderBase.I"
 
 #endif
-

+ 12 - 42
panda/src/recorder/recorderController.cxx

@@ -93,17 +93,11 @@ begin_record(const Filename &filename) {
   _user_table_modified = true;
 
   // Tell all of our recorders that they're live now.
-  RecorderTable::Recorders::iterator ri;
-  for (ri = _user_table->_recorders.begin(); 
-       ri != _user_table->_recorders.end(); 
-       ++ri) {
-    RecorderBase *recorder = (*ri).second;
-    recorder->_flags |= RecorderBase::F_recording;
-  }
+  _user_table->set_flags(RecorderBase::F_recording);
 
-  recorder_cat.info() 
+  recorder_cat.info()
     << "Recording session to " << _filename << "\n";
-    
+
   return true;
 }
 
@@ -150,7 +144,7 @@ begin_playback(const Filename &filename) {
   // Start out by reading the RecorderHeader.
   TypedWritable *object = _reader->read_object();
 
-  if (object == (TypedWritable *)NULL || 
+  if (object == (TypedWritable *)NULL ||
       !object->is_of_type(RecorderHeader::get_class_type())) {
     recorder_cat.error()
       << _filename << " does not contain a recorded session.\n";
@@ -176,7 +170,7 @@ begin_playback(const Filename &filename) {
     return false;
   }
 
-  recorder_cat.info() 
+  recorder_cat.info()
     << "Playing back session from " << _filename << "\n";
 
   return true;
@@ -194,26 +188,14 @@ close() {
     _writer = NULL;
 
     // Tell all of our recorders that they're no longer recording.
-    RecorderTable::Recorders::iterator ri;
-    for (ri = _user_table->_recorders.begin(); 
-         ri != _user_table->_recorders.end(); 
-         ++ri) {
-      RecorderBase *recorder = (*ri).second;
-      recorder->_flags &= ~RecorderBase::F_recording;
-    }
+    _user_table->clear_flags(RecorderBase::F_recording);
   }
   if (_reader != (BamReader *)NULL) {
     delete _reader;
     _reader = NULL;
 
     // Tell all of our recorders that they're no longer playing.
-    RecorderTable::Recorders::iterator ri;
-    for (ri = _active_table->_recorders.begin(); 
-         ri != _active_table->_recorders.end(); 
-         ++ri) {
-      RecorderBase *recorder = (*ri).second;
-      recorder->_flags &= ~RecorderBase::F_playing;
-    }
+    _active_table->clear_flags(RecorderBase::F_playing);
   }
   _dout.close();
   _din.close();
@@ -244,7 +226,7 @@ record_frame() {
 
     RecorderFrame data(now, frame, _user_table_modified, _user_table);
     _user_table_modified = false;
-    
+
     _writer->write_object(&data);
   }
 }
@@ -300,27 +282,15 @@ play_frame() {
       if (_next_frame->_table_changed || _user_table_modified) {
         // We're about to change the active table.  Temporarily
         // disable the playing flag on the currently-active recorders.
-        RecorderTable::Recorders::iterator ri;
-        for (ri = _active_table->_recorders.begin(); 
-             ri != _active_table->_recorders.end(); 
-             ++ri) {
-          RecorderBase *recorder = (*ri).second;
-          recorder->_flags &= ~RecorderBase::F_playing;
-        }
-
+        _active_table->clear_flags(RecorderBase::F_playing);
         delete _active_table;
         _active_table = new RecorderTable(*_file_table);
         _active_table->merge_from(*_user_table);
         _user_table_modified = false;
-        
+
         // Now reenable the playing flag on the newly-active
         // recorders.
-        for (ri = _active_table->_recorders.begin(); 
-             ri != _active_table->_recorders.end(); 
-             ++ri) {
-          RecorderBase *recorder = (*ri).second;
-          recorder->_flags |= RecorderBase::F_playing;
-        }
+        _active_table->set_flags(RecorderBase::F_playing);
       }
 
       _next_frame->_table = _active_table;
@@ -353,7 +323,7 @@ RecorderFrame *RecorderController::
 read_frame() {
   TypedWritable *object = _reader->read_object();
 
-  if (object == (TypedWritable *)NULL || 
+  if (object == (TypedWritable *)NULL ||
       !object->is_of_type(RecorderFrame::get_class_type())) {
     return NULL;
   }

+ 3 - 16
panda/src/recorder/recorderFrame.cxx

@@ -30,14 +30,7 @@ TypeHandle RecorderFrame::_type_handle;
 void RecorderFrame::
 play_frame(BamReader *manager) {
   DatagramIterator scan(_data, _data_pos);
-
-  RecorderTable::Recorders::iterator ri;
-  for (ri = _table->_recorders.begin(); 
-       ri != _table->_recorders.end(); 
-       ++ri) {
-    RecorderBase *recorder = (*ri).second;
-    recorder->play_frame(scan, manager);
-  }
+  _table->play_frame(scan, manager);
 
   // We expect to use up all of the data in the datagram.
   nassertv(scan.get_remaining_size() == 0);
@@ -77,14 +70,8 @@ write_datagram(BamWriter *manager, Datagram &dg) {
     _local_table = *_table;
     manager->write_pointer(dg, &_local_table);
   }
-  
-  RecorderTable::Recorders::iterator ri;
-  for (ri = _table->_recorders.begin(); 
-       ri != _table->_recorders.end(); 
-       ++ri) {
-    RecorderBase *recorder = (*ri).second;
-    recorder->record_frame(manager, dg);
-  }
+
+  _table->record_frame(manager, dg);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 55 - 8
panda/src/recorder/recorderTable.I

@@ -29,10 +29,8 @@ RecorderTable() {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE RecorderTable::
-RecorderTable(const RecorderTable &copy) :
-  _recorders(copy._recorders),
-  _error(copy._error)
-{
+RecorderTable(const RecorderTable &copy) {
+  *this = copy;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -44,13 +42,62 @@ INLINE void RecorderTable::
 operator = (const RecorderTable &copy) {
   _recorders = copy._recorders;
   _error = copy._error;
+
+  Recorders::iterator ri;
+  for (ri = _recorders.begin(); ri != _recorders.end(); ++ri) {
+    ri->second->ref();
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: RecorderTable::Destructor
+//     Function: RecorderTable::add_recorder
 //       Access: Published
-//  Description:
+//  Description: Adds the named recorder to the set of recorders.
 ////////////////////////////////////////////////////////////////////
-INLINE RecorderTable::
-~RecorderTable() {
+INLINE void RecorderTable::
+add_recorder(const string &name, RecorderBase *recorder) {
+  nassertv(recorder != (RecorderBase *)NULL);
+  recorder->ref();
+
+  std::pair<Recorders::iterator, bool> result =
+    _recorders.insert(Recorders::value_type(name, recorder));
+
+  if (!result.second) {
+    // Take out the previous one first.
+    unref_delete(result.first->second);
+    result.first->second = recorder;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RecorderTable::get_recorder
+//       Access: Published
+//  Description: Returns the recorder with the indicated name, or NULL
+//               if there is no such recorder.
+////////////////////////////////////////////////////////////////////
+INLINE RecorderBase *RecorderTable::
+get_recorder(const string &name) const {
+  Recorders::const_iterator ri = _recorders.find(name);
+  if (ri != _recorders.end()) {
+    return (*ri).second;
+  }
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RecorderTable::remove_recorder
+//       Access: Published
+//  Description: Removes the named recorder from the table.  Returns
+//               true if successful, false if there was no such
+//               recorder.
+////////////////////////////////////////////////////////////////////
+INLINE bool RecorderTable::
+remove_recorder(const string &name) {
+  Recorders::iterator ri = _recorders.find(name);
+  if (ri != _recorders.end()) {
+    unref_delete(ri->second);
+    _recorders.erase(ri);
+    return true;
+  }
+  return false;
 }

+ 71 - 31
panda/src/recorder/recorderTable.cxx

@@ -21,6 +21,19 @@
 
 TypeHandle RecorderTable::_type_handle;
 
+////////////////////////////////////////////////////////////////////
+//     Function: RecorderTable::Destructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+RecorderTable::
+~RecorderTable() {
+  Recorders::iterator ri;
+  for (ri = _recorders.begin(); ri != _recorders.end(); ++ri) {
+    unref_delete(ri->second);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: RecorderTable::merge_from
 //       Access: Public
@@ -47,12 +60,16 @@ merge_from(const RecorderTable &other) {
     } else if ((*ri).second->get_type() == recorder->get_type()) {
       // If we already had a recorder by that name with the same type,
       // throw it away (otherwise, keep the one we had before).
-      (*ri).second = recorder;
+      if ((*ri).second != recorder) {
+        recorder->ref();
+        unref_delete((*ri).second);
+        (*ri).second = recorder;
+      }
 
     } else {
       recorder_cat.warning()
-        << "Keeping recorder " << name << " of type " 
-        << (*ri).second->get_type() << " instead of recorder of type " 
+        << "Keeping recorder " << name << " of type "
+        << (*ri).second->get_type() << " instead of recorder of type "
         << recorder->get_type() << "\n";
     }
   }
@@ -71,51 +88,73 @@ merge_from(const RecorderTable &other) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: RecorderTable::add_recorder
+//     Function: RecorderTable::record_frame
 //       Access: Published
-//  Description: Adds the named recorder to the set of recorders.
+//  Description: Calls record_frame on all recorders.
 ////////////////////////////////////////////////////////////////////
 void RecorderTable::
-add_recorder(const string &name, RecorderBase *recorder) {
-  _recorders[name] = recorder;
+record_frame(BamWriter *manager, Datagram &dg) {
+  Recorders::iterator ri;
+  for (ri = _recorders.begin();
+       ri != _recorders.end();
+       ++ri) {
+    RecorderBase *recorder = (*ri).second;
+    recorder->record_frame(manager, dg);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: RecorderTable::get_recorder
+//     Function: RecorderTable::play_frame
 //       Access: Published
-//  Description: Returns the recorder with the indicated name, or NULL
-//               if there is no such recorder.
-////////////////////////////////////////////////////////////////////
-RecorderBase *RecorderTable::
-get_recorder(const string &name) const {
-  Recorders::const_iterator ri = _recorders.find(name);
-  if (ri != _recorders.end()) {
-    return (*ri).second;
+//  Description: Calls play_frame on all recorders.
+////////////////////////////////////////////////////////////////////
+void RecorderTable::
+play_frame(DatagramIterator &scan, BamReader *manager) {
+  Recorders::iterator ri;
+  for (ri = _recorders.begin();
+       ri != _recorders.end();
+       ++ri) {
+    RecorderBase *recorder = (*ri).second;
+    recorder->play_frame(scan, manager);
   }
-  return NULL;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: RecorderTable::remove_recorder
+//     Function: RecorderTable::set_flags
 //       Access: Published
-//  Description: Removes the named recorder from the table.  Returns
-//               true if successful, false if there was no such
-//               recorder.
-////////////////////////////////////////////////////////////////////
-bool RecorderTable::
-remove_recorder(const string &name) {
-  Recorders::iterator ri = _recorders.find(name);
-  if (ri != _recorders.end()) {
-    _recorders.erase(ri);
-    return true;
+//  Description: Sets the given flags on all recorders.
+////////////////////////////////////////////////////////////////////
+void RecorderTable::
+set_flags(short flags) {
+  Recorders::iterator ri;
+  for (ri = _recorders.begin();
+       ri != _recorders.end();
+       ++ri) {
+    RecorderBase *recorder = (*ri).second;
+    recorder->_flags |= flags;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RecorderTable::clear_flags
+//       Access: Published
+//  Description: Clears the given flags on all recorders.
+////////////////////////////////////////////////////////////////////
+void RecorderTable::
+clear_flags(short flags) {
+  Recorders::iterator ri;
+  for (ri = _recorders.begin();
+       ri != _recorders.end();
+       ++ri) {
+    RecorderBase *recorder = (*ri).second;
+    recorder->_flags &= ~flags;
   }
-  return false;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: RecorderTable::write
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 void RecorderTable::
 write(ostream &out, int indent_level) const {
@@ -203,7 +242,7 @@ fillin(DatagramIterator &scan, BamReader *manager) {
     FactoryParams fparams;
     fparams.add_param(new BamReaderParam(scan, manager));
 
-    PT(RecorderBase) recorder = 
+    RecorderBase *recorder =
       RecorderController::get_factory()->make_instance_more_general(type, fparams);
     if (recorder == (RecorderBase *)NULL) {
       recorder_cat.error()
@@ -211,6 +250,7 @@ fillin(DatagramIterator &scan, BamReader *manager) {
       _error = true;
 
     } else {
+      recorder->ref();
       bool inserted =
         _recorders.insert(Recorders::value_type(name, recorder)).second;
       nassertv(inserted);

+ 14 - 7
panda/src/recorder/recorderTable.h

@@ -37,18 +37,25 @@ public:
   INLINE RecorderTable();
   INLINE RecorderTable(const RecorderTable &copy);
   INLINE void operator = (const RecorderTable &copy);
-  INLINE ~RecorderTable();
+  ~RecorderTable();
 
   void merge_from(const RecorderTable &other);
 
-  void add_recorder(const string &name, RecorderBase *recorder);
-  RecorderBase *get_recorder(const string &name) const;
-  bool remove_recorder(const string &name);
+  INLINE void add_recorder(const string &name, RecorderBase *recorder);
+  INLINE RecorderBase *get_recorder(const string &name) const;
+  INLINE bool remove_recorder(const string &name);
 
-  void write(ostream &out, int indent_level) const;
+  void record_frame(BamWriter *manager, Datagram &dg);
+  void play_frame(DatagramIterator &scan, BamReader *manager);
+  void set_flags(short flags);
+  void clear_flags(short flags);
 
+  void write(ostream &out, int indent_level) const;
 
-  typedef pmap<string, PT(RecorderBase) > Recorders;
+  // RecorderBase itself doesn't inherit from ReferenceCount, so
+  // we can't put a PT() around it.  Instead, we manage the reference
+  // count using calls to ref() and unref().
+  typedef pmap<string, RecorderBase*> Recorders;
   Recorders _recorders;
 
   bool _error;
@@ -60,7 +67,7 @@ public:
 protected:
   static TypedWritable *make_from_bam(const FactoryParams &params);
   void fillin(DatagramIterator &scan, BamReader *manager);
-  
+
 public:
   static TypeHandle get_class_type() {
     return _type_handle;

+ 5 - 1
panda/src/recorder/socketStreamRecorder.h

@@ -42,7 +42,8 @@ class DatagramIterator;
 //               straight through to the socket if it is connected, or
 //               silently ignored if it is not.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA_RECORDER SocketStreamRecorder : public RecorderBase {
+class EXPCL_PANDA_RECORDER SocketStreamRecorder : public RecorderBase,
+                                                  public ReferenceCount {
 PUBLISHED:
   INLINE SocketStreamRecorder();
   INLINE SocketStreamRecorder(SocketStream *stream, bool owns_stream);
@@ -78,6 +79,9 @@ public:
   static void register_with_read_factory();
   virtual void write_recorder(BamWriter *manager, Datagram &dg);
 
+  INLINE virtual void ref() const FINAL { ReferenceCount::ref(); };
+  INLINE virtual bool unref() const FINAL { return ReferenceCount::unref(); };
+
 protected:
   static RecorderBase *make_recorder(const FactoryParams &params);
   void fillin_recorder(DatagramIterator &scan, BamReader *manager);

+ 9 - 7
panda/src/tform/Sources.pp

@@ -7,7 +7,7 @@
     p3grutil p3dgraph p3pgraph p3linmath p3display p3event p3putil p3gobj p3gsgbase \
     p3mathutil p3device
 
-  #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx 
+  #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx
 
   #define SOURCES  \
     buttonThrower.I buttonThrower.h \
@@ -16,12 +16,12 @@
     mouseInterfaceNode.I mouseInterfaceNode.h \
     mouseSubregion.I mouseSubregion.h \
     mouseWatcher.I mouseWatcher.h \
-    mouseWatcherGroup.h \
+    mouseWatcherBase.h mouseWatcherGroup.h \
     mouseWatcherParameter.I mouseWatcherParameter.h \
     mouseWatcherRegion.I mouseWatcherRegion.h \
     trackball.h \
-    transform2sg.h  
-     
+    transform2sg.h
+
   #define INCLUDED_SOURCES  \
     buttonThrower.cxx \
     config_tform.cxx \
@@ -29,10 +29,12 @@
     mouseInterfaceNode.cxx \
     mouseSubregion.cxx \
     mouseWatcher.cxx \
+    mouseWatcherBase.cxx \
     mouseWatcherGroup.cxx \
-    mouseWatcherParameter.cxx mouseWatcherRegion.cxx  \
+    mouseWatcherParameter.cxx
+    mouseWatcherRegion.cxx \
     trackball.cxx \
-    transform2sg.cxx 
+    transform2sg.cxx
 
   #define INSTALL_HEADERS \
     buttonThrower.I buttonThrower.h \
@@ -40,7 +42,7 @@
     mouseInterfaceNode.I mouseInterfaceNode.h \
     mouseSubregion.I mouseSubregion.h \
     mouseWatcher.I mouseWatcher.h \
-    mouseWatcherGroup.h \
+    mouseWatcherBase.h mouseWatcherGroup.h \
     mouseWatcherParameter.I mouseWatcherParameter.h \
     mouseWatcherRegion.I mouseWatcherRegion.h \
     trackball.h \

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

@@ -18,6 +18,7 @@
 #include "buttonThrower.h"
 #include "mouseSubregion.h"
 #include "mouseWatcher.h"
+#include "mouseWatcherBase.h"
 #include "mouseWatcherGroup.h"
 #include "mouseWatcherRegion.h"
 #include "trackball.h"
@@ -67,6 +68,7 @@ ConfigureFn(config_tform) {
   MouseInterfaceNode::init_type();
   MouseSubregion::init_type();
   MouseWatcher::init_type();
+  MouseWatcherBase::init_type();
   MouseWatcherGroup::init_type();
   MouseWatcherRegion::init_type();
   Trackball::init_type();

+ 57 - 57
panda/src/tform/mouseWatcher.cxx

@@ -44,7 +44,7 @@ TypeHandle MouseWatcher::_type_handle;
 //  Description:
 ////////////////////////////////////////////////////////////////////
 MouseWatcher::
-MouseWatcher(const string &name) : 
+MouseWatcher(const string &name) :
   DataNode(name)
 {
   _pixel_xy_input = define_input("pixel_xy", EventStoreVec2::get_class_type());
@@ -73,24 +73,24 @@ MouseWatcher(const string &name) :
   _button_down_display_region = (DisplayRegion *)NULL;
 
   _frame.set(-1.0f, 1.0f, -1.0f, 1.0f);
-  
+
   _inactivity_timeout = inactivity_timeout;
   _has_inactivity_timeout = !IS_NEARLY_ZERO(_inactivity_timeout);
-  
+
   _num_trail_recent = 0;
   _trail_log_duration = 0.0;
   _trail_log = new PointerEventList();
-  
+
   _inactivity_timeout_event = "inactivity_timeout";
   _last_activity = 0.0;
   _inactivity_state = IS_active;
-  
+
   // When this flag is true, the mouse pointer is allowed to be
   // "entered" into multiple regions simultaneously; when false, it
   // will only be "within" multiple regions, but "entered" into the
   // topmost of those.
   _enter_multiple = false;
-  
+
   // When this flag is true, moving the pointer into a region is
   // enough to click it.  The click is simulated with mouse button
   // one.
@@ -128,7 +128,7 @@ remove_region(MouseWatcherRegion *region) {
     _preferred_button_down_region = (MouseWatcherRegion *)NULL;
   }
 
-  return MouseWatcherGroup::do_remove_region(region);
+  return MouseWatcherBase::do_remove_region(region);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -156,7 +156,7 @@ get_over_region(const LPoint2 &pos) const {
 //               regions the MouseWatcher will monitor each frame.
 //
 //               Since the MouseWatcher itself inherits from
-//               MouseWatcherGroup, this operation is normally not
+//               MouseWatcherBase, this operation is normally not
 //               necessary--you can simply add the Regions you care
 //               about one at a time.  Adding a complete group is
 //               useful when you may want to explicitly remove the
@@ -171,7 +171,7 @@ add_group(MouseWatcherGroup *group) {
 
   // See if the group is in the set/vector already
   PT(MouseWatcherGroup) pt = group;
-  Groups::const_iterator gi = 
+  Groups::const_iterator gi =
     find(_groups.begin(), _groups.end(), pt);
   if (gi != _groups.end()) {
     // Already in the set, return false
@@ -228,7 +228,7 @@ remove_group(MouseWatcherGroup *group) {
 
   // See if the group is in the set/vector
   PT(MouseWatcherGroup) pt = group;
-  Groups::iterator gi = 
+  Groups::iterator gi =
     find(_groups.begin(), _groups.end(), pt);
   if (gi != _groups.end()) {
     // Found it, now erase it
@@ -270,7 +270,7 @@ replace_group(MouseWatcherGroup *old_group, MouseWatcherGroup *new_group) {
 #ifndef NDEBUG
   if (!_show_regions_render2d.is_empty()) {
     old_group->do_hide_regions();
-    new_group->do_show_regions(_show_regions_render2d, _show_regions_bin_name, 
+    new_group->do_show_regions(_show_regions_render2d, _show_regions_bin_name,
                                _show_regions_draw_order);
   }
 #endif  // NDEBUG
@@ -327,7 +327,7 @@ replace_group(MouseWatcherGroup *old_group, MouseWatcherGroup *new_group) {
 
   // Add the new group, if it's not already there.
   PT(MouseWatcherGroup) pt = new_group;
-  Groups::iterator gi = 
+  Groups::iterator gi =
     find(_groups.begin(), _groups.end(), pt);
   if (gi == _groups.end()) {
     _groups.push_back(new_group);
@@ -338,7 +338,7 @@ replace_group(MouseWatcherGroup *old_group, MouseWatcherGroup *new_group) {
     new_group->do_update_regions();
   }
 #endif  // NDEBUG
-    
+
   // Remove the old group, if it is already there.
   pt = old_group;
   gi = find(_groups.begin(), _groups.end(), pt);
@@ -476,17 +476,17 @@ update_trail_node() {
   if (_trail_log->get_num_events() < 2) {
     return;
   }
-  
+
   PT(GeomVertexData) data = new GeomVertexData
     ("mouseTrailSegs", GeomVertexFormat::get_v3(), Geom::UH_static);
-  
+
   GeomVertexWriter vertex(data, InternalName::get_vertex());
-    
+
   PT(GeomLinestrips) lines = new GeomLinestrips(Geom::UH_static);
-  
+
   double xscale = 2.0 / _pixel_size->get_value().get_x();
   double yscale = 2.0 / _pixel_size->get_value().get_y();
-  
+
   for (int i=0; i<(int)_trail_log->get_num_events(); i++) {
     double x = (_trail_log->get_xpos(i) * xscale) - 1.0;
     double y = (_trail_log->get_ypos(i) * yscale) - 1.0;
@@ -565,7 +565,7 @@ void MouseWatcher::
 write(ostream &out, int indent_level) const {
   indent(out, indent_level)
     << "MouseWatcher " << get_name() << ":\n";
-  MouseWatcherGroup::write(out, indent_level + 2);
+  MouseWatcherBase::write(out, indent_level + 2);
 
   LightMutexHolder holder(_lock);
   if (!_groups.empty()) {
@@ -620,11 +620,11 @@ get_over_regions(MouseWatcher::Regions &regions, const LPoint2 &pos) const {
     for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) {
       MouseWatcherRegion *region = (*ri);
       const LVecBase4 &frame = region->get_frame();
-      
+
       if (region->get_active() &&
           mx >= frame[0] && mx <= frame[1] &&
           my >= frame[2] && my <= frame[3]) {
-        
+
         regions.push_back(region);
       }
     }
@@ -750,9 +750,9 @@ set_current_regions(MouseWatcher::Regions &regions) {
     // Determine which is the "preferred region", if any.  This is the
     // topmost region that the mouse cursor is over, and the one that
     // we are considered "entered" into.
-    MouseWatcherRegion *new_preferred_region = 
+    MouseWatcherRegion *new_preferred_region =
       get_preferred_region(_current_regions);
-    
+
     if (_button_down && new_preferred_region != _preferred_button_down_region) {
       // If the button's being held down, we're only allowed to select
       // the preferred button down region.
@@ -786,9 +786,9 @@ clear_current_regions() {
     MouseWatcherParameter param;
     param.set_modifier_buttons(_mods);
     param.set_mouse(_mouse);
-    
+
     Regions::const_iterator old_ri = _current_regions.begin();
-    
+
     while (old_ri != _current_regions.end()) {
       // Here's a region we don't have any more.
       MouseWatcherRegion *old_region = (*old_ri);
@@ -796,7 +796,7 @@ clear_current_regions() {
       throw_event_pattern(_leave_pattern, old_region, ButtonHandle::none());
       ++old_ri;
     }
-    
+
     _current_regions.clear();
 
     if (_preferred_region != (MouseWatcherRegion *)NULL) {
@@ -815,9 +815,9 @@ clear_current_regions() {
 //               assumes the lock is already held.
 ////////////////////////////////////////////////////////////////////
 void MouseWatcher::
-do_show_regions(const NodePath &render2d, const string &bin_name, 
+do_show_regions(const NodePath &render2d, const string &bin_name,
                 int draw_order) {
-  MouseWatcherGroup::do_show_regions(render2d, bin_name, draw_order);
+  MouseWatcherBase::do_show_regions(render2d, bin_name, draw_order);
   _show_regions_render2d = render2d;
   _show_regions_bin_name = bin_name;
   _show_regions_draw_order = draw_order;
@@ -839,7 +839,7 @@ do_show_regions(const NodePath &render2d, const string &bin_name,
 ////////////////////////////////////////////////////////////////////
 void MouseWatcher::
 do_hide_regions() {
-  MouseWatcherGroup::do_hide_regions();
+  MouseWatcherBase::do_hide_regions();
   _show_regions_render2d = NodePath();
   _show_regions_bin_name = string();
   _show_regions_draw_order = 0;
@@ -1027,7 +1027,7 @@ press(ButtonHandle button, bool keyrepeat) {
 
   if (MouseButton::is_mouse_button(button)) {
     // Mouse buttons are inextricably linked to the mouse position.
-    
+
     if (!_button_down) {
       _preferred_button_down_region = _preferred_region;
     }
@@ -1043,7 +1043,7 @@ press(ButtonHandle button, bool keyrepeat) {
                             _preferred_button_down_region, button);
       }
     }
-    
+
   } else {
     // It's a keyboard button; therefore, send the event to every
     // region that wants keyboard buttons, regardless of the mouse
@@ -1084,7 +1084,7 @@ release(ButtonHandle button) {
   if (MouseButton::is_mouse_button(button)) {
     // Button up.  Send the up event associated with the region(s) we
     // were over when the button went down.
-    
+
     // There is some danger of losing button-up events here.  If
     // more than one button goes down together, we won't detect
     // both of the button-up events properly.
@@ -1097,7 +1097,7 @@ release(ButtonHandle button) {
 
     _button_down = false;
     _preferred_button_down_region = (MouseWatcherRegion *)NULL;
-    
+
   } else {
     // It's a keyboard button; therefore, send the event to every
     // region that wants keyboard buttons, regardless of the mouse
@@ -1105,7 +1105,7 @@ release(ButtonHandle button) {
     if (_preferred_region != (MouseWatcherRegion *)NULL) {
       _preferred_region->release(param);
     }
-    
+
     param.set_outside(true);
     global_keyboard_release(param);
   }
@@ -1166,7 +1166,7 @@ keystroke(int keycode) {
 //               highlighted in the IME.
 ////////////////////////////////////////////////////////////////////
 void MouseWatcher::
-candidate(const wstring &candidate_string, size_t highlight_start, 
+candidate(const wstring &candidate_string, size_t highlight_start,
           size_t highlight_end, size_t cursor_pos) {
   nassertv(_lock.debug_is_locked());
 
@@ -1275,7 +1275,7 @@ global_keyboard_release(const MouseWatcherParameter &param) {
 ////////////////////////////////////////////////////////////////////
 //     Function: MouseWatcher::enter_region
 //       Access: Protected
-//  Description: Called internally to indicate the mouse pointer is 
+//  Description: Called internally to indicate the mouse pointer is
 //               favoring the indicated region.
 ////////////////////////////////////////////////////////////////////
 void MouseWatcher::
@@ -1326,7 +1326,7 @@ set_no_mouse() {
       _geometry->set_overall_hidden(true);
     }
   }
-  
+
   _has_mouse = false;
   clear_current_regions();
 }
@@ -1350,11 +1350,11 @@ set_mouse(const LVecBase2 &xy, const LVecBase2 &pixel_xy) {
       _geometry->set_overall_hidden(false);
     }
   }
-  
+
   _has_mouse = true;
   _mouse = xy;
   _mouse_pixel = pixel_xy;
-    
+
   Regions regions;
   get_over_regions(regions, _mouse);
   set_current_regions(regions);
@@ -1366,7 +1366,7 @@ set_mouse(const LVecBase2 &xy, const LVecBase2 &pixel_xy) {
 //  Description: If we send any keyboard events to a region that has
 //               the SF_other_button suppress flag set, that means we
 //               should not send the keyboard event along the data
-//               graph.  
+//               graph.
 //
 //               This method is called as each keyboard event is sent
 //               to a region; it should update the internal
@@ -1436,7 +1436,7 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
         // The mouse is outside the display region, even though it's
         // within the window.  This is considered not having a mouse.
         set_no_mouse();
-        
+
         // This also means we should suppress mouse button events below us.
         _internal_suppress |= MouseWatcherRegion::SF_mouse_button;
       }
@@ -1469,7 +1469,7 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
   if (_num_trail_recent > _trail_log->get_num_events()) {
     _num_trail_recent = _trail_log->get_num_events();
   }
-  
+
   // If the mouse is over a particular region, or still considered
   // owned by a region because of a recent button-down event, that
   // region determines whether we suppress events below us.
@@ -1561,7 +1561,7 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
   if (_has_inactivity_timeout) {
     if (activity) {
       note_activity();
-      
+
     } else {
       double now = ClockObject::get_global_clock()->get_frame_time();
       double elapsed = now - _last_activity;
@@ -1575,10 +1575,10 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
 
         case IS_inactive:
           break;
-          
+
         case IS_active_to_inactive:
           break;
-          
+
         case IS_inactive_to_active:
           _inactivity_state = IS_inactive;
           break;
@@ -1591,7 +1591,7 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
   case IS_active:
   case IS_inactive:
     break;
-    
+
   case IS_active_to_inactive:
     // "Release" all of the currently-held buttons.
     if (tform_cat.is_debug()) {
@@ -1610,7 +1610,7 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
     _inactivity_state = IS_inactive;
     throw_event(_inactivity_timeout_event);
     break;
-    
+
   case IS_inactive_to_active:
     // "Press" all of the buttons we "released" before.
     {
@@ -1643,20 +1643,20 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
   for (int i = 0; i < num_events; i++) {
     const ButtonEvent &be = new_button_events.get_event(i);
     bool suppress = true;
-    
-    if (be._type != ButtonEvent::T_keystroke && 
+
+    if (be._type != ButtonEvent::T_keystroke &&
         MouseButton::is_mouse_button(be._button)) {
       suppress = ((suppress_buttons & MouseWatcherRegion::SF_mouse_button) != 0);
     } else {
       suppress = ((suppress_buttons & MouseWatcherRegion::SF_other_button) != 0);
     }
-    
+
     if (!suppress || be._type == ButtonEvent::T_up) {
       // Don't suppress this button event; pass it through.
       _button_events->add_event(be);
     }
   }
-  
+
   if (_button_events->get_num_events() != 0) {
     output.set_data(_button_events_output, EventParameter(_button_events));
   }
@@ -1673,7 +1673,7 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
 //               leaves f and p unchanged, and returns false.
 ////////////////////////////////////////////////////////////////////
 bool MouseWatcher::
-constrain_display_region(DisplayRegion *display_region, 
+constrain_display_region(DisplayRegion *display_region,
                          LVecBase2 &f, LVecBase2 &p,
                          Thread *current_thread) {
   if (!_button_down) {
@@ -1700,7 +1700,7 @@ constrain_display_region(DisplayRegion *display_region,
   DisplayRegionPipelineReader dr_reader(display_region, current_thread);
   PN_stdfloat left, right, bottom, top;
   dr_reader.get_dimensions(left, right, bottom, top);
-  
+
   // Need to translate this into DisplayRegion [0, 1] space
   PN_stdfloat x = (f[0] + 1.0f) / 2.0f;
   PN_stdfloat y = (f[1] + 1.0f) / 2.0f;
@@ -1715,18 +1715,18 @@ constrain_display_region(DisplayRegion *display_region,
   if (_button_down) {
     _button_down_display_region = display_region;
   }
-  
+
   // Scale in DR space
   PN_stdfloat xp = (x - left) / (right - left);
   // Translate back into [-1, 1] space
   PN_stdfloat xpp = (xp * 2.0f) - 1.0f;
-  
+
   PN_stdfloat yp = (y - bottom) / (top - bottom);
   PN_stdfloat ypp = (yp * 2.0f) - 1.0f;
-  
+
   int xo, yo, w, h;
   dr_reader.get_region_pixels_i(xo, yo, w, h);
-  
+
   f.set(xpp, ypp);
   p.set(p[0] - xo, p[1] - yo);
   return true;

+ 11 - 11
panda/src/tform/mouseWatcher.h

@@ -61,11 +61,11 @@ class DisplayRegion;
 //               it took to get there.  This information is mainly useful
 //               for gesture-recognition code.  To use trail logging,
 //               you need to enable the generation of pointer events
-//               in the GraphicsWindowInputDevice and set the trail 
+//               in the GraphicsWindowInputDevice and set the trail
 //               log duration in the MouseWatcher. Otherwise, the
 //               trail log will be empty.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA_TFORM MouseWatcher : public DataNode, public MouseWatcherGroup {
+class EXPCL_PANDA_TFORM MouseWatcher : public DataNode, public MouseWatcherBase {
 PUBLISHED:
   MouseWatcher(const string &name = "");
   ~MouseWatcher();
@@ -143,14 +143,14 @@ PUBLISHED:
 
   INLINE void set_inactivity_timeout_event(const string &event);
   INLINE const string &get_inactivity_timeout_event() const;
-  
+
   INLINE CPT(PointerEventList) get_trail_log() const;
   INLINE int   num_trail_recent() const;
   void         set_trail_log_duration(double duration);
   PT(GeomNode) get_trail_node();
   void         clear_trail_node();
   INLINE void  clear_trail_log();
-  
+
   void note_activity();
 
 public:
@@ -179,7 +179,7 @@ protected:
                                  MouseWatcherRegion *region);
   static bool has_region_in(const Regions &regions,
                             MouseWatcherRegion *region);
-    
+
   void throw_event_pattern(const string &pattern,
                            const MouseWatcherRegion *region,
                            const ButtonHandle &button);
@@ -188,9 +188,9 @@ protected:
   void press(ButtonHandle button, bool keyrepeat);
   void release(ButtonHandle button);
   void keystroke(int keycode);
-  void candidate(const wstring &candidate, size_t highlight_start, 
+  void candidate(const wstring &candidate, size_t highlight_start,
                  size_t highlight_end, size_t cursor_pos);
-                 
+
   void global_keyboard_press(const MouseWatcherParameter &param);
   void global_keyboard_release(const MouseWatcherParameter &param);
 
@@ -207,10 +207,10 @@ private:
   void discard_excess_trail_log();
   void update_trail_node();
 
-  bool constrain_display_region(DisplayRegion *display_region, 
+  bool constrain_display_region(DisplayRegion *display_region,
                                 LVecBase2 &f, LVecBase2 &p,
                                 Thread *current_thread);
-  
+
 private:
   // This wants to be a set, but because you cannot export sets across
   // dlls in windows, we will make it a vector instead
@@ -230,7 +230,7 @@ private:
   int _num_trail_recent;
   double _trail_log_duration;
   PT(GeomNode) _trail_node;
-  
+
   Regions _current_regions;
   PT(MouseWatcherRegion) _preferred_region;
   PT(MouseWatcherRegion) _preferred_button_down_region;
@@ -248,7 +248,7 @@ private:
   string _without_pattern;
 
   PT(PandaNode) _geometry;
-  
+
   EventHandler *_eh;
   ModifierButtons _mods;
   DisplayRegion *_display_region;

+ 443 - 0
panda/src/tform/mouseWatcherBase.cxx

@@ -0,0 +1,443 @@
+// Filename: mouseWatcherBase.cxx
+// Created by:  rdb (13Jan14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "mouseWatcherBase.h"
+#include "lineSegs.h"
+#include "indent.h"
+#include "lightMutexHolder.h"
+
+TypeHandle MouseWatcherBase::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+MouseWatcherBase::
+MouseWatcherBase() :
+  _lock("MouseWatcherBase")
+{
+  _sorted = true;
+#ifndef NDEBUG
+  _show_regions = false;
+  _color.set(0.4, 0.6f, 1.0f, 1.0f);
+#endif  // NDEBUG
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+MouseWatcherBase::
+~MouseWatcherBase() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::add_region
+//       Access: Published
+//  Description: Adds the indicated region to the set of regions in
+//               the group.  It is an error to add the same region to
+//               the set more than once.
+////////////////////////////////////////////////////////////////////
+void MouseWatcherBase::
+add_region(MouseWatcherRegion *region) {
+  PT(MouseWatcherRegion) pt = region;
+
+  LightMutexHolder holder(_lock);
+
+  // We will only bother to check for duplicates in the region list if
+  // we are building a development Panda.  The overhead for doing this
+  // may be too high if we have many regions.
+#ifdef _DEBUG
+  // See if the region is in the set/vector already
+  Regions::const_iterator ri =
+    find(_regions.begin(), _regions.end(), pt);
+  nassertv(ri == _regions.end());
+#endif  // _DEBUG
+
+#ifndef NDEBUG
+  // Also add it to the vizzes if we have them.
+  if (_show_regions) {
+    nassertv(_vizzes.size() == _regions.size());
+    _vizzes.push_back(make_viz_region(pt));
+  }
+#endif  // NDEBUG
+
+  _regions.push_back(pt);
+  _sorted = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::has_region
+//       Access: Published
+//  Description: Returns true if the indicated region has already been
+//               added to the MouseWatcherBase, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool MouseWatcherBase::
+has_region(MouseWatcherRegion *region) const {
+  LightMutexHolder holder(_lock);
+
+  PT(MouseWatcherRegion) ptr = region;
+
+  if (_sorted) {
+    // If the vector is already sorted, we can do this the quick way.
+    Regions::const_iterator ri = lower_bound(_regions.begin(), _regions.end(), ptr);
+    return (ri != _regions.end() && (*ri) == ptr);
+  }
+
+  // If the vector isn't sorted, do a linear scan.
+  Regions::const_iterator ri = find(_regions.begin(), _regions.end(), ptr);
+  return (ri != _regions.end());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::remove_region
+//       Access: Published
+//  Description: Removes the indicated region from the group.
+//               Returns true if it was successfully removed, or false
+//               if it wasn't there in the first place.
+////////////////////////////////////////////////////////////////////
+bool MouseWatcherBase::
+remove_region(MouseWatcherRegion *region) {
+  LightMutexHolder holder(_lock);
+  return do_remove_region(region);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::find_region
+//       Access: Published
+//  Description: Returns a pointer to the first region found with the
+//               indicated name.  If multiple regions share the same
+//               name, the one that is returned is indeterminate.
+////////////////////////////////////////////////////////////////////
+MouseWatcherRegion *MouseWatcherBase::
+find_region(const string &name) const {
+  LightMutexHolder holder(_lock);
+
+  Regions::const_iterator ri;
+  for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
+    MouseWatcherRegion *region = (*ri);
+    if (region->get_name() == name) {
+      return region;
+    }
+  }
+
+  return (MouseWatcherRegion *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::clear_regions
+//       Access: Published
+//  Description: Removes all the regions from the group.
+////////////////////////////////////////////////////////////////////
+void MouseWatcherBase::
+clear_regions() {
+  LightMutexHolder holder(_lock);
+
+  _regions.clear();
+  _sorted = true;
+
+#ifndef NDEBUG
+  if (_show_regions) {
+    _show_regions_root.node()->remove_all_children();
+    _vizzes.clear();
+  }
+#endif  // NDEBUG
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::sort_regions
+//       Access: Published
+//  Description: Sorts all the regions in this group into pointer
+//               order.
+////////////////////////////////////////////////////////////////////
+void MouseWatcherBase::
+sort_regions() {
+  LightMutexHolder holder(_lock);
+  do_sort_regions();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::is_sorted
+//       Access: Published
+//  Description: Returns true if the group has already been sorted,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+bool MouseWatcherBase::
+is_sorted() const {
+  LightMutexHolder holder(_lock);
+
+  return _sorted;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::get_num_regions
+//       Access: Published
+//  Description: Returns the number of regions in the group.
+////////////////////////////////////////////////////////////////////
+int MouseWatcherBase::
+get_num_regions() const {
+  LightMutexHolder holder(_lock);
+
+  return _regions.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::get_region
+//       Access: Published
+//  Description: Returns the nth region of the group; returns NULL if
+//               there is no nth region.  Note that this is not
+//               thread-safe; another thread might have removed the
+//               nth region before you called this method.
+////////////////////////////////////////////////////////////////////
+MouseWatcherRegion *MouseWatcherBase::
+get_region(int n) const {
+  LightMutexHolder holder(_lock);
+  if (n >= 0 && n < (int)_regions.size()) {
+    return _regions[n];
+  }
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::output
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+void MouseWatcherBase::
+output(ostream &out) const {
+  out << "MouseWatcherBase (" << _regions.size() << " regions)";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::write
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+void MouseWatcherBase::
+write(ostream &out, int indent_level) const {
+  LightMutexHolder holder(_lock);
+
+  Regions::const_iterator ri;
+  for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
+    MouseWatcherRegion *region = (*ri);
+    region->write(out, indent_level);
+  }
+}
+
+#ifndef NDEBUG
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::show_regions
+//       Access: Published
+//  Description: Enables the visualization of all of the regions
+//               handled by this MouseWatcherBase.  The supplied
+//               NodePath should be the root of the 2-d scene graph
+//               for the window.
+////////////////////////////////////////////////////////////////////
+void MouseWatcherBase::
+show_regions(const NodePath &render2d, const string &bin_name, int draw_order) {
+  LightMutexHolder holder(_lock);
+  do_show_regions(render2d, bin_name, draw_order);
+}
+#endif  // NDEBUG
+
+#ifndef NDEBUG
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::set_color
+//       Access: Published
+//  Description: Specifies the color used to draw the region
+//               rectangles for the regions visualized by
+//               show_regions().
+////////////////////////////////////////////////////////////////////
+void MouseWatcherBase::
+set_color(const LColor &color) {
+  LightMutexHolder holder(_lock);
+
+  _color = color;
+  do_update_regions();
+}
+#endif  // NDEBUG
+
+#ifndef NDEBUG
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::hide_regions
+//       Access: Published
+//  Description: Stops the visualization created by a previous call to
+//               show_regions().
+////////////////////////////////////////////////////////////////////
+void MouseWatcherBase::
+hide_regions() {
+  LightMutexHolder holder(_lock);
+  do_hide_regions();
+}
+#endif  // NDEBUG
+
+#ifndef NDEBUG
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::update_regions
+//       Access: Published
+//  Description: Refreshes the visualization created by
+//               show_regions().
+////////////////////////////////////////////////////////////////////
+void MouseWatcherBase::
+update_regions() {
+  LightMutexHolder holder(_lock);
+  do_update_regions();
+}
+#endif  // NDEBUG
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::do_sort_regions
+//       Access: Protected
+//  Description: Sorts all the regions in this group into pointer
+//               order.  Assumes the lock is already held.
+////////////////////////////////////////////////////////////////////
+void MouseWatcherBase::
+do_sort_regions() {
+  if (!_sorted) {
+    sort(_regions.begin(), _regions.end());
+    _sorted = true;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::do_remove_region
+//       Access: Protected
+//  Description: The internal implementation of remove_region();
+//               assumes the lock is already held.
+////////////////////////////////////////////////////////////////////
+bool MouseWatcherBase::
+do_remove_region(MouseWatcherRegion *region) {
+  // See if the region is in the vector.
+  PT(MouseWatcherRegion) ptr = region;
+  Regions::iterator ri;
+
+  if (_sorted) {
+    // Faster, binary search
+    ri = lower_bound(_regions.begin(), _regions.end(), ptr);
+  } else {
+    // Unsorted, so use slower linear scan
+    ri = find(_regions.begin(), _regions.end(), ptr);
+  }
+
+  if (ri != _regions.end() && (*ri) == ptr) {
+    // Found it, now erase it
+#ifndef NDEBUG
+    // Also remove it from the vizzes.
+    if (_show_regions) {
+      nassertr(_vizzes.size() == _regions.size(), false);
+      size_t index = ri - _regions.begin();
+      Vizzes::iterator vi = _vizzes.begin() + index;
+      _show_regions_root.node()->remove_child(*vi);
+      _vizzes.erase(vi);
+    }
+#endif  // NDEBUG
+
+    _regions.erase(ri);
+    return true;
+  }
+
+  // Did not find the region to erase
+  return false;
+}
+
+#ifndef NDEBUG
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::do_show_regions
+//       Access: Protected, Virtual
+//  Description: The protected implementation of show_regions().  This
+//               assumes the lock is already held.
+////////////////////////////////////////////////////////////////////
+void MouseWatcherBase::
+do_show_regions(const NodePath &render2d, const string &bin_name,
+                int draw_order) {
+  do_hide_regions();
+  _show_regions = true;
+  _show_regions_root = render2d.attach_new_node("show_regions");
+  _show_regions_root.set_bin(bin_name, draw_order);
+  do_update_regions();
+}
+#endif  // NDEBUG
+
+#ifndef NDEBUG
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::do_hide_regions
+//       Access: Protected, Virtual
+//  Description: The protected implementation of hide_regions().  This
+//               assumes the lock is already held.
+////////////////////////////////////////////////////////////////////
+void MouseWatcherBase::
+do_hide_regions() {
+  _show_regions_root.remove_node();
+  _show_regions = false;
+  _vizzes.clear();
+}
+#endif  // NDEBUG
+
+#ifndef NDEBUG
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::do_update_regions
+//       Access: Protected
+//  Description: Internally regenerates the show_regions()
+//               visualization.  Assumes the lock is already held.
+////////////////////////////////////////////////////////////////////
+void MouseWatcherBase::
+do_update_regions() {
+  nassertv(_lock.debug_is_locked());
+
+  if (_show_regions) {
+    _show_regions_root.node()->remove_all_children();
+    _vizzes.clear();
+    _vizzes.reserve(_regions.size());
+
+    Regions::const_iterator ri;
+    for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
+      _vizzes.push_back(make_viz_region(*ri));
+    }
+  }
+}
+#endif  // NDEBUG
+
+
+#ifndef NDEBUG
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherBase::make_viz_region
+//       Access: Private
+//  Description: Creates a node to represent the indicated region, and
+//               attaches it to the _show_regions_root.  Does not add
+//               it to _vizzes.  Assumes the lock is already held.
+////////////////////////////////////////////////////////////////////
+PandaNode *MouseWatcherBase::
+make_viz_region(MouseWatcherRegion *region) {
+  nassertr(_lock.debug_is_locked(), NULL);
+
+  LineSegs ls("show_regions");
+  ls.set_color(_color);
+
+  const LVecBase4 &f = region->get_frame();
+
+  ls.move_to(LVector3::rfu(f[0], 0.0f, f[2]));
+  ls.draw_to(LVector3::rfu(f[1], 0.0f, f[2]));
+  ls.draw_to(LVector3::rfu(f[1], 0.0f, f[3]));
+  ls.draw_to(LVector3::rfu(f[0], 0.0f, f[3]));
+  ls.draw_to(LVector3::rfu(f[0], 0.0f, f[2]));
+
+  PT(PandaNode) node = ls.create();
+  _show_regions_root.attach_new_node(node);
+
+  return node;
+}
+#endif  // NDEBUG

+ 113 - 0
panda/src/tform/mouseWatcherBase.h

@@ -0,0 +1,113 @@
+// Filename: mouseWatcherBase.h
+// Created by:  rdb (13Jan14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef MOUSEWATCHERBASE_H
+#define MOUSEWATCHERBASE_H
+
+#include "pandabase.h"
+#include "mouseWatcherRegion.h"
+
+#include "pointerTo.h"
+#include "pvector.h"
+#include "nodePath.h"
+#include "lightMutex.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : MouseWatcherBase
+// Description : This represents a collection of MouseWatcherRegions
+//               that may be managed as a group.  This is the base
+//               class for both MouseWatcherGroup and MouseWatcher,
+//               and exists so that we don't have to make MouseWatcher
+//               inherit from ReferenceCount more than once.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_TFORM MouseWatcherBase {
+public:
+  MouseWatcherBase();
+  virtual ~MouseWatcherBase();
+
+PUBLISHED:
+  void add_region(MouseWatcherRegion *region);
+  bool has_region(MouseWatcherRegion *region) const;
+  bool remove_region(MouseWatcherRegion *region);
+  MouseWatcherRegion *find_region(const string &name) const;
+  void clear_regions();
+
+  void sort_regions();
+  bool is_sorted() const;
+
+  int get_num_regions() const;
+  MouseWatcherRegion *get_region(int n) const;
+  MAKE_SEQ(get_regions, get_num_regions, get_region);
+
+  void output(ostream &out) const;
+  void write(ostream &out, int indent_level = 0) const;
+
+#ifndef NDEBUG
+  void show_regions(const NodePath &render2d,
+                    const string &bin_name, int draw_order);
+  void set_color(const LColor &color);
+  void hide_regions();
+
+  void update_regions();
+#endif  // NDEBUG
+
+protected:
+  void do_sort_regions();
+  bool do_remove_region(MouseWatcherRegion *region);
+
+#ifndef NDEBUG
+  virtual void do_show_regions(const NodePath &render2d,
+                               const string &bin_name, int draw_order);
+  virtual void do_hide_regions();
+  void do_update_regions();
+#endif  // NDEBUG
+
+protected:
+  typedef pvector< PT(MouseWatcherRegion) > Regions;
+  Regions _regions;
+  bool _sorted;
+
+  // This mutex protects the above list of regions, as well as the
+  // below list of vizzes.  It is also referenced directly by
+  // MouseWatcher, a derived class.
+  LightMutex _lock;
+
+private:
+#ifndef NDEBUG
+  PandaNode *make_viz_region(MouseWatcherRegion *region);
+
+  typedef pvector< PT(PandaNode) > Vizzes;
+  Vizzes _vizzes;
+
+  bool _show_regions;
+  NodePath _show_regions_root;
+  LColor _color;
+#endif  // NDEBUG
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    register_type(_type_handle, "MouseWatcherBase");
+  }
+
+private:
+  static TypeHandle _type_handle;
+
+  friend class MouseWatcher;
+  friend class BlobWatcher;
+};
+
+#endif

+ 0 - 427
panda/src/tform/mouseWatcherGroup.cxx

@@ -13,432 +13,5 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "mouseWatcherGroup.h"
-#include "lineSegs.h"
-#include "indent.h"
-#include "lightMutexHolder.h"
 
 TypeHandle MouseWatcherGroup::_type_handle;
-
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::Constructor
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-MouseWatcherGroup::
-MouseWatcherGroup() :
-  _lock("MouseWatcherGroup")
-{
-  _sorted = true;
-#ifndef NDEBUG
-  _show_regions = false;
-  _color.set(0.4, 0.6f, 1.0f, 1.0f);
-#endif  // NDEBUG
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::Destructor
-//       Access: Public, Virtual
-//  Description: 
-////////////////////////////////////////////////////////////////////
-MouseWatcherGroup::
-~MouseWatcherGroup() {
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::add_region
-//       Access: Published
-//  Description: Adds the indicated region to the set of regions in
-//               the group.  It is an error to add the same region to
-//               the set more than once.
-////////////////////////////////////////////////////////////////////
-void MouseWatcherGroup::
-add_region(MouseWatcherRegion *region) {
-  PT(MouseWatcherRegion) pt = region;
-
-  LightMutexHolder holder(_lock);
-
-  // We will only bother to check for duplicates in the region list if
-  // we are building a development Panda.  The overhead for doing this
-  // may be too high if we have many regions.
-#ifdef _DEBUG
-  // See if the region is in the set/vector already
-  Regions::const_iterator ri = 
-    find(_regions.begin(), _regions.end(), pt);
-  nassertv(ri == _regions.end());
-#endif  // _DEBUG
-
-#ifndef NDEBUG
-  // Also add it to the vizzes if we have them.
-  if (_show_regions) {
-    nassertv(_vizzes.size() == _regions.size());
-    _vizzes.push_back(make_viz_region(pt));
-  }
-#endif  // NDEBUG
-
-  _regions.push_back(pt);
-  _sorted = false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::has_region
-//       Access: Published
-//  Description: Returns true if the indicated region has already been
-//               added to the MouseWatcherGroup, false otherwise.
-////////////////////////////////////////////////////////////////////
-bool MouseWatcherGroup::
-has_region(MouseWatcherRegion *region) const {
-  LightMutexHolder holder(_lock);
-
-  PT(MouseWatcherRegion) ptr = region;
-
-  if (_sorted) {
-    // If the vector is already sorted, we can do this the quick way.
-    Regions::const_iterator ri = lower_bound(_regions.begin(), _regions.end(), ptr);
-    return (ri != _regions.end() && (*ri) == ptr);
-  }
-
-  // If the vector isn't sorted, do a linear scan.
-  Regions::const_iterator ri = find(_regions.begin(), _regions.end(), ptr);
-  return (ri != _regions.end());
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::remove_region
-//       Access: Published
-//  Description: Removes the indicated region from the group.
-//               Returns true if it was successfully removed, or false
-//               if it wasn't there in the first place.
-////////////////////////////////////////////////////////////////////
-bool MouseWatcherGroup::
-remove_region(MouseWatcherRegion *region) {
-  LightMutexHolder holder(_lock);
-  return do_remove_region(region);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::find_region
-//       Access: Published
-//  Description: Returns a pointer to the first region found with the
-//               indicated name.  If multiple regions share the same
-//               name, the one that is returned is indeterminate.
-////////////////////////////////////////////////////////////////////
-MouseWatcherRegion *MouseWatcherGroup::
-find_region(const string &name) const {
-  LightMutexHolder holder(_lock);
-
-  Regions::const_iterator ri;
-  for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
-    MouseWatcherRegion *region = (*ri);
-    if (region->get_name() == name) {
-      return region;
-    }
-  }
-
-  return (MouseWatcherRegion *)NULL;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::clear_regions
-//       Access: Published
-//  Description: Removes all the regions from the group.
-////////////////////////////////////////////////////////////////////
-void MouseWatcherGroup::
-clear_regions() {
-  LightMutexHolder holder(_lock);
-
-  _regions.clear();
-  _sorted = true;
-
-#ifndef NDEBUG
-  if (_show_regions) {
-    _show_regions_root.node()->remove_all_children();
-    _vizzes.clear();
-  }
-#endif  // NDEBUG
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::sort_regions
-//       Access: Published
-//  Description: Sorts all the regions in this group into pointer
-//               order.
-////////////////////////////////////////////////////////////////////
-void MouseWatcherGroup::
-sort_regions() {
-  LightMutexHolder holder(_lock);
-  do_sort_regions();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::is_sorted
-//       Access: Published
-//  Description: Returns true if the group has already been sorted,
-//               false otherwise.
-////////////////////////////////////////////////////////////////////
-bool MouseWatcherGroup::
-is_sorted() const {
-  LightMutexHolder holder(_lock);
-
-  return _sorted;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::get_num_regions
-//       Access: Published
-//  Description: Returns the number of regions in the group.
-////////////////////////////////////////////////////////////////////
-int MouseWatcherGroup::
-get_num_regions() const {
-  LightMutexHolder holder(_lock);
-
-  return _regions.size();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::get_region
-//       Access: Published
-//  Description: Returns the nth region of the group; returns NULL if
-//               there is no nth region.  Note that this is not
-//               thread-safe; another thread might have removed the
-//               nth region before you called this method.
-////////////////////////////////////////////////////////////////////
-MouseWatcherRegion *MouseWatcherGroup::
-get_region(int n) const {
-  LightMutexHolder holder(_lock);
-  if (n >= 0 && n < (int)_regions.size()) {
-    return _regions[n];
-  }
-  return NULL;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::output
-//       Access: Published
-//  Description:
-////////////////////////////////////////////////////////////////////
-void MouseWatcherGroup::
-output(ostream &out) const {
-  out << "MouseWatcherGroup (" << _regions.size() << " regions)";
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::write
-//       Access: Published
-//  Description:
-////////////////////////////////////////////////////////////////////
-void MouseWatcherGroup::
-write(ostream &out, int indent_level) const {
-  LightMutexHolder holder(_lock);
-
-  Regions::const_iterator ri;
-  for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
-    MouseWatcherRegion *region = (*ri);
-    region->write(out, indent_level);
-  }
-}
-
-#ifndef NDEBUG
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::show_regions
-//       Access: Published
-//  Description: Enables the visualization of all of the regions
-//               handled by this MouseWatcherGroup.  The supplied
-//               NodePath should be the root of the 2-d scene graph
-//               for the window.
-////////////////////////////////////////////////////////////////////
-void MouseWatcherGroup::
-show_regions(const NodePath &render2d, const string &bin_name, int draw_order) {
-  LightMutexHolder holder(_lock);
-  do_show_regions(render2d, bin_name, draw_order);
-}
-#endif  // NDEBUG
-
-#ifndef NDEBUG
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::set_color
-//       Access: Published
-//  Description: Specifies the color used to draw the region
-//               rectangles for the regions visualized by
-//               show_regions().
-////////////////////////////////////////////////////////////////////
-void MouseWatcherGroup::
-set_color(const LColor &color) {
-  LightMutexHolder holder(_lock);
-
-  _color = color;
-  do_update_regions();
-}
-#endif  // NDEBUG
-
-#ifndef NDEBUG
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::hide_regions
-//       Access: Published
-//  Description: Stops the visualization created by a previous call to
-//               show_regions().
-////////////////////////////////////////////////////////////////////
-void MouseWatcherGroup::
-hide_regions() {
-  LightMutexHolder holder(_lock);
-  do_hide_regions();
-}
-#endif  // NDEBUG
-
-#ifndef NDEBUG
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::update_regions
-//       Access: Published
-//  Description: Refreshes the visualization created by
-//               show_regions().
-////////////////////////////////////////////////////////////////////
-void MouseWatcherGroup::
-update_regions() {
-  LightMutexHolder holder(_lock);
-  do_update_regions();
-}
-#endif  // NDEBUG
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::do_sort_regions
-//       Access: Protected
-//  Description: Sorts all the regions in this group into pointer
-//               order.  Assumes the lock is already held.
-////////////////////////////////////////////////////////////////////
-void MouseWatcherGroup::
-do_sort_regions() {
-  if (!_sorted) {
-    sort(_regions.begin(), _regions.end());
-    _sorted = true;
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::do_remove_region
-//       Access: Protected
-//  Description: The internal implementation of remove_region();
-//               assumes the lock is already held.
-////////////////////////////////////////////////////////////////////
-bool MouseWatcherGroup::
-do_remove_region(MouseWatcherRegion *region) {
-  // See if the region is in the vector.
-  PT(MouseWatcherRegion) ptr = region;
-  Regions::iterator ri;
-
-  if (_sorted) {
-    // Faster, binary search
-    ri = lower_bound(_regions.begin(), _regions.end(), ptr);
-  } else {
-    // Unsorted, so use slower linear scan
-    ri = find(_regions.begin(), _regions.end(), ptr);
-  }
-
-  if (ri != _regions.end() && (*ri) == ptr) {
-    // Found it, now erase it
-#ifndef NDEBUG
-    // Also remove it from the vizzes.
-    if (_show_regions) {
-      nassertr(_vizzes.size() == _regions.size(), false);
-      size_t index = ri - _regions.begin();
-      Vizzes::iterator vi = _vizzes.begin() + index;
-      _show_regions_root.node()->remove_child(*vi);
-      _vizzes.erase(vi);
-    }
-#endif  // NDEBUG    
-
-    _regions.erase(ri);
-    return true;
-  }
-
-  // Did not find the region to erase
-  return false;
-}
-
-#ifndef NDEBUG
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::do_show_regions
-//       Access: Protected, Virtual
-//  Description: The protected implementation of show_regions().  This
-//               assumes the lock is already held.
-////////////////////////////////////////////////////////////////////
-void MouseWatcherGroup::
-do_show_regions(const NodePath &render2d, const string &bin_name, 
-                int draw_order) {
-  do_hide_regions();
-  _show_regions = true;
-  _show_regions_root = render2d.attach_new_node("show_regions");
-  _show_regions_root.set_bin(bin_name, draw_order);
-  do_update_regions();
-}
-#endif  // NDEBUG
-
-#ifndef NDEBUG
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::do_hide_regions
-//       Access: Protected, Virtual
-//  Description: The protected implementation of hide_regions().  This
-//               assumes the lock is already held.
-////////////////////////////////////////////////////////////////////
-void MouseWatcherGroup::
-do_hide_regions() {
-  _show_regions_root.remove_node();
-  _show_regions = false;
-  _vizzes.clear();
-}
-#endif  // NDEBUG
-
-
-#ifndef NDEBUG
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::do_update_regions
-//       Access: Protected
-//  Description: Internally regenerates the show_regions()
-//               visualization.  Assumes the lock is already held.
-////////////////////////////////////////////////////////////////////
-void MouseWatcherGroup::
-do_update_regions() {
-  nassertv(_lock.debug_is_locked());
-
-  if (_show_regions) {
-    _show_regions_root.node()->remove_all_children();
-    _vizzes.clear();
-    _vizzes.reserve(_regions.size());
-    
-    Regions::const_iterator ri;
-    for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
-      _vizzes.push_back(make_viz_region(*ri));
-    }
-  }
-}
-#endif  // NDEBUG
-
-
-#ifndef NDEBUG
-////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcherGroup::make_viz_region
-//       Access: Private
-//  Description: Creates a node to represent the indicated region, and
-//               attaches it to the _show_regions_root.  Does not add
-//               it to _vizzes.  Assumes the lock is already held.
-////////////////////////////////////////////////////////////////////
-PandaNode *MouseWatcherGroup::
-make_viz_region(MouseWatcherRegion *region) {
-  nassertr(_lock.debug_is_locked(), NULL);
-
-  LineSegs ls("show_regions");
-  ls.set_color(_color);
-
-  const LVecBase4 &f = region->get_frame();
-
-  ls.move_to(LVector3::rfu(f[0], 0.0f, f[2]));
-  ls.draw_to(LVector3::rfu(f[1], 0.0f, f[2]));
-  ls.draw_to(LVector3::rfu(f[1], 0.0f, f[3]));
-  ls.draw_to(LVector3::rfu(f[0], 0.0f, f[3]));
-  ls.draw_to(LVector3::rfu(f[0], 0.0f, f[2]));
-
-  PT(PandaNode) node = ls.create();
-  _show_regions_root.attach_new_node(node);
-
-  return node;
-}
-#endif  // NDEBUG

+ 7 - 72
panda/src/tform/mouseWatcherGroup.h

@@ -16,82 +16,20 @@
 #define MOUSEWATCHERGROUP_H
 
 #include "pandabase.h"
-#include "mouseWatcherRegion.h"
-
-#include "pointerTo.h"
+#include "mouseWatcherBase.h"
 #include "referenceCount.h"
-#include "pvector.h"
-#include "nodePath.h"
-#include "lightMutex.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : MouseWatcherGroup
 // Description : This represents a collection of MouseWatcherRegions
-//               that may be managed as a group.
+//               that may be managed as a group.  The implementation
+//               for this is in MouseWatcherBase; this class exists
+//               so that we can inherit from ReferenceCount.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA_TFORM MouseWatcherGroup : virtual public ReferenceCount {
+class EXPCL_PANDA_TFORM MouseWatcherGroup : public MouseWatcherBase,
+                                            public ReferenceCount {
 public:
-  MouseWatcherGroup();
-  virtual ~MouseWatcherGroup();
-
-PUBLISHED:
-  void add_region(MouseWatcherRegion *region);
-  bool has_region(MouseWatcherRegion *region) const;
-  bool remove_region(MouseWatcherRegion *region);
-  MouseWatcherRegion *find_region(const string &name) const;
-  void clear_regions();
-
-  void sort_regions();
-  bool is_sorted() const;
-
-  int get_num_regions() const;
-  MouseWatcherRegion *get_region(int n) const;
-  MAKE_SEQ(get_regions, get_num_regions, get_region);
-
-  void output(ostream &out) const;
-  void write(ostream &out, int indent_level = 0) const;
-
-#ifndef NDEBUG
-  void show_regions(const NodePath &render2d, 
-                    const string &bin_name, int draw_order);
-  void set_color(const LColor &color);
-  void hide_regions();
-
-  void update_regions();
-#endif  // NDEBUG
-
-protected:
-  void do_sort_regions();
-  bool do_remove_region(MouseWatcherRegion *region);
-
-#ifndef NDEBUG
-  virtual void do_show_regions(const NodePath &render2d, 
-                               const string &bin_name, int draw_order);
-  virtual void do_hide_regions();
-  void do_update_regions();
-#endif  // NDEBUG
-
-protected:
-  typedef pvector< PT(MouseWatcherRegion) > Regions;
-  Regions _regions;
-  bool _sorted;
-
-  // This mutex protects the above list of regions, as well as the
-  // below list of vizzes.  It is also referenced directly by
-  // MouseWatcher, a derived class.
-  LightMutex _lock;
-
-private:
-#ifndef NDEBUG
-  PandaNode *make_viz_region(MouseWatcherRegion *region);
-
-  typedef pvector< PT(PandaNode) > Vizzes;
-  Vizzes _vizzes;
-
-  bool _show_regions;
-  NodePath _show_regions_root;
-  LColor _color;
-#endif  // NDEBUG
+  INLINE MouseWatcherGroup() {};
 
 public:
   static TypeHandle get_class_type() {
@@ -105,9 +43,6 @@ public:
 
 private:
   static TypeHandle _type_handle;
-
-  friend class MouseWatcher;
-  friend class BlobWatcher;
 };
 
 #endif

+ 1 - 0
panda/src/tform/p3tform_composite2.cxx

@@ -1,4 +1,5 @@
 #include "mouseWatcher.cxx"
+#include "mouseWatcherBase.cxx"
 #include "mouseWatcherGroup.cxx"
 #include "mouseWatcherParameter.cxx"
 #include "mouseWatcherRegion.cxx"