|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #ifndef UI_VIEWS_CONTROLS_MENU_MENU_CONTROLLER_H_ | 
|  | #define UI_VIEWS_CONTROLS_MENU_MENU_CONTROLLER_H_ | 
|  |  | 
|  | #include "build/build_config.h" | 
|  |  | 
|  | #include <list> | 
|  | #include <set> | 
|  | #include <vector> | 
|  |  | 
|  | #include "base/compiler_specific.h" | 
|  | #include "base/memory/linked_ptr.h" | 
|  | #include "base/memory/scoped_ptr.h" | 
|  | #include "base/timer/timer.h" | 
|  | #include "ui/events/event.h" | 
|  | #include "ui/events/event_constants.h" | 
|  | #include "ui/events/platform/platform_event_dispatcher.h" | 
|  | #include "ui/views/controls/button/menu_button.h" | 
|  | #include "ui/views/controls/menu/menu_config.h" | 
|  | #include "ui/views/controls/menu/menu_delegate.h" | 
|  | #include "ui/views/widget/widget_observer.h" | 
|  |  | 
|  | namespace base { | 
|  | class MessagePumpDispatcher; | 
|  | } | 
|  | namespace gfx { | 
|  | class Screen; | 
|  | } | 
|  | namespace ui { | 
|  | class OSExchangeData; | 
|  | class ScopedEventDispatcher; | 
|  | } | 
|  | namespace views { | 
|  |  | 
|  | class MenuButton; | 
|  | class MenuHostRootView; | 
|  | class MenuItemView; | 
|  | class MenuMessageLoop; | 
|  | class MouseEvent; | 
|  | class SubmenuView; | 
|  | class View; | 
|  |  | 
|  | namespace internal { | 
|  | class MenuControllerDelegate; | 
|  | class MenuEventDispatcher; | 
|  | class MenuMessagePumpDispatcher; | 
|  | class MenuRunnerImpl; | 
|  | } | 
|  |  | 
|  | namespace test { | 
|  | class MenuControllerTest; | 
|  | } | 
|  |  | 
|  | // MenuController ------------------------------------------------------------- | 
|  |  | 
|  | // MenuController is used internally by the various menu classes to manage | 
|  | // showing, selecting and drag/drop for menus. All relevant events are | 
|  | // forwarded to the MenuController from SubmenuView and MenuHost. | 
|  | class VIEWS_EXPORT MenuController : public WidgetObserver { | 
|  | public: | 
|  | // Enumeration of how the menu should exit. | 
|  | enum ExitType { | 
|  | // Don't exit. | 
|  | EXIT_NONE, | 
|  |  | 
|  | // All menus, including nested, should be exited. | 
|  | EXIT_ALL, | 
|  |  | 
|  | // Only the outermost menu should be exited. | 
|  | EXIT_OUTERMOST, | 
|  |  | 
|  | // This is set if the menu is being closed as the result of one of the menus | 
|  | // being destroyed. | 
|  | EXIT_DESTROYED | 
|  | }; | 
|  |  | 
|  | // If a menu is currently active, this returns the controller for it. | 
|  | static MenuController* GetActiveInstance(); | 
|  |  | 
|  | // Runs the menu at the specified location. If the menu was configured to | 
|  | // block, the selected item is returned. If the menu does not block this | 
|  | // returns NULL immediately. | 
|  | MenuItemView* Run(Widget* parent, | 
|  | MenuButton* button, | 
|  | MenuItemView* root, | 
|  | const gfx::Rect& bounds, | 
|  | MenuAnchorPosition position, | 
|  | bool context_menu, | 
|  | bool is_nested_drag, | 
|  | int* event_flags); | 
|  |  | 
|  | // Whether or not Run blocks. | 
|  | bool IsBlockingRun() const { return blocking_run_; } | 
|  |  | 
|  | bool in_nested_run() const { return !menu_stack_.empty(); } | 
|  |  | 
|  | // Whether or not drag operation is in progress. | 
|  | bool drag_in_progress() const { return drag_in_progress_; } | 
|  |  | 
|  | // Whether the MenuController initiated the drag in progress. False if there | 
|  | // is no drag in progress. | 
|  | bool did_initiate_drag() const { return did_initiate_drag_; } | 
|  |  | 
|  | // Returns the owner of child windows. | 
|  | // WARNING: this may be NULL. | 
|  | Widget* owner() { return owner_; } | 
|  |  | 
|  | // Get the anchor position wich is used to show this menu. | 
|  | MenuAnchorPosition GetAnchorPosition() { return state_.anchor; } | 
|  |  | 
|  | // Cancels the current Run. See ExitType for a description of what happens | 
|  | // with the various parameters. | 
|  | void Cancel(ExitType type); | 
|  |  | 
|  | // An alternative to Cancel(EXIT_ALL) that can be used with a OneShotTimer. | 
|  | void CancelAll() { Cancel(EXIT_ALL); } | 
|  |  | 
|  | // When is_nested_run() this will add a delegate to the stack. The most recent | 
|  | // delegate will be notified. It will be removed upon the exiting of the | 
|  | // nested menu. Ownership is not taken. | 
|  | void AddNestedDelegate(internal::MenuControllerDelegate* delegate); | 
|  |  | 
|  | // Sets whether the subsequent call to Run is asynchronous. When nesting calls | 
|  | // to Run, if a new MenuControllerDelegate has been nested, the previous | 
|  | // asynchronous state will be reapplied once nesting has ended. | 
|  | void SetAsyncRun(bool is_async); | 
|  |  | 
|  | // Returns the current exit type. This returns a value other than EXIT_NONE if | 
|  | // the menu is being canceled. | 
|  | ExitType exit_type() const { return exit_type_; } | 
|  |  | 
|  | // Returns the time from the event which closed the menu - or 0. | 
|  | base::TimeDelta closing_event_time() const { return closing_event_time_; } | 
|  |  | 
|  | void set_is_combobox(bool is_combobox) { is_combobox_ = is_combobox; } | 
|  |  | 
|  | // Various events, forwarded from the submenu. | 
|  | // | 
|  | // NOTE: the coordinates of the events are in that of the | 
|  | // MenuScrollViewContainer. | 
|  | bool OnMousePressed(SubmenuView* source, const ui::MouseEvent& event); | 
|  | bool OnMouseDragged(SubmenuView* source, const ui::MouseEvent& event); | 
|  | void OnMouseReleased(SubmenuView* source, const ui::MouseEvent& event); | 
|  | void OnMouseMoved(SubmenuView* source, const ui::MouseEvent& event); | 
|  | void OnMouseEntered(SubmenuView* source, const ui::MouseEvent& event); | 
|  | bool OnMouseWheel(SubmenuView* source, const ui::MouseWheelEvent& event); | 
|  | void OnGestureEvent(SubmenuView* source, ui::GestureEvent* event); | 
|  | View* GetTooltipHandlerForPoint(SubmenuView* source, const gfx::Point& point); | 
|  | void ViewHierarchyChanged(SubmenuView* source, | 
|  | const View::ViewHierarchyChangedDetails& details); | 
|  |  | 
|  | bool GetDropFormats(SubmenuView* source, | 
|  | int* formats, | 
|  | std::set<ui::Clipboard::FormatType>* format_types); | 
|  | bool AreDropTypesRequired(SubmenuView* source); | 
|  | bool CanDrop(SubmenuView* source, const ui::OSExchangeData& data); | 
|  | void OnDragEntered(SubmenuView* source, const ui::DropTargetEvent& event); | 
|  | int OnDragUpdated(SubmenuView* source, const ui::DropTargetEvent& event); | 
|  | void OnDragExited(SubmenuView* source); | 
|  | int OnPerformDrop(SubmenuView* source, const ui::DropTargetEvent& event); | 
|  |  | 
|  | // Invoked from the scroll buttons of the MenuScrollViewContainer. | 
|  | void OnDragEnteredScrollButton(SubmenuView* source, bool is_up); | 
|  | void OnDragExitedScrollButton(SubmenuView* source); | 
|  |  | 
|  | // Called by the Widget when a drag is about to start on a child view. This | 
|  | // could be initiated by one of our MenuItemViews, or could be through another | 
|  | // child View. | 
|  | void OnDragWillStart(); | 
|  |  | 
|  | // Called by the Widget when the drag has completed. |should_close| | 
|  | // corresponds to whether or not the menu should close. | 
|  | void OnDragComplete(bool should_close); | 
|  |  | 
|  | // Called while dispatching messages to intercept key events. | 
|  | // If |character| is other than 0, it is handled as a mnemonic. | 
|  | // Otherwise, |key_code| is handled as a menu navigation command. | 
|  | // Returns ui::POST_DISPATCH_NONE if the event was swallowed by the menu. | 
|  | ui::PostDispatchAction OnWillDispatchKeyEvent(base::char16 character, | 
|  | ui::KeyboardCode key_code); | 
|  |  | 
|  | // Update the submenu's selection based on the current mouse location | 
|  | void UpdateSubmenuSelection(SubmenuView* source); | 
|  |  | 
|  | // WidgetObserver overrides: | 
|  | void OnWidgetDestroying(Widget* widget) override; | 
|  |  | 
|  | // Only used for testing. | 
|  | bool IsCancelAllTimerRunningForTest(); | 
|  |  | 
|  | // Only used for testing. | 
|  | static void TurnOffMenuSelectionHoldForTest(); | 
|  |  | 
|  | private: | 
|  | friend class internal::MenuEventDispatcher; | 
|  | friend class internal::MenuMessagePumpDispatcher; | 
|  | friend class internal::MenuRunnerImpl; | 
|  | friend class test::MenuControllerTest; | 
|  | friend class MenuKeyEventHandler; | 
|  | friend class MenuHostRootView; | 
|  | friend class MenuItemView; | 
|  | friend class SubmenuView; | 
|  |  | 
|  | class MenuScrollTask; | 
|  |  | 
|  | struct SelectByCharDetails; | 
|  |  | 
|  | // Values supplied to SetSelection. | 
|  | enum SetSelectionTypes { | 
|  | SELECTION_DEFAULT               = 0, | 
|  |  | 
|  | // If set submenus are opened immediately, otherwise submenus are only | 
|  | // openned after a timer fires. | 
|  | SELECTION_UPDATE_IMMEDIATELY    = 1 << 0, | 
|  |  | 
|  | // If set and the menu_item has a submenu, the submenu is shown. | 
|  | SELECTION_OPEN_SUBMENU          = 1 << 1, | 
|  |  | 
|  | // SetSelection is being invoked as the result exiting or cancelling the | 
|  | // menu. This is used for debugging. | 
|  | SELECTION_EXIT                  = 1 << 2, | 
|  | }; | 
|  |  | 
|  | // Direction for IncrementSelection and FindInitialSelectableMenuItem. | 
|  | enum SelectionIncrementDirectionType { | 
|  | // Navigate the menu up. | 
|  | INCREMENT_SELECTION_UP, | 
|  |  | 
|  | // Navigate the menu down. | 
|  | INCREMENT_SELECTION_DOWN, | 
|  | }; | 
|  |  | 
|  | // Tracks selection information. | 
|  | struct State { | 
|  | State(); | 
|  | ~State(); | 
|  |  | 
|  | // The selected menu item. | 
|  | MenuItemView* item; | 
|  |  | 
|  | // If item has a submenu this indicates if the submenu is showing. | 
|  | bool submenu_open; | 
|  |  | 
|  | // Bounds passed to the run menu. Used for positioning the first menu. | 
|  | gfx::Rect initial_bounds; | 
|  |  | 
|  | // Position of the initial menu. | 
|  | MenuAnchorPosition anchor; | 
|  |  | 
|  | // The direction child menus have opened in. | 
|  | std::list<bool> open_leading; | 
|  |  | 
|  | // Bounds for the monitor we're showing on. | 
|  | gfx::Rect monitor_bounds; | 
|  |  | 
|  | // Is the current menu a context menu. | 
|  | bool context_menu; | 
|  | }; | 
|  |  | 
|  | // Used by GetMenuPart to indicate the menu part at a particular location. | 
|  | struct MenuPart { | 
|  | // Type of part. | 
|  | enum Type { | 
|  | NONE, | 
|  | MENU_ITEM, | 
|  | SCROLL_UP, | 
|  | SCROLL_DOWN | 
|  | }; | 
|  |  | 
|  | MenuPart() : type(NONE), menu(NULL), parent(NULL), submenu(NULL) {} | 
|  |  | 
|  | // Convenience for testing type == SCROLL_DOWN or type == SCROLL_UP. | 
|  | bool is_scroll() const { return type == SCROLL_DOWN || type == SCROLL_UP; } | 
|  |  | 
|  | // Type of part. | 
|  | Type type; | 
|  |  | 
|  | // If type is MENU_ITEM, this is the menu item the mouse is over, otherwise | 
|  | // this is NULL. | 
|  | // NOTE: if type is MENU_ITEM and the mouse is not over a valid menu item | 
|  | //       but is over a menu (for example, the mouse is over a separator or | 
|  | //       empty menu), this is NULL and parent is the menu the mouse was | 
|  | //       clicked on. | 
|  | MenuItemView* menu; | 
|  |  | 
|  | // If type is MENU_ITEM but the mouse is not over a menu item this is the | 
|  | // parent of the menu item the user clicked on. Otherwise this is NULL. | 
|  | MenuItemView* parent; | 
|  |  | 
|  | // This is the submenu the mouse is over. | 
|  | SubmenuView* submenu; | 
|  | }; | 
|  |  | 
|  | // Sets the selection to |menu_item|. A value of NULL unselects | 
|  | // everything. |types| is a bitmask of |SetSelectionTypes|. | 
|  | // | 
|  | // Internally this updates pending_state_ immediatley. state_ is only updated | 
|  | // immediately if SELECTION_UPDATE_IMMEDIATELY is set. If | 
|  | // SELECTION_UPDATE_IMMEDIATELY is not set CommitPendingSelection is invoked | 
|  | // to show/hide submenus and update state_. | 
|  | void SetSelection(MenuItemView* menu_item, int types); | 
|  |  | 
|  | void SetSelectionOnPointerDown(SubmenuView* source, | 
|  | const ui::LocatedEvent& event); | 
|  | void StartDrag(SubmenuView* source, const gfx::Point& location); | 
|  |  | 
|  | // Key processing. | 
|  | void OnKeyDown(ui::KeyboardCode key_code); | 
|  |  | 
|  | // Creates a MenuController. If |blocking| is true a nested message loop is | 
|  | // started in |Run|. | 
|  | MenuController(bool blocking, | 
|  | internal::MenuControllerDelegate* delegate); | 
|  |  | 
|  | ~MenuController() override; | 
|  |  | 
|  | // Runs the platform specific bits of the message loop. If |nested_menu| is | 
|  | // true we're being asked to run a menu from within a menu (eg a context | 
|  | // menu). | 
|  | void RunMessageLoop(bool nested_menu); | 
|  |  | 
|  | // Invokes AcceleratorPressed() on the hot tracked view if there is one. | 
|  | // Returns true if AcceleratorPressed() was invoked. | 
|  | bool SendAcceleratorToHotTrackedView(); | 
|  |  | 
|  | void UpdateInitialLocation(const gfx::Rect& bounds, | 
|  | MenuAnchorPosition position, | 
|  | bool context_menu); | 
|  |  | 
|  | // 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 event_flags); | 
|  |  | 
|  | bool ShowSiblingMenu(SubmenuView* source, const gfx::Point& mouse_location); | 
|  |  | 
|  | // Shows a context menu for |menu_item| as a result of an event if | 
|  | // appropriate, using the given |screen_location|. This is invoked on long | 
|  | // press, releasing the right mouse button, and pressing the "app" key. | 
|  | // Returns whether a context menu was shown. | 
|  | bool ShowContextMenu(MenuItemView* menu_item, | 
|  | const gfx::Point& screen_location, | 
|  | ui::MenuSourceType source_type); | 
|  |  | 
|  | // Closes all menus, including any menus of nested invocations of Run. | 
|  | void CloseAllNestedMenus(); | 
|  |  | 
|  | // Gets the enabled menu item at the specified location. | 
|  | // If over_any_menu is non-null it is set to indicate whether the location | 
|  | // is over any menu. It is possible for this to return NULL, but | 
|  | // over_any_menu to be true. For example, the user clicked on a separator. | 
|  | MenuItemView* GetMenuItemAt(View* menu, int x, int y); | 
|  |  | 
|  | // If there is an empty menu item at the specified location, it is returned. | 
|  | MenuItemView* GetEmptyMenuItemAt(View* source, int x, int y); | 
|  |  | 
|  | // Returns true if the coordinate is over the scroll buttons of the | 
|  | // SubmenuView's MenuScrollViewContainer. If true is returned, part is set to | 
|  | // indicate which scroll button the coordinate is. | 
|  | bool IsScrollButtonAt(SubmenuView* source, | 
|  | int x, | 
|  | int y, | 
|  | MenuPart::Type* part); | 
|  |  | 
|  | // Returns the target for the mouse event. The coordinates are in terms of | 
|  | // source's scroll view container. | 
|  | MenuPart GetMenuPart(SubmenuView* source, const gfx::Point& source_loc); | 
|  |  | 
|  | // Returns the target for mouse events. The search is done through |item| and | 
|  | // all its parents. | 
|  | MenuPart GetMenuPartByScreenCoordinateUsingMenu(MenuItemView* item, | 
|  | const gfx::Point& screen_loc); | 
|  |  | 
|  | // Implementation of GetMenuPartByScreenCoordinate for a single menu. Returns | 
|  | // true if the supplied SubmenuView contains the location in terms of the | 
|  | // screen. If it does, part is set appropriately and true is returned. | 
|  | bool GetMenuPartByScreenCoordinateImpl(SubmenuView* menu, | 
|  | const gfx::Point& screen_loc, | 
|  | MenuPart* part); | 
|  |  | 
|  | // Returns the RootView of the target for the mouse event, if there is a | 
|  | // target at |source_loc|. | 
|  | MenuHostRootView* GetRootView(SubmenuView* source, | 
|  | const gfx::Point& source_loc); | 
|  |  | 
|  | // Converts the located event from |source|'s geometry to |dst|'s geometry, | 
|  | // iff the root view of source and dst differ. | 
|  | void ConvertLocatedEventForRootView(View* source, | 
|  | View* dst, | 
|  | ui::LocatedEvent* event); | 
|  |  | 
|  | // Returns true if the SubmenuView contains the specified location. This does | 
|  | // NOT included the scroll buttons, only the submenu view. | 
|  | bool DoesSubmenuContainLocation(SubmenuView* submenu, | 
|  | const gfx::Point& screen_loc); | 
|  |  | 
|  | // Opens/Closes the necessary menus such that state_ matches that of | 
|  | // pending_state_. This is invoked if submenus are not opened immediately, | 
|  | // but after a delay. | 
|  | void CommitPendingSelection(); | 
|  |  | 
|  | // If item has a submenu, it is closed. This does NOT update the selection | 
|  | // in anyway. | 
|  | void CloseMenu(MenuItemView* item); | 
|  |  | 
|  | // If item has a submenu, it is opened. This does NOT update the selection | 
|  | // in anyway. | 
|  | void OpenMenu(MenuItemView* item); | 
|  |  | 
|  | // Implementation of OpenMenu. If |show| is true, this invokes show on the | 
|  | // menu, otherwise Reposition is invoked. | 
|  | void OpenMenuImpl(MenuItemView* item, bool show); | 
|  |  | 
|  | // Invoked when the children of a menu change and the menu is showing. | 
|  | // This closes any submenus and resizes the submenu. | 
|  | void MenuChildrenChanged(MenuItemView* item); | 
|  |  | 
|  | // Builds the paths of the two menu items into the two paths, and | 
|  | // sets first_diff_at to the location of the first difference between the | 
|  | // two paths. | 
|  | void BuildPathsAndCalculateDiff(MenuItemView* old_item, | 
|  | MenuItemView* new_item, | 
|  | std::vector<MenuItemView*>* old_path, | 
|  | std::vector<MenuItemView*>* new_path, | 
|  | size_t* first_diff_at); | 
|  |  | 
|  | // Builds the path for the specified item. | 
|  | void BuildMenuItemPath(MenuItemView* item, std::vector<MenuItemView*>* path); | 
|  |  | 
|  | // Starts/stops the timer that commits the pending state to state | 
|  | // (opens/closes submenus). | 
|  | void StartShowTimer(); | 
|  | void StopShowTimer(); | 
|  |  | 
|  | // Starts/stops the timer cancel the menu. This is used during drag and | 
|  | // drop when the drop enters/exits the menu. | 
|  | void StartCancelAllTimer(); | 
|  | void StopCancelAllTimer(); | 
|  |  | 
|  | // Calculates the bounds of the menu to show. is_leading is set to match the | 
|  | // direction the menu opened in. | 
|  | gfx::Rect CalculateMenuBounds(MenuItemView* item, | 
|  | bool prefer_leading, | 
|  | bool* is_leading); | 
|  |  | 
|  | // Calculates the bubble bounds of the menu to show. is_leading is set to | 
|  | // match the direction the menu opened in. | 
|  | gfx::Rect CalculateBubbleMenuBounds(MenuItemView* item, | 
|  | bool prefer_leading, | 
|  | bool* is_leading); | 
|  |  | 
|  | // Returns the depth of the menu. | 
|  | static int MenuDepth(MenuItemView* item); | 
|  |  | 
|  | // Selects the next or previous (depending on |direction|) menu item. | 
|  | void IncrementSelection(SelectionIncrementDirectionType direction); | 
|  |  | 
|  | // Returns the first (|direction| == NAVIGATE_SELECTION_DOWN) or the last | 
|  | // (|direction| == INCREMENT_SELECTION_UP) selectable child menu item of | 
|  | // |parent|. If there are no selectable items returns NULL. | 
|  | MenuItemView* FindInitialSelectableMenuItem( | 
|  | MenuItemView* parent, | 
|  | SelectionIncrementDirectionType direction); | 
|  |  | 
|  | // Returns the next or previous selectable child menu item of |parent| | 
|  | // starting at |index| and incrementing or decrementing index by 1 depending | 
|  | // on |direction|. If there are no more selectable items NULL is returned. | 
|  | MenuItemView* FindNextSelectableMenuItem( | 
|  | MenuItemView* parent, | 
|  | int index, | 
|  | SelectionIncrementDirectionType direction); | 
|  |  | 
|  | // If the selected item has a submenu and it isn't currently open, the | 
|  | // the selection is changed such that the menu opens immediately. | 
|  | void OpenSubmenuChangeSelectionIfCan(); | 
|  |  | 
|  | // If possible, closes the submenu. | 
|  | void CloseSubmenu(); | 
|  |  | 
|  | // Returns details about which menu items match the mnemonic |key|. | 
|  | // |match_function| is used to determine which menus match. | 
|  | SelectByCharDetails FindChildForMnemonic( | 
|  | MenuItemView* parent, | 
|  | base::char16 key, | 
|  | bool (*match_function)(MenuItemView* menu, base::char16 mnemonic)); | 
|  |  | 
|  | // Selects or accepts the appropriate menu item based on |details|. | 
|  | void AcceptOrSelect(MenuItemView* parent, const SelectByCharDetails& details); | 
|  |  | 
|  | // Selects by mnemonic, and if that doesn't work tries the first character of | 
|  | // the title. | 
|  | void SelectByChar(base::char16 key); | 
|  |  | 
|  | // For Windows and Aura we repost an event for some events that dismiss | 
|  | // the context menu. The event is then reprocessed to cause its result | 
|  | // if the context menu had not been present. | 
|  | // On non-aura Windows, a new mouse event is generated and posted to | 
|  | // the window (if there is one) at the location of the event. On | 
|  | // aura, the event is reposted on the RootWindow. | 
|  | void RepostEvent(SubmenuView* source, const ui::LocatedEvent& event); | 
|  |  | 
|  | // Sets the drop target to new_item. | 
|  | void SetDropMenuItem(MenuItemView* new_item, | 
|  | MenuDelegate::DropPosition position); | 
|  |  | 
|  | // Starts/stops scrolling as appropriate. part gives the part the mouse is | 
|  | // over. | 
|  | void UpdateScrolling(const MenuPart& part); | 
|  |  | 
|  | // Stops scrolling. | 
|  | void StopScrolling(); | 
|  |  | 
|  | // Updates active mouse view from the location of the event and sends it | 
|  | // the appropriate events. This is used to send mouse events to child views so | 
|  | // that they react to click-drag-release as if the user clicked on the view | 
|  | // itself. | 
|  | void UpdateActiveMouseView(SubmenuView* event_source, | 
|  | const ui::MouseEvent& event, | 
|  | View* target_menu); | 
|  |  | 
|  | // Sends a mouse release event to the current active mouse view and sets | 
|  | // it to null. | 
|  | void SendMouseReleaseToActiveView(SubmenuView* event_source, | 
|  | const ui::MouseEvent& event); | 
|  |  | 
|  | // Sends a mouse capture lost event to the current active mouse view and sets | 
|  | // it to null. | 
|  | void SendMouseCaptureLostToActiveView(); | 
|  |  | 
|  | // Sets/gets the active mouse view. See UpdateActiveMouseView() for details. | 
|  | void SetActiveMouseView(View* view); | 
|  | View* GetActiveMouseView(); | 
|  |  | 
|  | // Sets exit type. Calling this can terminate the active nested message-loop. | 
|  | void SetExitType(ExitType type); | 
|  |  | 
|  | // Terminates the current nested message-loop. | 
|  | void TerminateNestedMessageLoop(); | 
|  |  | 
|  | // Performs the teardown of the menu launched by Run(). The selected item is | 
|  | // returned. | 
|  | MenuItemView* ExitMenuRun(); | 
|  |  | 
|  | // Handles the mouse location event on the submenu |source|. | 
|  | void HandleMouseLocation(SubmenuView* source, | 
|  | const gfx::Point& mouse_location); | 
|  |  | 
|  | // Retrieve an appropriate Screen. | 
|  | gfx::Screen* GetScreen(); | 
|  |  | 
|  | // The active instance. | 
|  | static MenuController* active_instance_; | 
|  |  | 
|  | // If true, Run blocks. If false, Run doesn't block and this is used for | 
|  | // drag and drop. Note that the semantics for drag and drop are slightly | 
|  | // different: cancel timer is kicked off any time the drag moves outside the | 
|  | // menu, mouse events do nothing... | 
|  | bool blocking_run_; | 
|  |  | 
|  | // If true, we're showing. | 
|  | bool showing_; | 
|  |  | 
|  | // Indicates what to exit. | 
|  | ExitType exit_type_; | 
|  |  | 
|  | // Whether we did a capture. We do a capture only if we're blocking and | 
|  | // the mouse was down when Run. | 
|  | bool did_capture_; | 
|  |  | 
|  | // As the user drags the mouse around pending_state_ changes immediately. | 
|  | // When the user stops moving/dragging the mouse (or clicks the mouse) | 
|  | // pending_state_ is committed to state_, potentially resulting in | 
|  | // opening or closing submenus. This gives a slight delayed effect to | 
|  | // submenus as the user moves the mouse around. This is done so that as the | 
|  | // user moves the mouse all submenus don't immediately pop. | 
|  | State pending_state_; | 
|  | State state_; | 
|  |  | 
|  | // If the user accepted the selection, this is the result. | 
|  | MenuItemView* result_; | 
|  |  | 
|  | // The event flags when the user selected the menu. | 
|  | int accept_event_flags_; | 
|  |  | 
|  | // If not empty, it means we're nested. When Run is invoked from within | 
|  | // Run, the current state (state_) is pushed onto menu_stack_. This allows | 
|  | // MenuController to restore the state when the nested run returns. | 
|  | typedef std::pair<State, linked_ptr<MenuButton::PressedLock> > NestedState; | 
|  | std::list<NestedState> menu_stack_; | 
|  |  | 
|  | // When Run is invoked during an active Run, it may be called from a separate | 
|  | // MenuControllerDelegate. If not empty is means we are nested, and the | 
|  | // stacked delegates should be notified instead of |delegate_|. | 
|  | typedef std::pair<internal::MenuControllerDelegate*, bool> NestedDelegate; | 
|  | std::list<NestedDelegate> delegate_stack_; | 
|  |  | 
|  | // As the mouse moves around submenus are not opened immediately. Instead | 
|  | // they open after this timer fires. | 
|  | base::OneShotTimer show_timer_; | 
|  |  | 
|  | // Used to invoke CancelAll(). This is used during drag and drop to hide the | 
|  | // menu after the mouse moves out of the of the menu. This is necessitated by | 
|  | // the lack of an ability to detect when the drag has completed from the drop | 
|  | // side. | 
|  | base::OneShotTimer cancel_all_timer_; | 
|  |  | 
|  | // Drop target. | 
|  | MenuItemView* drop_target_; | 
|  | MenuDelegate::DropPosition drop_position_; | 
|  |  | 
|  | // Owner of child windows. | 
|  | // WARNING: this may be NULL. | 
|  | Widget* owner_; | 
|  |  | 
|  | // Indicates a possible drag operation. | 
|  | bool possible_drag_; | 
|  |  | 
|  | // True when drag operation is in progress. | 
|  | bool drag_in_progress_; | 
|  |  | 
|  | // True when the drag operation in progress was initiated by the | 
|  | // MenuController for a child MenuItemView (as opposed to initiated separately | 
|  | // by a child View). | 
|  | bool did_initiate_drag_; | 
|  |  | 
|  | // Location the mouse was pressed at. Used to detect d&d. | 
|  | gfx::Point press_pt_; | 
|  |  | 
|  | // We get a slew of drag updated messages as the mouse is over us. To avoid | 
|  | // continually processing whether we can drop, we cache the coordinates. | 
|  | bool valid_drop_coordinates_; | 
|  | gfx::Point drop_pt_; | 
|  | int last_drop_operation_; | 
|  |  | 
|  | // If true, we're in the middle of invoking ShowAt on a submenu. | 
|  | bool showing_submenu_; | 
|  |  | 
|  | // Task for scrolling the menu. If non-null indicates a scroll is currently | 
|  | // underway. | 
|  | scoped_ptr<MenuScrollTask> scroll_task_; | 
|  |  | 
|  | // The lock to keep the menu button pressed while a menu is visible. | 
|  | scoped_ptr<MenuButton::PressedLock> pressed_lock_; | 
|  |  | 
|  | // ViewStorage id used to store the view mouse drag events are forwarded to. | 
|  | // See UpdateActiveMouseView() for details. | 
|  | const int active_mouse_view_id_; | 
|  |  | 
|  | internal::MenuControllerDelegate* delegate_; | 
|  |  | 
|  | // How deep we are in nested message loops. This should be at most 2 (when | 
|  | // showing a context menu from a menu). | 
|  | int message_loop_depth_; | 
|  |  | 
|  | // The timestamp of the event which closed the menu - or 0 otherwise. | 
|  | base::TimeDelta closing_event_time_; | 
|  |  | 
|  | // Time when the menu is first shown. | 
|  | base::TimeTicks menu_start_time_; | 
|  |  | 
|  | // If a mouse press triggered this menu, this will have its location (in | 
|  | // screen coordinates). Otherwise this will be (0, 0). | 
|  | gfx::Point menu_start_mouse_press_loc_; | 
|  |  | 
|  | // Controls behviour differences between an asynchronous run, and other types | 
|  | // of run (blocking, drag and drop). | 
|  | bool async_run_; | 
|  |  | 
|  | // Controls behavior differences between a combobox and other types of menu | 
|  | // (like a context menu). | 
|  | bool is_combobox_; | 
|  |  | 
|  | // Set to true if the menu item was selected by touch. | 
|  | bool item_selected_by_touch_; | 
|  |  | 
|  | // During mouse event handling, this is the RootView to forward mouse events | 
|  | // to. We need this, because if we forward one event to it (e.g., mouse | 
|  | // pressed), subsequent events (like dragging) should also go to it, even if | 
|  | // the mouse is no longer over the view. | 
|  | MenuHostRootView* current_mouse_event_target_; | 
|  |  | 
|  | // A mask of the EventFlags for the mouse buttons currently pressed. | 
|  | int current_mouse_pressed_state_; | 
|  |  | 
|  | scoped_ptr<MenuMessageLoop> message_loop_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(MenuController); | 
|  | }; | 
|  |  | 
|  | }  // namespace views | 
|  |  | 
|  | #endif  // UI_VIEWS_CONTROLS_MENU_MENU_CONTROLLER_H_ |