diff options
author | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-26 15:35:13 +0000 |
---|---|---|
committer | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-26 15:35:13 +0000 |
commit | e959f0b97dba7228c76b7722cf4227fdf6416524 (patch) | |
tree | 7af84f30c8a3560c19e17f7cb1dc220e1e4fa0be /views | |
parent | bf6a46bed6770a7041c222e4505135bd30701576 (diff) | |
download | chromium_src-e959f0b97dba7228c76b7722cf4227fdf6416524.zip chromium_src-e959f0b97dba7228c76b7722cf4227fdf6416524.tar.gz chromium_src-e959f0b97dba7228c76b7722cf4227fdf6416524.tar.bz2 |
Makes it so that when a folder is open on the bookmark bar and the
mouse moves over another folder, the menu for that folder is shown.
BUG=355
TEST=thorougly test all possible permutations of bookmark menus you
can think of, including drag and drop to the menus.
Review URL: http://codereview.chromium.org/328012
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@30049 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views')
-rw-r--r-- | views/controls/button/menu_button.cc | 1 | ||||
-rw-r--r-- | views/controls/menu/menu_controller.cc | 117 | ||||
-rw-r--r-- | views/controls/menu/menu_controller.h | 11 | ||||
-rw-r--r-- | views/controls/menu/menu_delegate.h | 16 | ||||
-rw-r--r-- | views/controls/menu/menu_host_gtk.cc | 69 | ||||
-rw-r--r-- | views/controls/menu/menu_host_gtk.h | 1 | ||||
-rw-r--r-- | views/controls/menu/menu_host_win.cc | 19 | ||||
-rw-r--r-- | views/controls/menu/menu_host_win.h | 1 | ||||
-rw-r--r-- | views/controls/menu/menu_item_view.cc | 25 | ||||
-rw-r--r-- | views/controls/menu/menu_item_view.h | 2 | ||||
-rw-r--r-- | views/controls/menu/submenu_view.cc | 2 | ||||
-rw-r--r-- | views/screen.h | 3 | ||||
-rw-r--r-- | views/screen_gtk.cc | 14 | ||||
-rw-r--r-- | views/screen_win.cc | 5 |
14 files changed, 202 insertions, 84 deletions
diff --git a/views/controls/button/menu_button.cc b/views/controls/button/menu_button.cc index 5dc59cb..e457304 100644 --- a/views/controls/button/menu_button.cc +++ b/views/controls/button/menu_button.cc @@ -47,7 +47,6 @@ MenuButton::MenuButton(ButtonListener* listener, bool show_menu_marker) : TextButton(listener, text), menu_visible_(false), - menu_closed_time_(), menu_delegate_(menu_delegate), show_menu_marker_(show_menu_marker) { if (kMenuMarker == NULL) { diff --git a/views/controls/menu/menu_controller.cc b/views/controls/menu/menu_controller.cc index 6175e62..4388d91 100644 --- a/views/controls/menu/menu_controller.cc +++ b/views/controls/menu/menu_controller.cc @@ -9,6 +9,7 @@ #include "app/os_exchange_data.h" #include "base/keyboard_codes.h" #include "base/time.h" +#include "views/controls/button/menu_button.h" #include "views/controls/menu/menu_scroll_view_container.h" #include "views/controls/menu/submenu_view.h" #include "views/drag_utils.h" @@ -143,6 +144,7 @@ static int nested_depth = 0; #endif MenuItemView* MenuController::Run(gfx::NativeWindow parent, + MenuButton* button, MenuItemView* root, const gfx::Rect& bounds, MenuItemView::AnchorPosition position, @@ -168,19 +170,9 @@ MenuItemView* MenuController::Run(gfx::NativeWindow parent, // Reset current state. pending_state_ = State(); state_ = State(); - pending_state_.initial_bounds = bounds; - if (bounds.height() > 1) { - // Inset the bounds slightly, otherwise drag coordinates don't line up - // nicely and menus close prematurely. - pending_state_.initial_bounds.Inset(0, 1); - } - pending_state_.anchor = position; - owner_ = parent; + UpdateInitialLocation(bounds, position); - // Calculate the bounds of the monitor we'll show menus on. Do this once to - // avoid repeated system queries for the info. - pending_state_.monitor_bounds = Screen::GetMonitorAreaNearestPoint( - bounds.origin()); + owner_ = parent; // Set the selection, which opens the initial menu. SetSelection(root, true, true); @@ -190,6 +182,8 @@ MenuItemView* MenuController::Run(gfx::NativeWindow parent, // notification when the drag has finished. StartCancelAllTimer(); return NULL; + } else if (button) { + menu_button_ = button; } #ifdef DEBUG_MENU @@ -245,6 +239,11 @@ MenuItemView* MenuController::Run(gfx::NativeWindow parent, exit_all_ = true; } + if (menu_button_) { + menu_button_->SetState(CustomButton::BS_NORMAL); + menu_button_->SchedulePaint(); + } + return result; } @@ -316,8 +315,7 @@ void MenuController::OnMousePressed(SubmenuView* source, if (!blocking_run_) return; - MenuPart part = - GetMenuPartByScreenCoordinate(source, event.x(), event.y()); + MenuPart part = GetMenuPartByScreenCoordinate(source, event.x(), event.y()); if (part.is_scroll()) return; // Ignore presses on scroll buttons. @@ -362,8 +360,7 @@ void MenuController::OnMouseDragged(SubmenuView* source, #ifdef DEBUG_MENU DLOG(INFO) << "OnMouseDragged source=" << source; #endif - MenuPart part = - GetMenuPartByScreenCoordinate(source, event.x(), event.y()); + MenuPart part = GetMenuPartByScreenCoordinate(source, event.x(), event.y()); UpdateScrolling(part); if (!blocking_run_) @@ -409,6 +406,8 @@ void MenuController::OnMouseDragged(SubmenuView* source, if (!part.menu) part.menu = source->GetMenuItem(); SetSelection(part.menu ? part.menu : state_.item, true, false); + } else if (part.type == MenuPart::NONE) { + ShowSiblingMenu(source, event); } } @@ -423,8 +422,7 @@ void MenuController::OnMouseReleased(SubmenuView* source, DCHECK(state_.item); possible_drag_ = false; DCHECK(blocking_run_); - MenuPart part = - GetMenuPartByScreenCoordinate(source, event.x(), event.y()); + MenuPart part = GetMenuPartByScreenCoordinate(source, event.x(), event.y()); if (event.IsRightMouseButton() && (part.type == MenuPart::MENU_ITEM && part.menu)) { // Set the selection immediately, making sure the submenu is only open @@ -460,14 +458,16 @@ void MenuController::OnMouseMoved(SubmenuView* source, if (showing_submenu_) return; - MenuPart part = - GetMenuPartByScreenCoordinate(source, event.x(), event.y()); + MenuPart part = GetMenuPartByScreenCoordinate(source, event.x(), event.y()); UpdateScrolling(part); if (!blocking_run_) return; + if (part.type == MenuPart::NONE && ShowSiblingMenu(source, event)) + return; + if (part.type == MenuPart::MENU_ITEM && part.menu) { SetSelection(part.menu, true, false); } else if (!part.is_scroll() && pending_state_.item && @@ -803,7 +803,8 @@ MenuController::MenuController(bool blocking) owner_(NULL), possible_drag_(false), valid_drop_coordinates_(false), - showing_submenu_(false) { + showing_submenu_(false), + menu_button_(NULL) { #ifdef DEBUG_MENU instance_count++; DLOG(INFO) << "created MC, count=" << instance_count; @@ -820,6 +821,23 @@ MenuController::~MenuController() { #endif } +void MenuController::UpdateInitialLocation( + const gfx::Rect& bounds, + MenuItemView::AnchorPosition position) { + pending_state_.initial_bounds = bounds; + if (bounds.height() > 1) { + // Inset the bounds slightly, otherwise drag coordinates don't line up + // nicely and menus close prematurely. + pending_state_.initial_bounds.Inset(0, 1); + } + pending_state_.anchor = position; + + // Calculate the bounds of the monitor we'll show menus on. Do this once to + // avoid repeated system queries for the info. + pending_state_.monitor_bounds = Screen::GetMonitorAreaNearestPoint( + bounds.origin()); +} + void MenuController::Accept(MenuItemView* item, int mouse_event_flags) { DCHECK(IsBlockingRun()); result_ = item; @@ -827,6 +845,63 @@ void MenuController::Accept(MenuItemView* item, int mouse_event_flags) { result_mouse_event_flags_ = mouse_event_flags; } +bool MenuController::ShowSiblingMenu(SubmenuView* source, const MouseEvent& e) { + if (!menu_stack_.empty() || !menu_button_) + return false; + + View* source_view = source->GetScrollViewContainer(); + if (e.x() >= 0 && e.x() < source_view->width() && e.y() >= 0 && + e.y() < source_view->height()) { + // The mouse is over the menu, no need to continue. + return false; + } + + gfx::NativeWindow window_under_mouse = Screen::GetWindowAtCursorScreenPoint(); + if (window_under_mouse != owner_) + return false; + + // The user moved the mouse outside the menu and over the owning window. See + // if there is a sibling menu we should show. + gfx::Point screen_point(e.location()); + View::ConvertPointToScreen(source_view, &screen_point); + MenuItemView::AnchorPosition anchor; + bool has_mnemonics; + MenuButton* button = NULL; + MenuItemView* alt_menu = source->GetMenuItem()->GetDelegate()-> + GetSiblingMenu(source->GetMenuItem()->GetRootMenuItem(), + screen_point, &anchor, &has_mnemonics, &button); + if (!alt_menu || alt_menu == state_.item) + return false; + + if (!button) { + // If the delegate returns a menu, they must also return a button. + NOTREACHED(); + return false; + } + + // There is a sibling menu, update the button state, hide the current menu + // and show the new one. + menu_button_->SetState(CustomButton::BS_NORMAL); + menu_button_->SchedulePaint(); + menu_button_ = button; + menu_button_->SetState(CustomButton::BS_PUSHED); + menu_button_->SchedulePaint(); + + // Need to reset capture when we show the menu again, otherwise we aren't + // going to get any events. + did_capture_ = false; + gfx::Point screen_menu_loc; + View::ConvertPointToScreen(button, &screen_menu_loc); + // Subtract 1 from the height to make the popup flush with the button border. + UpdateInitialLocation(gfx::Rect(screen_menu_loc.x(), screen_menu_loc.y(), + button->width(), button->height() - 1), + anchor); + alt_menu->PrepareForRun(has_mnemonics); + alt_menu->controller_ = this; + SetSelection(alt_menu, true, true); + return true; +} + void MenuController::CloseAllNestedMenus() { for (std::list<State>::iterator i = menu_stack_.begin(); i != menu_stack_.end(); ++i) { diff --git a/views/controls/menu/menu_controller.h b/views/controls/menu/menu_controller.h index bc57036..2ef1bb7 100644 --- a/views/controls/menu/menu_controller.h +++ b/views/controls/menu/menu_controller.h @@ -22,6 +22,7 @@ class OSExchangeData; namespace views { class DropTargetEvent; +class MenuButton; class MenuHostRootView; class MouseEvent; class SubmenuView; @@ -44,6 +45,7 @@ class MenuController : public MessageLoopForUI::Dispatcher { // block, the selected item is returned. If the menu does not block this // returns NULL immediately. MenuItemView* Run(gfx::NativeWindow parent, + MenuButton* button, MenuItemView* root, const gfx::Rect& bounds, MenuItemView::AnchorPosition position, @@ -182,10 +184,15 @@ class MenuController : public MessageLoopForUI::Dispatcher { ~MenuController(); + void UpdateInitialLocation(const gfx::Rect& bounds, + MenuItemView::AnchorPosition position); + // Invoked when the user accepts the selected item. This is only used // when blocking. This schedules the loop to quit. void Accept(MenuItemView* item, int mouse_event_flags); + bool ShowSiblingMenu(SubmenuView* source, const MouseEvent& e); + // Closes all menus, including any menus of nested invocations of Run. void CloseAllNestedMenus(); @@ -330,7 +337,7 @@ class MenuController : public MessageLoopForUI::Dispatcher { MenuItemView* result_; // The mouse event flags when the user clicked on a menu. Is 0 if the - // user did not use the mousee to select the menu. + // user did not use the mouse to select the menu. int result_mouse_event_flags_; // If not empty, it means we're nested. When Run is invoked from within @@ -376,6 +383,8 @@ class MenuController : public MessageLoopForUI::Dispatcher { // underway. scoped_ptr<MenuScrollTask> scroll_task_; + MenuButton* menu_button_; + DISALLOW_COPY_AND_ASSIGN(MenuController); }; diff --git a/views/controls/menu/menu_delegate.h b/views/controls/menu/menu_delegate.h index ec66a4f..2f7cd66 100644 --- a/views/controls/menu/menu_delegate.h +++ b/views/controls/menu/menu_delegate.h @@ -12,12 +12,13 @@ #include "app/os_exchange_data.h" #include "base/logging.h" #include "views/controls/menu/controller.h" +#include "views/controls/menu/menu_item_view.h" #include "views/event.h" namespace views { class DropTargetEvent; -class MenuItemView; +class MenuButton; // MenuDelegate -------------------------------------------------------------- @@ -180,6 +181,19 @@ class MenuDelegate : Controller { // Notification that the user has highlighted the specified item. virtual void SelectionChanged(MenuItemView* menu) { } + + // If the user drags the mouse outside the bounds of the menu the delegate + // is queried for a sibling menu to show. If this returns non-null the + // current menu is hidden, and the menu returned from this method is shown. + // + // The delegate owns the returned menu, not the controller. + virtual MenuItemView* GetSiblingMenu(MenuItemView* menu, + const gfx::Point& screen_point, + MenuItemView::AnchorPosition* anchor, + bool* has_mnemonics, + MenuButton** button) { + return NULL; + } }; } // namespace views diff --git a/views/controls/menu/menu_host_gtk.cc b/views/controls/menu/menu_host_gtk.cc index 11033c4..38945e5 100644 --- a/views/controls/menu/menu_host_gtk.cc +++ b/views/controls/menu/menu_host_gtk.cc @@ -19,14 +19,13 @@ MenuHost::MenuHost(SubmenuView* submenu) closed_(false), submenu_(submenu), did_pointer_grab_(false) { - GdkModifierType current_event_mod; - if (gtk_get_current_event_state(¤t_event_mod)) { - set_mouse_down( - (current_event_mod & GDK_BUTTON1_MASK) || - (current_event_mod & GDK_BUTTON2_MASK) || - (current_event_mod & GDK_BUTTON3_MASK) || - (current_event_mod & GDK_BUTTON4_MASK) || - (current_event_mod & GDK_BUTTON5_MASK)); + GdkEvent* event = gtk_get_current_event(); + if (event) { + if (event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS || + event->type == GDK_3BUTTON_PRESS) { + set_mouse_down(true); + } + gdk_event_free(event); } } @@ -36,33 +35,9 @@ void MenuHost::Init(gfx::NativeWindow parent, bool do_capture) { WidgetGtk::Init(GTK_WIDGET(parent), bounds); SetContentsView(contents_view); - // TODO(sky): see if there is some way to show without changing focus. Show(); - if (do_capture) { - // Release the current grab. - GtkWidget* current_grab_window = gtk_grab_get_current(); - if (current_grab_window) - gtk_grab_remove(current_grab_window); - - // Make sure all app mouse events are targetted at us only. - DoGrab(); - - // And do a grab. - // NOTE: we do this to ensure we get mouse events from other apps, a grab - // done with gtk_grab_add doesn't get events from other apps. - GdkGrabStatus grab_status = - gdk_pointer_grab(window_contents()->window, FALSE, - static_cast<GdkEventMask>( - GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | - GDK_POINTER_MOTION_MASK), - NULL, NULL, GDK_CURRENT_TIME); - did_pointer_grab_ = (grab_status == GDK_GRAB_SUCCESS); - DCHECK(did_pointer_grab_); - // need keyboard grab. -#ifdef DEBUG_MENU - DLOG(INFO) << "Doing capture"; -#endif - } + if (do_capture) + DoCapture(); } gfx::NativeWindow MenuHost::GetNativeWindow() { @@ -95,6 +70,32 @@ void MenuHost::HideWindow() { WidgetGtk::Hide(); } +void MenuHost::DoCapture() { + // Release the current grab. + GtkWidget* current_grab_window = gtk_grab_get_current(); + if (current_grab_window) + gtk_grab_remove(current_grab_window); + + // Make sure all app mouse events are targetted at us only. + DoGrab(); + + // And do a grab. + // NOTE: we do this to ensure we get mouse events from other apps, a grab + // done with gtk_grab_add doesn't get events from other apps. + GdkGrabStatus grab_status = + gdk_pointer_grab(window_contents()->window, FALSE, + static_cast<GdkEventMask>( + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK), + NULL, NULL, GDK_CURRENT_TIME); + did_pointer_grab_ = (grab_status == GDK_GRAB_SUCCESS); + DCHECK(did_pointer_grab_); + // need keyboard grab. +#ifdef DEBUG_MENU + DLOG(INFO) << "Doing capture"; +#endif +} + void MenuHost::ReleaseCapture() { ReleaseGrab(); } diff --git a/views/controls/menu/menu_host_gtk.h b/views/controls/menu/menu_host_gtk.h index 8e69ce8..4ae1b77 100644 --- a/views/controls/menu/menu_host_gtk.h +++ b/views/controls/menu/menu_host_gtk.h @@ -27,6 +27,7 @@ class MenuHost : public WidgetGtk { void Show(); virtual void Hide(); virtual void HideWindow(); + void DoCapture(); void ReleaseCapture(); protected: diff --git a/views/controls/menu/menu_host_win.cc b/views/controls/menu/menu_host_win.cc index d6fd655..f2c2c2c 100644 --- a/views/controls/menu/menu_host_win.cc +++ b/views/controls/menu/menu_host_win.cc @@ -37,14 +37,8 @@ void MenuHost::Init(HWND parent, WidgetWin::Init(parent, bounds); SetContentsView(contents_view); Show(); - owns_capture_ = do_capture; - if (do_capture) { - SetCapture(); - has_capture_ = true; -#ifdef DEBUG_MENU - DLOG(INFO) << "Doing capture"; -#endif - } + if (do_capture) + DoCapture(); } void MenuHost::Show() { @@ -82,6 +76,15 @@ void MenuHost::OnCaptureChanged(HWND hwnd) { #endif } +void MenuHost::DoCapture() { + owns_capture_ = true; + SetCapture(); + has_capture_ = true; +#ifdef DEBUG_MENU + DLOG(INFO) << "Doing capture"; +#endif +} + void MenuHost::ReleaseCapture() { if (owns_capture_) { #ifdef DEBUG_MENU diff --git a/views/controls/menu/menu_host_win.h b/views/controls/menu/menu_host_win.h index 6e645d2..ec5d060 100644 --- a/views/controls/menu/menu_host_win.h +++ b/views/controls/menu/menu_host_win.h @@ -28,6 +28,7 @@ class MenuHost : public WidgetWin { virtual void Hide(); virtual void HideWindow(); virtual void OnCaptureChanged(HWND hwnd); + void DoCapture(); void ReleaseCapture(); protected: diff --git a/views/controls/menu/menu_item_view.cc b/views/controls/menu/menu_item_view.cc index 4d5b3ae..2734788 100644 --- a/views/controls/menu/menu_item_view.cc +++ b/views/controls/menu/menu_item_view.cc @@ -66,23 +66,15 @@ MenuItemView::MenuItemView(MenuDelegate* delegate) { } MenuItemView::~MenuItemView() { - if (controller_) { - // We're currently showing. - - // We can't delete ourselves while we're blocking. - DCHECK(!controller_->IsBlockingRun()); - - // Invoking Cancel is going to call us back and notify the delegate. - // Notifying the delegate from the destructor can be problematic. To avoid - // this the delegate is set to NULL. - delegate_ = NULL; - - controller_->Cancel(true); - } + // TODO(sky): ownership is bit wrong now. In particular if a nested message + // loop is running deletion can't be done, otherwise the stack gets + // thoroughly screwed. The destructor should be made private, and + // MenuController should be the only place handling deletion of the menu. delete submenu_; } void MenuItemView::RunMenuAt(gfx::NativeWindow parent, + MenuButton* button, const gfx::Rect& bounds, AnchorPosition anchor, bool has_mnemonics) { @@ -115,7 +107,7 @@ void MenuItemView::RunMenuAt(gfx::NativeWindow parent, // Run the loop. MenuItemView* result = - controller->Run(parent, this, bounds, anchor, &mouse_event_flags); + controller->Run(parent, button, this, bounds, anchor, &mouse_event_flags); RemoveEmptyMenus(); @@ -151,7 +143,7 @@ void MenuItemView::RunMenuForDropAt(gfx::NativeWindow parent, // Set the instance, that way it can be canceled by another menu. MenuController::SetActiveInstance(controller_); - controller_->Run(parent, this, bounds, anchor, NULL); + controller_->Run(parent, NULL, this, bounds, anchor, NULL); } void MenuItemView::Cancel() { @@ -337,9 +329,6 @@ void MenuItemView::PrepareForRun(bool has_mnemonics) { // Currently we only support showing the root. DCHECK(!parent_menu_item_); - // Don't invoke run from within run on the same menu. - DCHECK(!controller_); - // Force us to have a submenu. CreateSubmenu(); diff --git a/views/controls/menu/menu_item_view.h b/views/controls/menu/menu_item_view.h index ecb3b78..a85e265 100644 --- a/views/controls/menu/menu_item_view.h +++ b/views/controls/menu/menu_item_view.h @@ -10,6 +10,7 @@ namespace views { +class MenuButton; class MenuController; class MenuDelegate; class SubmenuView; @@ -82,6 +83,7 @@ class MenuItemView : public View { // whether the items have mnemonics. Mnemonics are identified by way of the // character following the '&'. void RunMenuAt(gfx::NativeWindow parent, + MenuButton* button, const gfx::Rect& bounds, AnchorPosition anchor, bool has_mnemonics); diff --git a/views/controls/menu/submenu_view.cc b/views/controls/menu/submenu_view.cc index d08de6f..3b315ef 100644 --- a/views/controls/menu/submenu_view.cc +++ b/views/controls/menu/submenu_view.cc @@ -221,6 +221,8 @@ void SubmenuView::ShowAt(gfx::NativeWindow parent, bool do_capture) { if (host_) { host_->Show(); + if (do_capture) + host_->DoCapture(); return; } diff --git a/views/screen.h b/views/screen.h index f7c023c..f95b368 100644 --- a/views/screen.h +++ b/views/screen.h @@ -27,6 +27,9 @@ class Screen { // Returns the monitor area (not the work area, but the complete bounds) of // the monitor nearest the specified point. static gfx::Rect GetMonitorAreaNearestPoint(const gfx::Point& point); + + // Returns the window under the cursor. + static gfx::NativeWindow GetWindowAtCursorScreenPoint(); }; } // namespace views diff --git a/views/screen_gtk.cc b/views/screen_gtk.cc index 9302853..a0daa19 100644 --- a/views/screen_gtk.cc +++ b/views/screen_gtk.cc @@ -58,5 +58,19 @@ gfx::Rect Screen::GetMonitorAreaNearestPoint(const gfx::Point& point) { return gfx::Rect(bounds); } +gfx::NativeWindow Screen::GetWindowAtCursorScreenPoint() { + GdkWindow* window = gdk_window_at_pointer(NULL, NULL); + if (!window) + return NULL; + + gpointer data = NULL; + gdk_window_get_user_data(window, &data); + GtkWidget* widget = reinterpret_cast<GtkWidget*>(data); + if (!widget) + return NULL; + widget = gtk_widget_get_toplevel(widget); + return GTK_IS_WINDOW(widget) ? GTK_WINDOW(widget) : NULL; +} + } // namespace diff --git a/views/screen_win.cc b/views/screen_win.cc index 82e95e6..4ddb7c1 100644 --- a/views/screen_win.cc +++ b/views/screen_win.cc @@ -46,5 +46,10 @@ gfx::Rect Screen::GetMonitorAreaNearestPoint(const gfx::Point& point) { return gfx::Rect(mi.rcMonitor); } +gfx::NativeWindow Screen::GetWindowAtCursorScreenPoint() { + POINT location; + return GetCursorPos(&location) ? WindowFromPoint(location) : NULL; +} + } // namespace |