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