Panda3D
 All Classes Functions Variables Enumerations
mouseWatcher.cxx
00001 // Filename: mouseWatcher.cxx
00002 // Created by:  drose (12Mar02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "mouseWatcher.h"
00016 #include "config_tform.h"
00017 #include "dataGraphTraverser.h"
00018 #include "mouseWatcherParameter.h"
00019 #include "mouseAndKeyboard.h"
00020 #include "mouseData.h"
00021 #include "buttonEventList.h"
00022 #include "mouseButton.h"
00023 #include "throw_event.h"
00024 #include "eventParameter.h"
00025 #include "dataNodeTransmit.h"
00026 #include "transformState.h"
00027 #include "displayRegion.h"
00028 #include "stereoDisplayRegion.h"
00029 #include "geomVertexWriter.h"
00030 #include "geomLinestrips.h"
00031 #include "geomPoints.h"
00032 #include "dcast.h"
00033 #include "indent.h"
00034 #include "lightMutexHolder.h"
00035 #include "nearly_zero.h"
00036 
00037 #include <algorithm>
00038 
00039 TypeHandle MouseWatcher::_type_handle;
00040 
00041 ////////////////////////////////////////////////////////////////////
00042 //     Function: MouseWatcher::Constructor
00043 //       Access: Published
00044 //  Description:
00045 ////////////////////////////////////////////////////////////////////
00046 MouseWatcher::
00047 MouseWatcher(const string &name) : 
00048   DataNode(name)
00049 {
00050   _pixel_xy_input = define_input("pixel_xy", EventStoreVec2::get_class_type());
00051   _pixel_size_input = define_input("pixel_size", EventStoreVec2::get_class_type());
00052   _xy_input = define_input("xy", EventStoreVec2::get_class_type());
00053   _button_events_input = define_input("button_events", ButtonEventList::get_class_type());
00054   _pointer_events_input = define_input("pointer_events", PointerEventList::get_class_type());
00055 
00056   _pixel_xy_output = define_output("pixel_xy", EventStoreVec2::get_class_type());
00057   _pixel_size_output = define_output("pixel_size", EventStoreVec2::get_class_type());
00058   _xy_output = define_output("xy", EventStoreVec2::get_class_type());
00059   _button_events_output = define_output("button_events", ButtonEventList::get_class_type());
00060 
00061   _pixel_xy = new EventStoreVec2(LPoint2(0.0f, 0.0f));
00062   _xy = new EventStoreVec2(LPoint2(0.0f, 0.0f));
00063   _pixel_size = new EventStoreVec2(LPoint2(0.0f, 0.0f));
00064   _button_events = new ButtonEventList;
00065 
00066   _has_mouse = false;
00067   _internal_suppress = 0;
00068   _preferred_region = (MouseWatcherRegion *)NULL;
00069   _preferred_button_down_region = (MouseWatcherRegion *)NULL;
00070   _button_down = false;
00071   _eh = (EventHandler *)NULL;
00072   _display_region = (DisplayRegion *)NULL;
00073 
00074   _frame.set(-1.0f, 1.0f, -1.0f, 1.0f);
00075   
00076   _inactivity_timeout = inactivity_timeout;
00077   _has_inactivity_timeout = !IS_NEARLY_ZERO(_inactivity_timeout);
00078   
00079   _num_trail_recent = 0;
00080   _trail_log_duration = 0.0;
00081   _trail_log = new PointerEventList();
00082   
00083   _inactivity_timeout_event = "inactivity_timeout";
00084   _last_activity = 0.0;
00085   _inactivity_state = IS_active;
00086   
00087   // When this flag is true, the mouse pointer is allowed to be
00088   // "entered" into multiple regions simultaneously; when false, it
00089   // will only be "within" multiple regions, but "entered" into the
00090   // topmost of those.
00091   _enter_multiple = false;
00092   
00093   // When this flag is true, moving the pointer into a region is
00094   // enough to click it.  The click is simulated with mouse button
00095   // one.
00096   _implicit_click = false;
00097 }
00098 
00099 ////////////////////////////////////////////////////////////////////
00100 //     Function: MouseWatcher::Destructor
00101 //       Access: Published
00102 //  Description:
00103 ////////////////////////////////////////////////////////////////////
00104 MouseWatcher::
00105 ~MouseWatcher() {
00106 }
00107 
00108 ////////////////////////////////////////////////////////////////////
00109 //     Function: MouseWatcher::remove_region
00110 //       Access: Published
00111 //  Description: Removes the indicated region from the group.
00112 //               Returns true if it was successfully removed, or false
00113 //               if it wasn't there in the first place.
00114 ////////////////////////////////////////////////////////////////////
00115 bool MouseWatcher::
00116 remove_region(MouseWatcherRegion *region) {
00117   LightMutexHolder holder(_lock);
00118 
00119   remove_region_from(_current_regions, region);
00120   if (region == _preferred_region) {
00121     if (_preferred_region != (MouseWatcherRegion *)NULL) {
00122       exit_region(_preferred_region, MouseWatcherParameter());
00123     }
00124     _preferred_region = (MouseWatcherRegion *)NULL;
00125   }
00126   if (region == _preferred_button_down_region) {
00127     _preferred_button_down_region = (MouseWatcherRegion *)NULL;
00128   }
00129 
00130   return MouseWatcherGroup::do_remove_region(region);
00131 }
00132 
00133 ////////////////////////////////////////////////////////////////////
00134 //     Function: MouseWatcher::get_over_region
00135 //       Access: Published
00136 //  Description: Returns the preferred region the mouse is over.  In
00137 //               the case of overlapping regions, the region with the
00138 //               largest sort order is preferred; if two regions have
00139 //               the same sort order, then the smaller region is
00140 //               preferred.
00141 ////////////////////////////////////////////////////////////////////
00142 MouseWatcherRegion *MouseWatcher::
00143 get_over_region(const LPoint2 &pos) const {
00144   LightMutexHolder holder(_lock);
00145 
00146   Regions regions;
00147   get_over_regions(regions, pos);
00148   return get_preferred_region(regions);
00149 }
00150 
00151 ////////////////////////////////////////////////////////////////////
00152 //     Function: MouseWatcher::add_group
00153 //       Access: Published
00154 //  Description: Adds the indicated group of regions to the set of
00155 //               regions the MouseWatcher will monitor each frame.
00156 //
00157 //               Since the MouseWatcher itself inherits from
00158 //               MouseWatcherGroup, this operation is normally not
00159 //               necessary--you can simply add the Regions you care
00160 //               about one at a time.  Adding a complete group is
00161 //               useful when you may want to explicitly remove the
00162 //               regions as a group later.
00163 //
00164 //               Returns true if the group was successfully added, or
00165 //               false if it was already on the list.
00166 ////////////////////////////////////////////////////////////////////
00167 bool MouseWatcher::
00168 add_group(MouseWatcherGroup *group) {
00169   LightMutexHolder holder(_lock);
00170 
00171   // See if the group is in the set/vector already
00172   PT(MouseWatcherGroup) pt = group;
00173   Groups::const_iterator gi = 
00174     find(_groups.begin(), _groups.end(), pt);
00175   if (gi != _groups.end()) {
00176     // Already in the set, return false
00177     return false;
00178   }
00179 
00180 #ifndef NDEBUG
00181   if (!_show_regions_render2d.is_empty()) {
00182     group->show_regions(_show_regions_render2d, _show_regions_bin_name,
00183                         _show_regions_draw_order);
00184   }
00185 #endif  // NDEBUG
00186 
00187   // Not in the set, add it and return true
00188   _groups.push_back(pt);
00189   return true;
00190 }
00191 
00192 ////////////////////////////////////////////////////////////////////
00193 //     Function: MouseWatcher::remove_group
00194 //       Access: Published
00195 //  Description: Removes the indicated group from the set of extra
00196 //               groups associated with the MouseWatcher.  Returns
00197 //               true if successful, or false if the group was already
00198 //               removed or was never added via add_group().
00199 ////////////////////////////////////////////////////////////////////
00200 bool MouseWatcher::
00201 remove_group(MouseWatcherGroup *group) {
00202   LightMutexHolder holder(_lock);
00203   LightMutexHolder holder2(group->_lock);
00204 
00205   group->do_sort_regions();
00206 
00207   Regions only_a, only_b, both;
00208   intersect_regions(only_a, only_b, both,
00209                     _current_regions, group->_regions);
00210   set_current_regions(only_a);
00211 
00212   if (has_region_in(both, _preferred_region)) {
00213     if (_preferred_region != (MouseWatcherRegion *)NULL) {
00214       exit_region(_preferred_region, MouseWatcherParameter());
00215     }
00216     _preferred_region = (MouseWatcherRegion *)NULL;
00217   }
00218   if (has_region_in(both, _preferred_button_down_region)) {
00219     _preferred_button_down_region = (MouseWatcherRegion *)NULL;
00220   }
00221 
00222 #ifndef NDEBUG
00223   if (!_show_regions_render2d.is_empty()) {
00224     group->do_hide_regions();
00225   }
00226 #endif  // NDEBUG
00227 
00228   // See if the group is in the set/vector
00229   PT(MouseWatcherGroup) pt = group;
00230   Groups::iterator gi = 
00231     find(_groups.begin(), _groups.end(), pt);
00232   if (gi != _groups.end()) {
00233     // Found it, now erase it
00234     _groups.erase(gi);
00235     return true;
00236   }
00237 
00238   // Did not find the group to erase
00239   return false;
00240 }
00241 
00242 ////////////////////////////////////////////////////////////////////
00243 //     Function: MouseWatcher::replace_group
00244 //       Access: Published
00245 //  Description: Atomically removes old_group from the MouseWatcher,
00246 //               and replaces it with new_group.  Presumably old_group
00247 //               and new_group might have some regions in common;
00248 //               these are handled properly.
00249 //
00250 //               If old_group is not already present, simply adds
00251 //               new_group and returns false.  Otherwise, removes
00252 //               old_group and adds new_group, and then returns true.
00253 ////////////////////////////////////////////////////////////////////
00254 bool MouseWatcher::
00255 replace_group(MouseWatcherGroup *old_group, MouseWatcherGroup *new_group) {
00256   if (old_group == new_group) {
00257     // Trivial.
00258     return true;
00259   }
00260 
00261   LightMutexHolder holder(_lock);
00262 
00263   LightMutexHolder holder2(old_group->_lock);
00264   LightMutexHolder holder3(new_group->_lock);
00265 
00266   old_group->do_sort_regions();
00267   new_group->do_sort_regions();
00268 
00269 #ifndef NDEBUG
00270   if (!_show_regions_render2d.is_empty()) {
00271     old_group->do_hide_regions();
00272     new_group->do_show_regions(_show_regions_render2d, _show_regions_bin_name, 
00273                                _show_regions_draw_order);
00274   }
00275 #endif  // NDEBUG
00276 
00277   // Figure out the list of regions that change
00278   Regions remove, add, keep;
00279   intersect_regions(remove, add, keep,
00280                     old_group->_regions, new_group->_regions);
00281 
00282   Regions new_current_regions;
00283   bool any_new_current_regions = false;
00284 
00285   // Remove the old regions
00286   if (!remove.empty()) {
00287     Regions only_a, only_b, both;
00288     intersect_regions(only_a, only_b, both,
00289                       _current_regions, remove);
00290     new_current_regions.swap(only_a);
00291     any_new_current_regions = true;
00292 
00293     if (has_region_in(both, _preferred_region)) {
00294       if (_preferred_region != (MouseWatcherRegion *)NULL) {
00295         exit_region(_preferred_region, MouseWatcherParameter());
00296       }
00297       _preferred_region = (MouseWatcherRegion *)NULL;
00298     }
00299     if (has_region_in(both, _preferred_button_down_region)) {
00300       _preferred_button_down_region = (MouseWatcherRegion *)NULL;
00301     }
00302   }
00303 
00304   // Don't add the new regions--we have no reason to believe these
00305   // should become current; some of them may not even be under the
00306   // mouse.
00307   /*
00308   // And add the new regions
00309   if (!add.empty()) {
00310     Regions new_list;
00311     if (any_new_current_regions) {
00312       intersect_regions(new_list, new_list, new_list,
00313                         new_current_regions, add);
00314     } else {
00315       intersect_regions(new_list, new_list, new_list,
00316                         _current_regions, add);
00317     }
00318     new_current_regions.swap(new_list);
00319     any_new_current_regions = true;
00320   }
00321   */
00322 
00323   if (any_new_current_regions) {
00324     set_current_regions(new_current_regions);
00325   }
00326 
00327   // Add the new group, if it's not already there.
00328   PT(MouseWatcherGroup) pt = new_group;
00329   Groups::iterator gi = 
00330     find(_groups.begin(), _groups.end(), pt);
00331   if (gi == _groups.end()) {
00332     _groups.push_back(new_group);
00333   }
00334 
00335 #ifndef NDEBUG
00336   if (!_show_regions_render2d.is_empty()) {
00337     new_group->do_update_regions();
00338   }
00339 #endif  // NDEBUG
00340     
00341   // Remove the old group, if it is already there.
00342   pt = old_group;
00343   gi = find(_groups.begin(), _groups.end(), pt);
00344   if (gi != _groups.end()) {
00345     // Found it, now erase it
00346     _groups.erase(gi);
00347     return true;
00348   }
00349 
00350   // Did not find the group to erase
00351   return false;
00352 }
00353 
00354 ////////////////////////////////////////////////////////////////////
00355 //     Function: MouseWatcher::get_num_groups
00356 //       Access: Published
00357 //  Description: Returns the number of separate groups added to the
00358 //               MouseWatcher via add_group().
00359 ////////////////////////////////////////////////////////////////////
00360 int MouseWatcher::
00361 get_num_groups() const {
00362   LightMutexHolder holder(_lock);
00363   return _groups.size();
00364 }
00365 
00366 ////////////////////////////////////////////////////////////////////
00367 //     Function: MouseWatcher::get_group
00368 //       Access: Published
00369 //  Description: Returns the nth group added to the MouseWatcher via
00370 //               add_group().
00371 ////////////////////////////////////////////////////////////////////
00372 MouseWatcherGroup *MouseWatcher::
00373 get_group(int n) const {
00374   LightMutexHolder holder(_lock);
00375   nassertr(n >= 0 && n < (int)_groups.size(), NULL);
00376   return _groups[n];
00377 }
00378 
00379 ////////////////////////////////////////////////////////////////////
00380 //     Function: MouseWatcher::set_trail_log_duration
00381 //       Access: Published
00382 //  Description: If the duration is nonzero, causes the MouseWatcher
00383 //               to log the mouse's trail.  Events older than the
00384 //               specified duration are discarded.  If the duration is
00385 //               zero, logging is disabled.
00386 ////////////////////////////////////////////////////////////////////
00387 void MouseWatcher::
00388 set_trail_log_duration(double duration) {
00389   if (duration < 0.0) {
00390     duration = 0.0;
00391   }
00392   _trail_log_duration = duration;
00393   discard_excess_trail_log();
00394 }
00395 
00396 ////////////////////////////////////////////////////////////////////
00397 //     Function: MouseWatcher::discard_excess_trail_log
00398 //       Access: Private
00399 //  Description: Discards trail log events whose age exceed the
00400 //               desired log duration.  Keeps one event that is beyond
00401 //               the specified age, because otherwise, it is not always
00402 //               possible to determine where the mouse was for the
00403 //               full logging duration.  Also, keeps a minimum of two
00404 //               events in the queue.  If the duration is zero, this
00405 //               method discards all trail events.
00406 ////////////////////////////////////////////////////////////////////
00407 void MouseWatcher::
00408 discard_excess_trail_log() {
00409   if (_trail_log_duration == 0.0) {
00410     _trail_log->clear();
00411   } else {
00412     if (_trail_log->get_num_events() > 2) {
00413       double old = ClockObject::get_global_clock()->get_frame_time() - _trail_log_duration;
00414       while ((_trail_log->get_num_events() > 2)&&
00415              (_trail_log->get_time(0) <= old)&&
00416              (_trail_log->get_time(1) <= old)) {
00417         _trail_log->pop_front();
00418       }
00419     }
00420   }
00421 }
00422 
00423 ////////////////////////////////////////////////////////////////////
00424 //     Function: MouseWatcher::get_trail_node
00425 //       Access: Published
00426 //  Description: Returns a GeomNode that represents the mouse trail.
00427 //               The intent is that you should reparent this GeomNode
00428 //               to Render2D, and then forget about it.  The
00429 //               MouseWatcher will continually update the trail node.
00430 //               There is only one trail node, it does not create a
00431 //               new one each time you call get_trail_node.
00432 //
00433 //               This is not a particularly beautiful way to render
00434 //               a mouse trail.  It is intended more for debugging
00435 //               purposes than for finished applications.  Even so,
00436 //               It is suggested that you might want to apply a line
00437 //               thickness and antialias mode to the line --- doing
00438 //               so makes it look a lot better.
00439 ////////////////////////////////////////////////////////////////////
00440 PT(GeomNode) MouseWatcher::
00441 get_trail_node() {
00442   if (_trail_node == 0) {
00443     _trail_node = new GeomNode("Mouse Trail Node");
00444     update_trail_node();
00445   }
00446   return _trail_node;
00447 }
00448 
00449 ////////////////////////////////////////////////////////////////////
00450 //     Function: MouseWatcher::clear_trail_node
00451 //       Access: Published
00452 //  Description: If you have previously fetched the trail node
00453 //               using get_trail_node, then the MouseWatcher is
00454 //               continually updating the trail node every frame.
00455 //               Using clear_trail_node causes the MouseWatcher to
00456 //               forget the trail node and stop updating it.
00457 ////////////////////////////////////////////////////////////////////
00458 void MouseWatcher::
00459 clear_trail_node() {
00460   _trail_node = 0;
00461 }
00462 
00463 ////////////////////////////////////////////////////////////////////
00464 //     Function: MouseWatcher::update_trail_node
00465 //       Access: Private
00466 //  Description: Causes the trail node to represent the mouse trail.
00467 ////////////////////////////////////////////////////////////////////
00468 void MouseWatcher::
00469 update_trail_node() {
00470   if (_trail_node == 0) {
00471     return;
00472   }
00473   _trail_node->remove_all_geoms();
00474 
00475   if (_trail_log->get_num_events() < 2) {
00476     return;
00477   }
00478   
00479   PT(GeomVertexData) data = new GeomVertexData
00480     ("mouseTrailSegs", GeomVertexFormat::get_v3(), Geom::UH_static);
00481   
00482   GeomVertexWriter vertex(data, InternalName::get_vertex());
00483     
00484   PT(GeomLinestrips) lines = new GeomLinestrips(Geom::UH_static);
00485   
00486   double xscale = 2.0 / _pixel_size->get_value().get_x();
00487   double yscale = 2.0 / _pixel_size->get_value().get_y();
00488   
00489   for (int i=0; i<(int)_trail_log->get_num_events(); i++) {
00490     double x = (_trail_log->get_xpos(i) * xscale) - 1.0;
00491     double y = (_trail_log->get_ypos(i) * yscale) - 1.0;
00492     vertex.add_data3(LVecBase3(x,0.0,-y));
00493     lines->add_vertex(i);
00494   }
00495   lines->close_primitive();
00496 
00497   PT(Geom) l_geom = new Geom(data);
00498   l_geom->add_primitive(lines);
00499   _trail_node->add_geom(l_geom);
00500 }
00501 
00502 ////////////////////////////////////////////////////////////////////
00503 //     Function: MouseWatcher::note_activity
00504 //       Access: Published
00505 //  Description: Can be used in conjunction with the inactivity
00506 //               timeout to inform the MouseWatcher that the user has
00507 //               just performed some action which proves he/she is
00508 //               present.  It may be necessary to call this for
00509 //               external events, such as joystick action, that the
00510 //               MouseWatcher might otherwise not know about.  This
00511 //               will reset the current inactivity timer.  When the
00512 //               inactivity timer reaches the length of time specified
00513 //               by set_inactivity_timeout(), with no keyboard or
00514 //               mouse activity and no calls to note_activity(), then
00515 //               any buttons held will be automatically released.
00516 ////////////////////////////////////////////////////////////////////
00517 void MouseWatcher::
00518 note_activity() {
00519   _last_activity = ClockObject::get_global_clock()->get_frame_time();
00520   switch (_inactivity_state) {
00521   case IS_active:
00522     break;
00523 
00524   case IS_inactive:
00525     _inactivity_state = IS_inactive_to_active;
00526     break;
00527 
00528   case IS_active_to_inactive:
00529     _inactivity_state = IS_active;
00530     break;
00531 
00532   case IS_inactive_to_active:
00533     break;
00534   }
00535 }
00536 
00537 
00538 ////////////////////////////////////////////////////////////////////
00539 //     Function: MouseWatcher::output
00540 //       Access: Public, Virtual
00541 //  Description:
00542 ////////////////////////////////////////////////////////////////////
00543 void MouseWatcher::
00544 output(ostream &out) const {
00545   LightMutexHolder holder(_lock);
00546   DataNode::output(out);
00547 
00548   int count = _regions.size();
00549   Groups::const_iterator gi;
00550   for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
00551     MouseWatcherGroup *group = (*gi);
00552     count += group->_regions.size();
00553   }
00554 
00555   out << " (" << count << " regions)";
00556 }
00557 
00558 ////////////////////////////////////////////////////////////////////
00559 //     Function: MouseWatcher::write
00560 //       Access: Public, Virtual
00561 //  Description:
00562 ////////////////////////////////////////////////////////////////////
00563 void MouseWatcher::
00564 write(ostream &out, int indent_level) const {
00565   indent(out, indent_level)
00566     << "MouseWatcher " << get_name() << ":\n";
00567   MouseWatcherGroup::write(out, indent_level + 2);
00568 
00569   LightMutexHolder holder(_lock);
00570   if (!_groups.empty()) {
00571     Groups::const_iterator gi;
00572     for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
00573       MouseWatcherGroup *group = (*gi);
00574       indent(out, indent_level + 2)
00575         << "Subgroup:\n";
00576       group->write(out, indent_level + 4);
00577     }
00578   }
00579 }
00580 
00581 ////////////////////////////////////////////////////////////////////
00582 //     Function: MouseWatcher::get_over_regions
00583 //       Access: Protected
00584 //  Description: Fills up the "regions" list with the set of regions
00585 //               that the indicated point is over, sorted in order by
00586 //               pointer.  Assumes the lock is held.
00587 ////////////////////////////////////////////////////////////////////
00588 void MouseWatcher::
00589 get_over_regions(MouseWatcher::Regions &regions, const LPoint2 &pos) const {
00590   nassertv(_lock.debug_is_locked());
00591 
00592   // Scale the mouse coordinates into the frame.
00593   PN_stdfloat mx = (pos[0] + 1.0f) * 0.5f * (_frame[1] - _frame[0]) + _frame[0];
00594   PN_stdfloat my = (pos[1] + 1.0f) * 0.5f * (_frame[3] - _frame[2]) + _frame[2];
00595 
00596   //  pos[0] = 2.0f * (mx - _frame[0]) / (_frame[1] - _frame[0]) - 1.0f;
00597   //  pos[1] = 2.0f * (my - _frame[2]) / (_frame[3] - _frame[2]) - 1.0f;
00598 
00599   // Ensure the vector is empty before we begin.
00600   regions.clear();
00601 
00602   Regions::const_iterator ri;
00603   for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
00604     MouseWatcherRegion *region = (*ri);
00605     const LVecBase4 &frame = region->get_frame();
00606 
00607     if (region->get_active() &&
00608         mx >= frame[0] && mx <= frame[1] &&
00609         my >= frame[2] && my <= frame[3]) {
00610 
00611       regions.push_back(region);
00612     }
00613   }
00614 
00615   // Also check all of our sub-groups.
00616   Groups::const_iterator gi;
00617   for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
00618     MouseWatcherGroup *group = (*gi);
00619     for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) {
00620       MouseWatcherRegion *region = (*ri);
00621       const LVecBase4 &frame = region->get_frame();
00622       
00623       if (region->get_active() &&
00624           mx >= frame[0] && mx <= frame[1] &&
00625           my >= frame[2] && my <= frame[3]) {
00626         
00627         regions.push_back(region);
00628       }
00629     }
00630   }
00631 
00632   // Now sort the regions by pointer.  By convention, the Regions
00633   // vectors are always kept in order by pointer, so we can do easy
00634   // linear comparison and intersection operations.
00635   sort(regions.begin(), regions.end());
00636 }
00637 
00638 ////////////////////////////////////////////////////////////////////
00639 //     Function: MouseWatcher::get_preferred_region
00640 //       Access: Protected, Static
00641 //  Description: Returns the innermost region of all the regions
00642 //               indicated in the given vector (usually, the regions
00643 //               the mouse is over).  This is the "preferred" region
00644 //               that gets some special treatment.  Assumes the lock
00645 //               is already held.
00646 ////////////////////////////////////////////////////////////////////
00647 MouseWatcherRegion *MouseWatcher::
00648 get_preferred_region(const MouseWatcher::Regions &regions) {
00649   if (regions.empty()) {
00650     return (MouseWatcherRegion *)NULL;
00651   }
00652 
00653   Regions::const_iterator ri;
00654   ri = regions.begin();
00655   MouseWatcherRegion *preferred = *ri;
00656   ++ri;
00657   while (ri != regions.end()) {
00658     MouseWatcherRegion *region = *ri;
00659 
00660     if (*region < *preferred) {
00661       preferred = region;
00662     }
00663     ++ri;
00664   }
00665 
00666   return preferred;
00667 }
00668 
00669 ////////////////////////////////////////////////////////////////////
00670 //     Function: MouseWatcher::set_current_regions
00671 //       Access: Protected
00672 //  Description: Changes the "current" regions--the one we consider the
00673 //               mouse to be over--to the indicated list, and throws
00674 //               whatever events are appropriate because of that.
00675 //
00676 //               The list passed in is destroyed.  Assumes the lock is
00677 //               already held.
00678 ////////////////////////////////////////////////////////////////////
00679 void MouseWatcher::
00680 set_current_regions(MouseWatcher::Regions &regions) {
00681   nassertv(_lock.debug_is_locked());
00682 
00683   // Set up a parameter for passing through any change events.
00684   MouseWatcherParameter param;
00685   param.set_modifier_buttons(_mods);
00686   param.set_mouse(_mouse);
00687 
00688   // Now do a standard sorted comparison between the two vectors.
00689   Regions::const_iterator new_ri = regions.begin();
00690   Regions::const_iterator old_ri = _current_regions.begin();
00691 
00692   // Queue up all the new regions so we can send the within patterns
00693   // all at once, after all of the without patterns have been thrown.
00694   vector<MouseWatcherRegion *> new_regions;
00695 
00696   bool any_changes = false;
00697   while (new_ri != regions.end() && old_ri != _current_regions.end()) {
00698     if ((*new_ri) < (*old_ri)) {
00699       // Here's a new region that we didn't have last frame.
00700       MouseWatcherRegion *new_region = (*new_ri);
00701       new_regions.push_back(new_region);
00702       any_changes = true;
00703       ++new_ri;
00704 
00705     } else if ((*old_ri) < (*new_ri)) {
00706       // Here's a region we don't have any more.
00707       MouseWatcherRegion *old_region = (*old_ri);
00708       without_region(old_region, param);
00709       any_changes = true;
00710       ++old_ri;
00711 
00712     } else {
00713       // Here's a region that hasn't changed.
00714       ++new_ri;
00715       ++old_ri;
00716     }
00717   }
00718 
00719   while (new_ri != regions.end()) {
00720     // Here's a new region that we didn't have last frame.
00721     MouseWatcherRegion *new_region = (*new_ri);
00722     new_regions.push_back(new_region);
00723     any_changes = true;
00724     ++new_ri;
00725   }
00726 
00727   while (old_ri != _current_regions.end()) {
00728     // Here's a region we don't have any more.
00729     MouseWatcherRegion *old_region = (*old_ri);
00730     without_region(old_region, param);
00731     any_changes = true;
00732     ++old_ri;
00733   }
00734 
00735   if (any_changes) {
00736     // Now that we've compared the two vectors, simply swap them to set
00737     // the new vector.
00738     _current_regions.swap(regions);
00739 
00740     // And don't forget to throw all of the new regions' "within" events.
00741     vector<MouseWatcherRegion *>::const_iterator ri;
00742     for (ri = new_regions.begin(); ri != new_regions.end(); ++ri) {
00743       MouseWatcherRegion *new_region = (*ri);
00744       within_region(new_region, param);
00745     }
00746   }
00747 
00748   if (!_enter_multiple) {
00749     // Determine which is the "preferred region", if any.  This is the
00750     // topmost region that the mouse cursor is over, and the one that
00751     // we are considered "entered" into.
00752     MouseWatcherRegion *new_preferred_region = 
00753       get_preferred_region(_current_regions);
00754     
00755     if (_button_down && new_preferred_region != _preferred_button_down_region) {
00756       // If the button's being held down, we're only allowed to select
00757       // the preferred button down region.
00758       new_preferred_region = (MouseWatcherRegion *)NULL;
00759     }
00760 
00761     if (new_preferred_region != _preferred_region) {
00762       if (_preferred_region != (MouseWatcherRegion *)NULL) {
00763         exit_region(_preferred_region, param);
00764       }
00765       _preferred_region = new_preferred_region;
00766       if (_preferred_region != (MouseWatcherRegion *)NULL) {
00767         enter_region(_preferred_region, param);
00768       }
00769     }
00770   }
00771 }
00772 
00773 ////////////////////////////////////////////////////////////////////
00774 //     Function: MouseWatcher::clear_current_regions
00775 //       Access: Protected
00776 //  Description: Empties the set of current regions.  Assumes the lock
00777 //               is already held.
00778 ////////////////////////////////////////////////////////////////////
00779 void MouseWatcher::
00780 clear_current_regions() {
00781   nassertv(_lock.debug_is_locked());
00782 
00783   if (!_current_regions.empty()) {
00784     // Set up a parameter for passing through any change events.
00785     MouseWatcherParameter param;
00786     param.set_modifier_buttons(_mods);
00787     param.set_mouse(_mouse);
00788     
00789     Regions::const_iterator old_ri = _current_regions.begin();
00790     
00791     while (old_ri != _current_regions.end()) {
00792       // Here's a region we don't have any more.
00793       MouseWatcherRegion *old_region = (*old_ri);
00794       old_region->exit_region(param);
00795       throw_event_pattern(_leave_pattern, old_region, ButtonHandle::none());
00796       ++old_ri;
00797     }
00798     
00799     _current_regions.clear();
00800 
00801     if (_preferred_region != (MouseWatcherRegion *)NULL) {
00802       _preferred_region->exit_region(param);
00803       throw_event_pattern(_leave_pattern, _preferred_region, ButtonHandle::none());
00804       _preferred_region = (MouseWatcherRegion *)NULL;
00805     }
00806   }
00807 }
00808 
00809 #ifndef NDEBUG
00810 ////////////////////////////////////////////////////////////////////
00811 //     Function: MouseWatcher::do_show_regions
00812 //       Access: Protected, Virtual
00813 //  Description: The protected implementation of show_regions().  This
00814 //               assumes the lock is already held.
00815 ////////////////////////////////////////////////////////////////////
00816 void MouseWatcher::
00817 do_show_regions(const NodePath &render2d, const string &bin_name, 
00818                 int draw_order) {
00819   MouseWatcherGroup::do_show_regions(render2d, bin_name, draw_order);
00820   _show_regions_render2d = render2d;
00821   _show_regions_bin_name = bin_name;
00822   _show_regions_draw_order = draw_order;
00823 
00824   Groups::const_iterator gi;
00825   for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
00826     MouseWatcherGroup *group = (*gi);
00827     group->show_regions(render2d, bin_name, draw_order);
00828   }
00829 }
00830 #endif  // NDEBUG
00831 
00832 #ifndef NDEBUG
00833 ////////////////////////////////////////////////////////////////////
00834 //     Function: MouseWatcher::do_hide_regions
00835 //       Access: Protected, Virtual
00836 //  Description: The protected implementation of hide_regions().  This
00837 //               assumes the lock is already held.
00838 ////////////////////////////////////////////////////////////////////
00839 void MouseWatcher::
00840 do_hide_regions() {
00841   MouseWatcherGroup::do_hide_regions();
00842   _show_regions_render2d = NodePath();
00843   _show_regions_bin_name = string();
00844   _show_regions_draw_order = 0;
00845 
00846   Groups::const_iterator gi;
00847   for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
00848     MouseWatcherGroup *group = (*gi);
00849     group->hide_regions();
00850   }
00851 }
00852 #endif  // NDEBUG
00853 
00854 ////////////////////////////////////////////////////////////////////
00855 //     Function: MouseWatcher::intersect_regions
00856 //       Access: Protected, Static
00857 //  Description: Computes the list of regions that are in both
00858 //               regions_a and regions_b, as well as the list of
00859 //               regions only in regions_a, and the list of regions
00860 //               only in regions_b.  Any or all of the three output
00861 //               lists may be the same object, but they must be
00862 //               different objects from both of the input lists.
00863 //
00864 //               It is assumed that both vectors are already sorted in
00865 //               pointer order.  It is also assumed that any relevant
00866 //               locks are already held.
00867 ////////////////////////////////////////////////////////////////////
00868 void MouseWatcher::
00869 intersect_regions(MouseWatcher::Regions &only_a,
00870                   MouseWatcher::Regions &only_b,
00871                   MouseWatcher::Regions &both,
00872                   const MouseWatcher::Regions &regions_a,
00873                   const MouseWatcher::Regions &regions_b) {
00874   // Now do a standard sorted intersection between the two vectors.
00875   Regions::const_iterator a_ri = regions_a.begin();
00876   Regions::const_iterator b_ri = regions_b.begin();
00877 
00878   while (a_ri != regions_a.end() && b_ri != regions_b.end()) {
00879     if ((*a_ri) < (*b_ri)) {
00880       // Here's a region in a, not in b.
00881       only_a.push_back(*a_ri);
00882       ++a_ri;
00883 
00884     } else if ((*b_ri) < (*a_ri)) {
00885       // Here's a region in b, not in a.
00886       only_b.push_back(*b_ri);
00887       ++b_ri;
00888 
00889     } else {
00890       // Here's a region in both vectors.
00891       both.push_back(*a_ri);
00892       ++a_ri;
00893       ++b_ri;
00894     }
00895   }
00896 }
00897 
00898 ////////////////////////////////////////////////////////////////////
00899 //     Function: MouseWatcher::remove_region_from
00900 //       Access: Protected, Static
00901 //  Description: Removes the indicated region from the given vector.
00902 //               Assumes the vector is sorted in pointer order.
00903 //               Returns true if removed, false if it wasn't there.
00904 //               Assumes any relevent locks are already held.
00905 ////////////////////////////////////////////////////////////////////
00906 bool MouseWatcher::
00907 remove_region_from(MouseWatcher::Regions &regions,
00908                    MouseWatcherRegion *region) {
00909   PT(MouseWatcherRegion) ptr = region;
00910   Regions::iterator ri = lower_bound(regions.begin(), regions.end(), ptr);
00911   if (ri != regions.end() && (*ri) == ptr) {
00912     // The region is in the vector.  Remove it.
00913     regions.erase(ri);
00914     return true;
00915   }
00916 
00917   return false;
00918 }
00919 
00920 ////////////////////////////////////////////////////////////////////
00921 //     Function: MouseWatcher::has_region_in
00922 //       Access: Protected, Static
00923 //  Description: Returns true if the indicated region is a member of
00924 //               the given sorted list, false otherwise.
00925 ////////////////////////////////////////////////////////////////////
00926 bool MouseWatcher::
00927 has_region_in(const MouseWatcher::Regions &regions,
00928               MouseWatcherRegion *region) {
00929   PT(MouseWatcherRegion) ptr = region;
00930   Regions::const_iterator ri = lower_bound(regions.begin(), regions.end(), ptr);
00931   return (ri != regions.end() && (*ri) == ptr);
00932 }
00933 
00934 ////////////////////////////////////////////////////////////////////
00935 //     Function: MouseWatcher::throw_event_pattern
00936 //       Access: Protected
00937 //  Description: Throws an event associated with the indicated region,
00938 //               using the given pattern.
00939 ////////////////////////////////////////////////////////////////////
00940 void MouseWatcher::
00941 throw_event_pattern(const string &pattern, const MouseWatcherRegion *region,
00942                     const ButtonHandle &button) {
00943   if (pattern.empty()) {
00944     return;
00945   }
00946 #ifndef NDEBUG
00947   if (region != (MouseWatcherRegion *)NULL) {
00948     region->test_ref_count_integrity();
00949   }
00950 #endif
00951 
00952   string button_name;
00953   if (button != ButtonHandle::none()) {
00954     if (!_mods.has_button(button)) {
00955       // We only prepend modifier names for buttons which are not
00956       // themselves modifiers.
00957       button_name = _mods.get_prefix();
00958     }
00959     button_name += button.get_name();
00960   }
00961 
00962   string event;
00963   for (size_t p = 0; p < pattern.size(); ++p) {
00964     if (pattern[p] == '%') {
00965       string cmd = pattern.substr(p + 1, 1);
00966       p++;
00967       if (cmd == "r") {
00968         if (region != (MouseWatcherRegion *)NULL) {
00969           event += region->get_name();
00970         }
00971 
00972       } else if (cmd == "b") {
00973         event += button.get_name();
00974 
00975       } else {
00976         tform_cat.error()
00977           << "Invalid symbol in event_pattern: %" << cmd << "\n";
00978       }
00979     } else {
00980       event += pattern[p];
00981     }
00982   }
00983 
00984   if (!event.empty()) {
00985     throw_event(event, EventParameter(region), EventParameter(button_name));
00986     if (_eh != (EventHandler*)0L)
00987       throw_event_directly(*_eh, event, EventParameter(region),
00988                            EventParameter(button_name));
00989   }
00990 }
00991 
00992 ////////////////////////////////////////////////////////////////////
00993 //     Function: MouseWatcher::move
00994 //       Access: Protected
00995 //  Description: Records the indicated mouse or keyboard button as
00996 //               being moved from last position.
00997 ////////////////////////////////////////////////////////////////////
00998 void MouseWatcher::
00999 move() {
01000   nassertv(_lock.debug_is_locked());
01001 
01002   MouseWatcherParameter param;
01003   param.set_modifier_buttons(_mods);
01004   param.set_mouse(_mouse);
01005 
01006   if (_preferred_button_down_region != (MouseWatcherRegion *)NULL) {
01007     _preferred_button_down_region->move(param);
01008   }
01009 }
01010 
01011 ////////////////////////////////////////////////////////////////////
01012 //     Function: MouseWatcher::press
01013 //       Access: Protected
01014 //  Description: Records the indicated mouse or keyboard button as
01015 //               being depressed.
01016 ////////////////////////////////////////////////////////////////////
01017 void MouseWatcher::
01018 press(ButtonHandle button, bool keyrepeat) {
01019   nassertv(_lock.debug_is_locked());
01020 
01021   MouseWatcherParameter param;
01022   param.set_button(button);
01023   param.set_keyrepeat(keyrepeat);
01024   param.set_modifier_buttons(_mods);
01025   param.set_mouse(_mouse);
01026 
01027   if (MouseButton::is_mouse_button(button)) {
01028     // Mouse buttons are inextricably linked to the mouse position.
01029     
01030     if (!_button_down) {
01031       _preferred_button_down_region = _preferred_region;
01032     }
01033     _button_down = true;
01034 
01035     if (_preferred_button_down_region != (MouseWatcherRegion *)NULL) {
01036       _preferred_button_down_region->press(param);
01037       if (keyrepeat) {
01038         throw_event_pattern(_button_repeat_pattern,
01039                             _preferred_button_down_region, button);
01040       } else {
01041         throw_event_pattern(_button_down_pattern,
01042                             _preferred_button_down_region, button);
01043       }
01044     }
01045     
01046   } else {
01047     // It's a keyboard button; therefore, send the event to every
01048     // region that wants keyboard buttons, regardless of the mouse
01049     // position.
01050     if (_preferred_region != (MouseWatcherRegion *)NULL) {
01051       // Our current region, the one under the mouse, always get
01052       // all the keyboard events, even if it doesn't set its
01053       // keyboard flag.
01054       _preferred_region->press(param);
01055       consider_keyboard_suppress(_preferred_region);
01056     }
01057 
01058     if ((_internal_suppress & MouseWatcherRegion::SF_other_button) == 0) {
01059       // All the other regions only get the keyboard events if they
01060       // set their global keyboard flag, *and* the current region does
01061       // not suppress keyboard buttons.
01062       param.set_outside(true);
01063       global_keyboard_press(param);
01064     }
01065   }
01066 }
01067 
01068 ////////////////////////////////////////////////////////////////////
01069 //     Function: MouseWatcher::release
01070 //       Access: Protected
01071 //  Description: Records the indicated mouse or keyboard button as
01072 //               being released.
01073 ////////////////////////////////////////////////////////////////////
01074 void MouseWatcher::
01075 release(ButtonHandle button) {
01076   nassertv(_lock.debug_is_locked());
01077 
01078   MouseWatcherParameter param;
01079   param.set_button(button);
01080   param.set_modifier_buttons(_mods);
01081   param.set_mouse(_mouse);
01082 
01083   if (MouseButton::is_mouse_button(button)) {
01084     // Button up.  Send the up event associated with the region(s) we
01085     // were over when the button went down.
01086     
01087     // There is some danger of losing button-up events here.  If
01088     // more than one button goes down together, we won't detect
01089     // both of the button-up events properly.
01090     if (_preferred_button_down_region != (MouseWatcherRegion *)NULL) {
01091       param.set_outside(_preferred_button_down_region != _preferred_region);
01092       _preferred_button_down_region->release(param);
01093       throw_event_pattern(_button_up_pattern,
01094                           _preferred_button_down_region, button);
01095     }
01096 
01097     _button_down = false;
01098     _preferred_button_down_region = (MouseWatcherRegion *)NULL;
01099     
01100   } else {
01101     // It's a keyboard button; therefore, send the event to every
01102     // region that wants keyboard buttons, regardless of the mouse
01103     // position.
01104     if (_preferred_region != (MouseWatcherRegion *)NULL) {
01105       _preferred_region->release(param);
01106     }
01107     
01108     param.set_outside(true);
01109     global_keyboard_release(param);
01110   }
01111 }
01112 
01113 ////////////////////////////////////////////////////////////////////
01114 //     Function: MouseWatcher::keystroke
01115 //       Access: Protected
01116 //  Description: Records that the indicated keystroke has been
01117 //               generated.
01118 ////////////////////////////////////////////////////////////////////
01119 void MouseWatcher::
01120 keystroke(int keycode) {
01121   nassertv(_lock.debug_is_locked());
01122 
01123   MouseWatcherParameter param;
01124   param.set_keycode(keycode);
01125   param.set_modifier_buttons(_mods);
01126   param.set_mouse(_mouse);
01127 
01128   // Keystrokes go to all those regions that want keyboard events,
01129   // regardless of which is the "preferred" region (that is, without
01130   // respect to the mouse position).  However, we do set the outside
01131   // flag according to whether the given region is the preferred
01132   // region or not.
01133 
01134   Regions::const_iterator ri;
01135   for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
01136     MouseWatcherRegion *region = (*ri);
01137 
01138     if (region->get_keyboard()) {
01139       param.set_outside(region != _preferred_region);
01140       region->keystroke(param);
01141       consider_keyboard_suppress(region);
01142     }
01143   }
01144 
01145   // Also check all of our sub-groups.
01146   Groups::const_iterator gi;
01147   for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
01148     MouseWatcherGroup *group = (*gi);
01149     for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) {
01150       MouseWatcherRegion *region = (*ri);
01151 
01152       if (region->get_keyboard()) {
01153         param.set_outside(region != _preferred_region);
01154         region->keystroke(param);
01155         consider_keyboard_suppress(region);
01156       }
01157     }
01158   }
01159 }
01160 
01161 ////////////////////////////////////////////////////////////////////
01162 //     Function: MouseWatcher::candidate
01163 //       Access: Protected
01164 //  Description: Records that the indicated candidate string has been
01165 //               highlighted in the IME.
01166 ////////////////////////////////////////////////////////////////////
01167 void MouseWatcher::
01168 candidate(const wstring &candidate_string, size_t highlight_start, 
01169           size_t highlight_end, size_t cursor_pos) {
01170   nassertv(_lock.debug_is_locked());
01171 
01172   MouseWatcherParameter param;
01173   param.set_candidate(candidate_string, highlight_start, highlight_end, cursor_pos);
01174   param.set_modifier_buttons(_mods);
01175   param.set_mouse(_mouse);
01176 
01177   // Candidate strings go to all those regions that want keyboard
01178   // events, exactly like keystrokes, above.
01179 
01180   Regions::const_iterator ri;
01181   for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
01182     MouseWatcherRegion *region = (*ri);
01183 
01184     if (region->get_keyboard()) {
01185       param.set_outside(region != _preferred_region);
01186       region->candidate(param);
01187     }
01188   }
01189 
01190   // Also check all of our sub-groups.
01191   Groups::const_iterator gi;
01192   for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
01193     MouseWatcherGroup *group = (*gi);
01194     for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) {
01195       MouseWatcherRegion *region = (*ri);
01196 
01197       if (region->get_keyboard()) {
01198         param.set_outside(region != _preferred_region);
01199         region->candidate(param);
01200       }
01201     }
01202   }
01203 }
01204 
01205 ////////////////////////////////////////////////////////////////////
01206 //     Function: MouseWatcher::global_keyboard_press
01207 //       Access: Protected
01208 //  Description: Calls press() on all regions that are interested in
01209 //               receiving global keyboard events, except for the
01210 //               current region (which already received this one).
01211 ////////////////////////////////////////////////////////////////////
01212 void MouseWatcher::
01213 global_keyboard_press(const MouseWatcherParameter &param) {
01214   nassertv(_lock.debug_is_locked());
01215 
01216   Regions::const_iterator ri;
01217   for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
01218     MouseWatcherRegion *region = (*ri);
01219 
01220     if (region != _preferred_region && region->get_keyboard()) {
01221       region->press(param);
01222       consider_keyboard_suppress(region);
01223     }
01224   }
01225 
01226   // Also check all of our sub-groups.
01227   Groups::const_iterator gi;
01228   for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
01229     MouseWatcherGroup *group = (*gi);
01230     for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) {
01231       MouseWatcherRegion *region = (*ri);
01232 
01233       if (region != _preferred_region && region->get_keyboard()) {
01234         region->press(param);
01235         consider_keyboard_suppress(region);
01236       }
01237     }
01238   }
01239 }
01240 ////////////////////////////////////////////////////////////////////
01241 //     Function: MouseWatcher::global_keyboard_release
01242 //       Access: Protected
01243 //  Description: Calls release() on all regions that are interested in
01244 //               receiving global keyboard events, except for the
01245 //               current region (which already received this one).
01246 ////////////////////////////////////////////////////////////////////
01247 void MouseWatcher::
01248 global_keyboard_release(const MouseWatcherParameter &param) {
01249   nassertv(_lock.debug_is_locked());
01250 
01251   Regions::const_iterator ri;
01252   for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
01253     MouseWatcherRegion *region = (*ri);
01254 
01255     if (region != _preferred_region && region->get_keyboard()) {
01256       region->release(param);
01257     }
01258   }
01259 
01260   // Also check all of our sub-groups.
01261   Groups::const_iterator gi;
01262   for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
01263     MouseWatcherGroup *group = (*gi);
01264     for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) {
01265       MouseWatcherRegion *region = (*ri);
01266 
01267       if (region != _preferred_region && region->get_keyboard()) {
01268         region->release(param);
01269       }
01270     }
01271   }
01272 }
01273 
01274 ////////////////////////////////////////////////////////////////////
01275 //     Function: MouseWatcher::enter_region
01276 //       Access: Protected
01277 //  Description: Called internally to indicate the mouse pointer is 
01278 //               favoring the indicated region.
01279 ////////////////////////////////////////////////////////////////////
01280 void MouseWatcher::
01281 enter_region(MouseWatcherRegion *region, const MouseWatcherParameter &param) {
01282   nassertv(_lock.debug_is_locked());
01283 
01284   region->enter_region(param);
01285   throw_event_pattern(_enter_pattern, region, ButtonHandle::none());
01286   if (_implicit_click) {
01287     MouseWatcherParameter param1(param);
01288     param1.set_button(MouseButton::one());
01289     region->press(param1);
01290   }
01291 }
01292 
01293 ////////////////////////////////////////////////////////////////////
01294 //     Function: MouseWatcher::exit_region
01295 //       Access: Protected
01296 //  Description: Called internally to indicate the mouse pointer is no
01297 //               longer favoring the indicated region.
01298 ////////////////////////////////////////////////////////////////////
01299 void MouseWatcher::
01300 exit_region(MouseWatcherRegion *region, const MouseWatcherParameter &param) {
01301   nassertv(_lock.debug_is_locked());
01302 
01303   if (_implicit_click) {
01304     MouseWatcherParameter param1(param);
01305     param1.set_button(MouseButton::one());
01306     region->release(param1);
01307   }
01308   region->exit_region(param);
01309   throw_event_pattern(_leave_pattern, region, ButtonHandle::none());
01310 }
01311 
01312 ////////////////////////////////////////////////////////////////////
01313 //     Function: MouseWatcher::set_no_mouse
01314 //       Access: Protected
01315 //  Description: Called from do_transmit_data() to indicate the mouse
01316 //               is not within the window.
01317 ////////////////////////////////////////////////////////////////////
01318 void MouseWatcher::
01319 set_no_mouse() {
01320   nassertv(_lock.debug_is_locked());
01321 
01322   if (_has_mouse) {
01323     // Hide the mouse pointer.
01324     if (!_geometry.is_null()) {
01325       _geometry->set_overall_hidden(true);
01326     }
01327   }
01328   
01329   _has_mouse = false;
01330   clear_current_regions();
01331 }
01332 
01333 ////////////////////////////////////////////////////////////////////
01334 //     Function: MouseWatcher::set_mouse
01335 //       Access: Protected
01336 //  Description: Called from do_transmit_data() to indicate the mouse
01337 //               is within the window, and to specify its current
01338 //               position.
01339 ////////////////////////////////////////////////////////////////////
01340 void MouseWatcher::
01341 set_mouse(const LVecBase2 &xy, const LVecBase2 &pixel_xy) {
01342   nassertv(_lock.debug_is_locked());
01343 
01344   if (!_geometry.is_null()) {
01345     // Transform the mouse pointer.
01346     _geometry->set_transform(TransformState::make_pos(LVecBase3(xy[0], 0, xy[1])));
01347     if (!_has_mouse) {
01348       // Show the mouse pointer.
01349       _geometry->set_overall_hidden(false);
01350     }
01351   }
01352   
01353   _has_mouse = true;
01354   _mouse = xy;
01355   _mouse_pixel = pixel_xy;
01356     
01357   Regions regions;
01358   get_over_regions(regions, _mouse);
01359   set_current_regions(regions);
01360 }
01361 
01362 ////////////////////////////////////////////////////////////////////
01363 //     Function: MouseWatcher::consider_keyboard_suppress
01364 //       Access: Private
01365 //  Description: If we send any keyboard events to a region that has
01366 //               the SF_other_button suppress flag set, that means we
01367 //               should not send the keyboard event along the data
01368 //               graph.  
01369 //
01370 //               This method is called as each keyboard event is sent
01371 //               to a region; it should update the internal
01372 //               _keyboard_suppress bitmask to indicate this.
01373 ////////////////////////////////////////////////////////////////////
01374 void MouseWatcher::
01375 consider_keyboard_suppress(const MouseWatcherRegion *region) {
01376   if ((region->get_suppress_flags() & MouseWatcherRegion::SF_other_button) != 0) {
01377     _external_suppress |= MouseWatcherRegion::SF_other_button;
01378   }
01379 }
01380 
01381 ////////////////////////////////////////////////////////////////////
01382 //     Function: MouseWatcher::do_transmit_data
01383 //       Access: Protected, Virtual
01384 //  Description: The virtual implementation of transmit_data().  This
01385 //               function receives an array of input parameters and
01386 //               should generate an array of output parameters.  The
01387 //               input parameters may be accessed with the index
01388 //               numbers returned by the define_input() calls that
01389 //               were made earlier (presumably in the constructor);
01390 //               likewise, the output parameters should be set with
01391 //               the index numbers returned by the define_output()
01392 //               calls.
01393 ////////////////////////////////////////////////////////////////////
01394 void MouseWatcher::
01395 do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
01396                  DataNodeTransmit &output) {
01397   Thread *current_thread = trav->get_current_thread();
01398   LightMutexHolder holder(_lock);
01399 
01400   bool activity = false;
01401 
01402   // Initially, we do not suppress any events to objects below us in
01403   // the data graph.
01404   _internal_suppress = 0;
01405   _external_suppress = 0;
01406 
01407   // We always pass the pixel_size data through.
01408   EventStoreVec2 *pixel_size;
01409   DCAST_INTO_V(pixel_size, input.get_data(_pixel_size_input).get_ptr());
01410   output.set_data(_pixel_size_output, pixel_size);
01411   _pixel_size = pixel_size;
01412 
01413   if (input.has_data(_xy_input)) {
01414     // The mouse is within the window.  Get the current mouse position.
01415     const EventStoreVec2 *xy, *pixel_xy;
01416     DCAST_INTO_V(xy, input.get_data(_xy_input).get_ptr());
01417     DCAST_INTO_V(pixel_xy, input.get_data(_pixel_xy_input).get_ptr());
01418 
01419     LVecBase2 f = xy->get_value();
01420     LVecBase2 p = pixel_xy->get_value();
01421 
01422     // Asad: determine if mouse moved from last position
01423     const LVecBase2 &last_f = _xy->get_value();
01424     if (f != last_f) {
01425       activity = true;
01426       move();
01427     }
01428 
01429     if (_display_region != (DisplayRegion *)NULL) {
01430       // If we've got a display region, constrain the mouse to it.
01431       if (constrain_display_region(_display_region, f, p, current_thread)) {
01432         set_mouse(f, p);
01433 
01434       } else {
01435         // The mouse is outside the display region, even though it's
01436         // within the window.  This is considered not having a mouse.
01437         set_no_mouse();
01438         
01439         // This also means we should suppress mouse button events below us.
01440         _internal_suppress |= MouseWatcherRegion::SF_mouse_button;
01441       }
01442 
01443     } else {
01444       // No display region; respect the whole window.
01445       set_mouse(f, p);
01446     }
01447   }
01448 
01449   // Code for recording the mouse trail.
01450   _num_trail_recent = 0;
01451   if (input.has_data(_pointer_events_input) && (_trail_log_duration > 0.0)) {
01452     const PointerEventList *this_pointer_events;
01453     DCAST_INTO_V(this_pointer_events, input.get_data(_pointer_events_input).get_ptr());
01454     _num_trail_recent = this_pointer_events->get_num_events();
01455     for (int i = 0; i < _num_trail_recent; i++) {
01456       bool in_win = this_pointer_events->get_in_window(i);
01457       int xpos = this_pointer_events->get_xpos(i);
01458       int ypos = this_pointer_events->get_ypos(i);
01459       int sequence = this_pointer_events->get_sequence(i);
01460       double time = this_pointer_events->get_time(i);
01461       _trail_log->add_event(in_win, xpos, ypos, sequence, time);
01462     }
01463   }
01464   if (_trail_log->get_num_events() > 0) {
01465     discard_excess_trail_log();
01466     update_trail_node();
01467   }
01468   if (_num_trail_recent > _trail_log->get_num_events()) {
01469     _num_trail_recent = _trail_log->get_num_events();
01470   }
01471   
01472   // If the mouse is over a particular region, or still considered
01473   // owned by a region because of a recent button-down event, that
01474   // region determines whether we suppress events below us.
01475   if (_preferred_region != (MouseWatcherRegion *)NULL) {
01476     _internal_suppress |= _preferred_region->get_suppress_flags();
01477   }
01478 
01479   ButtonEventList new_button_events;
01480 
01481   // Look for new button events.
01482   if (input.has_data(_button_events_input)) {
01483     const ButtonEventList *this_button_events;
01484     DCAST_INTO_V(this_button_events, input.get_data(_button_events_input).get_ptr());
01485     int num_events = this_button_events->get_num_events();
01486     for (int i = 0; i < num_events; i++) {
01487       const ButtonEvent &be = this_button_events->get_event(i);
01488       be.update_mods(_mods);
01489 
01490       switch (be._type) {
01491       case ButtonEvent::T_down:
01492         if (!_current_buttons_down.get_bit(be._button.get_index())) {
01493           // The button was not already depressed; thus, this is not
01494           // keyrepeat.
01495           activity = true;
01496           _current_buttons_down.set_bit(be._button.get_index());
01497           press(be._button, false);
01498           new_button_events.add_event(be);
01499           break;
01500         }
01501         // The button was already depressed, so this is really just
01502         // keyrepeat.  Fall through.
01503 
01504       case ButtonEvent::T_repeat:
01505         _current_buttons_down.set_bit(be._button.get_index());
01506         press(be._button, true);
01507         new_button_events.add_event(ButtonEvent(be._button, ButtonEvent::T_repeat,
01508                                                 be._time));
01509         break;
01510 
01511       case ButtonEvent::T_up:
01512         activity = true;
01513         _current_buttons_down.clear_bit(be._button.get_index());
01514         release(be._button);
01515         new_button_events.add_event(be);
01516         break;
01517 
01518       case ButtonEvent::T_keystroke:
01519         // We don't consider "keystroke" an activity event, because it
01520         // might be just keyrepeat.
01521         keystroke(be._keycode);
01522         new_button_events.add_event(be);
01523         break;
01524 
01525       case ButtonEvent::T_candidate:
01526         activity = true;
01527         candidate(be._candidate_string, be._highlight_start, be._highlight_end, be._cursor_pos);
01528         new_button_events.add_event(be);
01529         break;
01530 
01531       case ButtonEvent::T_resume_down:
01532         //_current_buttons_down.set_bit(be._button.get_index());
01533         // Don't call press(), since the button wasn't actually
01534         // pressed just now.
01535         new_button_events.add_event(be);
01536         break;
01537 
01538       case ButtonEvent::T_move:
01539         // This is handled below.
01540         break;
01541       }
01542     }
01543   }
01544 
01545   if (!input.has_data(_xy_input)) {
01546     // No mouse in the window.  We check this down here, below the
01547     // button checking, in case the mouse left the window in the same
01548     // frame it released a button (particularly likely with a
01549     // touchscreen input that's emulating a mouse).
01550     set_no_mouse();
01551   }
01552 
01553   // Now check the inactivity timer.
01554   if (_has_inactivity_timeout) {
01555     if (activity) {
01556       note_activity();
01557       
01558     } else {
01559       double now = ClockObject::get_global_clock()->get_frame_time();
01560       double elapsed = now - _last_activity;
01561 
01562       // Toggle the inactivity state to inactive.
01563       if (elapsed > _inactivity_timeout) {
01564         switch (_inactivity_state) {
01565         case IS_active:
01566           _inactivity_state = IS_active_to_inactive;
01567           break;
01568 
01569         case IS_inactive:
01570           break;
01571           
01572         case IS_active_to_inactive:
01573           break;
01574           
01575         case IS_inactive_to_active:
01576           _inactivity_state = IS_inactive;
01577           break;
01578         }
01579       }
01580     }
01581   }
01582 
01583   switch (_inactivity_state) {
01584   case IS_active:
01585   case IS_inactive:
01586     break;
01587     
01588   case IS_active_to_inactive:
01589     // "Release" all of the currently-held buttons.
01590     if (tform_cat.is_debug()) {
01591       tform_cat.info()
01592         << "MouseWatcher detected " << _inactivity_timeout
01593         << " seconds of inactivity; releasing held buttons.\n";
01594     }
01595     {
01596       for (int i = 0; i < _current_buttons_down.get_num_bits(); ++i) {
01597         if (_current_buttons_down.get_bit(i)) {
01598           release(ButtonHandle(i));
01599           new_button_events.add_event(ButtonEvent(ButtonHandle(i), ButtonEvent::T_up));
01600         }
01601       }
01602     }
01603     _inactivity_state = IS_inactive;
01604     throw_event(_inactivity_timeout_event);
01605     break;
01606     
01607   case IS_inactive_to_active:
01608     // "Press" all of the buttons we "released" before.
01609     {
01610       for (int i = 0; i < _current_buttons_down.get_num_bits(); ++i) {
01611         if (_current_buttons_down.get_bit(i)) {
01612           press(ButtonHandle(i), false);
01613           new_button_events.add_event(ButtonEvent(ButtonHandle(i), ButtonEvent::T_down));
01614         }
01615       }
01616     }
01617     _inactivity_state = IS_active;
01618     break;
01619   }
01620 
01621   if (_has_mouse &&
01622       (_internal_suppress & MouseWatcherRegion::SF_mouse_position) == 0) {
01623     // Transmit the mouse position.
01624     _xy->set_value(_mouse);
01625     output.set_data(_xy_output, EventParameter(_xy));
01626     _pixel_xy->set_value(_mouse_pixel);
01627     output.set_data(_pixel_xy_output, EventParameter(_pixel_xy));
01628   }
01629 
01630   // Now transmit the buttons events down the graph.
01631   int suppress_buttons = ((_internal_suppress | _external_suppress) & MouseWatcherRegion::SF_any_button);
01632 
01633   _button_events->clear();
01634 
01635   int num_events = new_button_events.get_num_events();
01636   for (int i = 0; i < num_events; i++) {
01637     const ButtonEvent &be = new_button_events.get_event(i);
01638     bool suppress = true;
01639     
01640     if (be._type != ButtonEvent::T_keystroke && 
01641         MouseButton::is_mouse_button(be._button)) {
01642       suppress = ((suppress_buttons & MouseWatcherRegion::SF_mouse_button) != 0);
01643     } else {
01644       suppress = ((suppress_buttons & MouseWatcherRegion::SF_other_button) != 0);
01645     }
01646     
01647     if (!suppress || be._type == ButtonEvent::T_up) {
01648       // Don't suppress this button event; pass it through.
01649       _button_events->add_event(be);
01650     }
01651   }
01652   
01653   if (_button_events->get_num_events() != 0) {
01654     output.set_data(_button_events_output, EventParameter(_button_events));
01655   }
01656 }
01657 
01658 ////////////////////////////////////////////////////////////////////
01659 //     Function: MouseWatcher::constrain_display_region
01660 //       Access: Private, Static
01661 //  Description: Constrains the mouse coordinates to within the
01662 //               indicated DisplayRegion.  If the mouse pointer does
01663 //               indeed fall within the DisplayRegion, rescales f and
01664 //               p correspondingly, and returns true.  If the mouse
01665 //               pointer does not fall within the DisplayRegion,
01666 //               leaves f and p unchanged, and returns false.
01667 ////////////////////////////////////////////////////////////////////
01668 bool MouseWatcher::
01669 constrain_display_region(DisplayRegion *display_region, 
01670                          LVecBase2 &f, LVecBase2 &p,
01671                          Thread *current_thread) {
01672   // If it's a stereo DisplayRegion, we should actually call this
01673   // method twice, once for each eye, in case we have side-by-side
01674   // stereo.
01675   if (display_region->is_stereo()) {
01676     StereoDisplayRegion *stereo_display_region;
01677     DCAST_INTO_R(stereo_display_region, display_region, false);
01678     return constrain_display_region(stereo_display_region->get_left_eye(), f, p, current_thread) ||
01679       constrain_display_region(stereo_display_region->get_right_eye(), f, p, current_thread);
01680   }
01681 
01682   DisplayRegionPipelineReader dr_reader(display_region, current_thread);
01683   PN_stdfloat left, right, bottom, top;
01684   dr_reader.get_dimensions(left, right, bottom, top);
01685   
01686   // Need to translate this into DisplayRegion [0, 1] space
01687   PN_stdfloat x = (f[0] + 1.0f) / 2.0f;
01688   PN_stdfloat y = (f[1] + 1.0f) / 2.0f;
01689   
01690   if (x < left || x >= right || 
01691       y < bottom || y >= top) {
01692     // The mouse is outside the display region.
01693     return false;
01694   }
01695     
01696   // The mouse is within the display region; rescale it.
01697   
01698   // Scale in DR space
01699   PN_stdfloat xp = (x - left) / (right - left);
01700   // Translate back into [-1, 1] space
01701   PN_stdfloat xpp = (xp * 2.0f) - 1.0f;
01702   
01703   PN_stdfloat yp = (y - bottom) / (top - bottom);
01704   PN_stdfloat ypp = (yp * 2.0f) - 1.0f;
01705   
01706   int xo, yo, w, h;
01707   dr_reader.get_region_pixels_i(xo, yo, w, h);
01708   
01709   f.set(xpp, ypp);
01710   p.set(p[0] - xo, p[1] - yo);
01711   return true;
01712 }
 All Classes Functions Variables Enumerations