| // Copyright (c) 2006-2008 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 CHROME_VIEWS_CHROME_MENU_H__ |
| #define CHROME_VIEWS_CHROME_MENU_H__ |
| |
| #include <list> |
| |
| #include "base/gfx/point.h" |
| #include "base/gfx/rect.h" |
| #include "base/message_loop.h" |
| #include "base/task.h" |
| #include "chrome/common/drag_drop_types.h" |
| #include "chrome/common/gfx/chrome_font.h" |
| #include "chrome/views/controller.h" |
| #include "chrome/views/view.h" |
| #include "skia/include/SkBitmap.h" |
| |
| namespace views { |
| |
| class WidgetWin; |
| class MenuController; |
| class MenuItemView; |
| class SubmenuView; |
| |
| namespace { |
| class MenuHost; |
| class MenuHostRootView; |
| class MenuScrollTask; |
| class MenuScrollViewContainer; |
| } |
| |
| // MenuDelegate -------------------------------------------------------------- |
| |
| // Delegate for the menu. |
| |
| class MenuDelegate : Controller { |
| public: |
| // Used during drag and drop to indicate where the drop indicator should |
| // be rendered. |
| enum DropPosition { |
| // Indicates a drop is not allowed here. |
| DROP_NONE, |
| |
| // Indicates the drop should occur before the item. |
| DROP_BEFORE, |
| |
| // Indicates the drop should occur after the item. |
| DROP_AFTER, |
| |
| // Indicates the drop should occur on the item. |
| DROP_ON |
| }; |
| |
| // Whether or not an item should be shown as checked. |
| // TODO(sky): need checked support. |
| virtual bool IsItemChecked(int id) const { |
| return false; |
| } |
| |
| // The string shown for the menu item. This is only invoked when an item is |
| // added with an empty label. |
| virtual std::wstring GetLabel(int id) const { |
| return std::wstring(); |
| } |
| |
| // Shows the context menu with the specified id. This is invoked when the |
| // user does the appropriate gesture to show a context menu. The id |
| // identifies the id of the menu to show the context menu for. |
| // is_mouse_gesture is true if this is the result of a mouse gesture. |
| // If this is not the result of a mouse gesture x/y is the recommended |
| // location to display the content menu at. In either case, x/y is in |
| // screen coordinates. |
| // Returns true if a context menu was displayed, otherwise false |
| virtual bool ShowContextMenu(MenuItemView* source, |
| int id, |
| int x, |
| int y, |
| bool is_mouse_gesture) { |
| return false; |
| } |
| |
| // Controller |
| virtual bool SupportsCommand(int id) const { |
| return true; |
| } |
| virtual bool IsCommandEnabled(int id) const { |
| return true; |
| } |
| virtual bool GetContextualLabel(int id, std::wstring* out) const { |
| return false; |
| } |
| virtual void ExecuteCommand(int id) { |
| } |
| |
| // Executes the specified command. mouse_event_flags give the flags of the |
| // mouse event that triggered this to be invoked (views::MouseEvent |
| // flags). mouse_event_flags is 0 if this is triggered by a user gesture |
| // other than a mouse event. |
| virtual void ExecuteCommand(int id, int mouse_event_flags) { |
| ExecuteCommand(id); |
| } |
| |
| // Returns true if the specified mouse event is one the user can use |
| // to trigger, or accept, the mouse. Defaults to left or right mouse buttons. |
| virtual bool IsTriggerableEvent(const MouseEvent& e) { |
| return e.IsLeftMouseButton() || e.IsRightMouseButton(); |
| } |
| |
| // Invoked to determine if drops can be accepted for a submenu. This is |
| // ONLY invoked for menus that have submenus and indicates whether or not |
| // a drop can occur on any of the child items of the item. For example, |
| // consider the following menu structure: |
| // |
| // A |
| // B |
| // C |
| // |
| // Where A has a submenu with children B and C. This is ONLY invoked for |
| // A, not B and C. |
| // |
| // To restrict which children can be dropped on override GetDropOperation. |
| virtual bool CanDrop(MenuItemView* menu, const OSExchangeData& data) { |
| return false; |
| } |
| |
| // Returns the drop operation for the specified target menu item. This is |
| // only invoked if CanDrop returned true for the parent menu. position |
| // is set based on the location of the mouse, reset to specify a different |
| // position. |
| // |
| // If a drop should not be allowed, returned DragDropTypes::DRAG_NONE. |
| virtual int GetDropOperation(MenuItemView* item, |
| const DropTargetEvent& event, |
| DropPosition* position) { |
| NOTREACHED() << "If you override CanDrop, you need to override this too"; |
| return DragDropTypes::DRAG_NONE; |
| } |
| |
| // Invoked to perform the drop operation. This is ONLY invoked if |
| // canDrop returned true for the parent menu item, and GetDropOperation |
| // returned an operation other than DragDropTypes::DRAG_NONE. |
| // |
| // menu indicates the menu the drop occurred on. |
| virtual int OnPerformDrop(MenuItemView* menu, |
| DropPosition position, |
| const DropTargetEvent& event) { |
| NOTREACHED() << "If you override CanDrop, you need to override this too"; |
| return DragDropTypes::DRAG_NONE; |
| } |
| |
| // Invoked to determine if it is possible for the user to drag the specified |
| // menu item. |
| virtual bool CanDrag(MenuItemView* menu) { |
| return false; |
| } |
| |
| // Invoked to write the data for a drag operation to data. sender is the |
| // MenuItemView being dragged. |
| virtual void WriteDragData(MenuItemView* sender, OSExchangeData* data) { |
| NOTREACHED() << "If you override CanDrag, you must override this too."; |
| } |
| |
| // Invoked to determine the drag operations for a drag session of sender. |
| // See DragDropTypes for possible values. |
| virtual int GetDragOperations(MenuItemView* sender) { |
| NOTREACHED() << "If you override CanDrag, you must override this too."; |
| return 0; |
| } |
| |
| // Notification the menu has closed. This is only sent when running the |
| // menu for a drop. |
| virtual void DropMenuClosed(MenuItemView* menu) { |
| } |
| |
| // Notification that the user has highlighted the specified item. |
| virtual void SelectionChanged(MenuItemView* menu) { |
| } |
| }; |
| |
| // MenuItemView -------------------------------------------------------------- |
| |
| // MenuItemView represents a single menu item with a label and optional icon. |
| // Each MenuItemView may also contain a submenu, which in turn may contain |
| // any number of child MenuItemViews. |
| // |
| // To use a menu create an initial MenuItemView using the constructor that |
| // takes a MenuDelegate, then create any number of child menu items by way |
| // of the various AddXXX methods. |
| // |
| // MenuItemView is itself a View, which means you can add Views to each |
| // MenuItemView. This normally NOT want you want, rather add other child Views |
| // to the submenu of the MenuItemView. |
| // |
| // There are two ways to show a MenuItemView: |
| // 1. Use RunMenuAt. This blocks the caller, executing the selected command |
| // on success. |
| // 2. Use RunMenuForDropAt. This is intended for use during a drop session |
| // and does NOT block the caller. Instead the delegate is notified when the |
| // menu closes via the DropMenuClosed method. |
| |
| class MenuItemView : public View { |
| friend class MenuController; |
| |
| public: |
| // ID used to identify menu items. |
| static const int kMenuItemViewID; |
| |
| // If true SetNestableTasksAllowed(true) is invoked before MessageLoop::Run |
| // is invoked. This is only useful for testing and defaults to false. |
| static bool allow_task_nesting_during_run_; |
| |
| // Different types of menu items. |
| enum Type { |
| NORMAL, |
| SUBMENU, |
| CHECKBOX, |
| RADIO, |
| SEPARATOR |
| }; |
| |
| // Where the menu should be anchored to. |
| enum AnchorPosition { |
| TOPLEFT, |
| TOPRIGHT |
| }; |
| |
| // Constructor for use with the top level menu item. This menu is never |
| // shown to the user, rather its use as the parent for all menu items. |
| explicit MenuItemView(MenuDelegate* delegate); |
| |
| virtual ~MenuItemView(); |
| |
| // Run methods. See description above class for details. Both Run methods take |
| // a rectangle, which is used to position the menu. |has_mnemonics| indicates |
| // whether the items have mnemonics. Mnemonics are identified by way of the |
| // character following the '&'. |
| void RunMenuAt(HWND parent, |
| const gfx::Rect& bounds, |
| AnchorPosition anchor, |
| bool has_mnemonics); |
| void RunMenuForDropAt(HWND parent, |
| const gfx::Rect& bounds, |
| AnchorPosition anchor); |
| |
| // Hides and cancels the menu. This does nothing if the menu is not open. |
| void Cancel(); |
| |
| // Adds an item to this menu. |
| // item_id The id of the item, used to identify it in delegate callbacks |
| // or (if delegate is NULL) to identify the command associated |
| // with this item with the controller specified in the ctor. Note |
| // that this value should not be 0 as this has a special meaning |
| // ("NULL command, no item selected") |
| // label The text label shown. |
| // type The type of item. |
| void AppendMenuItem(int item_id, |
| const std::wstring& label, |
| Type type) { |
| AppendMenuItemInternal(item_id, label, SkBitmap(), type); |
| } |
| |
| // Append a submenu to this menu. |
| // The returned pointer is owned by this menu. |
| MenuItemView* AppendSubMenu(int item_id, |
| const std::wstring& label) { |
| return AppendMenuItemInternal(item_id, label, SkBitmap(), SUBMENU); |
| } |
| |
| // Append a submenu with an icon to this menu. |
| // The returned pointer is owned by this menu. |
| MenuItemView* AppendSubMenuWithIcon(int item_id, |
| const std::wstring& label, |
| const SkBitmap& icon) { |
| return AppendMenuItemInternal(item_id, label, icon, SUBMENU); |
| } |
| |
| // This is a convenience for standard text label menu items where the label |
| // is provided with this call. |
| void AppendMenuItemWithLabel(int item_id, |
| const std::wstring& label) { |
| AppendMenuItem(item_id, label, NORMAL); |
| } |
| |
| // This is a convenience for text label menu items where the label is |
| // provided by the delegate. |
| void AppendDelegateMenuItem(int item_id) { |
| AppendMenuItem(item_id, std::wstring(), NORMAL); |
| } |
| |
| // Adds a separator to this menu |
| void AppendSeparator() { |
| AppendMenuItemInternal(0, std::wstring(), SkBitmap(), SEPARATOR); |
| } |
| |
| // Appends a menu item with an icon. This is for the menu item which |
| // needs an icon. Calling this function forces the Menu class to draw |
| // the menu, instead of relying on Windows. |
| void AppendMenuItemWithIcon(int item_id, |
| const std::wstring& label, |
| const SkBitmap& icon) { |
| AppendMenuItemInternal(item_id, label, icon, NORMAL); |
| } |
| |
| // Returns the view that contains child menu items. If the submenu has |
| // not been creates, this creates it. |
| virtual SubmenuView* CreateSubmenu(); |
| |
| // Returns true if this menu item has a submenu. |
| virtual bool HasSubmenu() const { return (submenu_ != NULL); } |
| |
| // Returns the view containing child menu items. |
| virtual SubmenuView* GetSubmenu() const { return submenu_; } |
| |
| // Returns the parent menu item. |
| MenuItemView* GetParentMenuItem() const { return parent_menu_item_; } |
| |
| // Sets the font. |
| void SetFont(const ChromeFont& font) { font_ = font; } |
| |
| // Sets the title |
| void SetTitle(const std::wstring& title) { |
| title_ = title; |
| } |
| |
| // Returns the title. |
| const std::wstring& GetTitle() const { return title_; } |
| |
| // Sets whether this item is selected. This is invoked as the user moves |
| // the mouse around the menu while open. |
| void SetSelected(bool selected); |
| |
| // Returns true if the item is selected. |
| bool IsSelected() const { return selected_; } |
| |
| // Sets the icon for the descendant identified by item_id. |
| void SetIcon(const SkBitmap& icon, int item_id); |
| |
| // Sets the icon of this menu item. |
| void SetIcon(const SkBitmap& icon); |
| |
| // Returns the icon. |
| const SkBitmap& GetIcon() const { return icon_; } |
| |
| // Sets the command id of this menu item. |
| void SetCommand(int command) { command_ = command; } |
| |
| // Returns the command id of this item. |
| int GetCommand() const { return command_; } |
| |
| // Paints the menu item. |
| virtual void Paint(ChromeCanvas* canvas); |
| |
| // Returns the preferred size of this item. |
| virtual gfx::Size GetPreferredSize(); |
| |
| // Returns the object responsible for controlling showing the menu. |
| MenuController* GetMenuController(); |
| |
| // Returns the delegate. This returns the delegate of the root menu item. |
| MenuDelegate* GetDelegate(); |
| |
| // Returns the root parent, or this if this has no parent. |
| MenuItemView* GetRootMenuItem(); |
| |
| // Returns the mnemonic for this MenuItemView, or 0 if this MenuItemView |
| // doesn't have a mnemonic. |
| wchar_t GetMnemonic(); |
| |
| // Do we have icons? This only has effect on the top menu. Turning this on |
| // makes the menus slightly wider and taller. |
| void set_has_icons(bool has_icons) { |
| has_icons_ = has_icons; |
| } |
| |
| protected: |
| // Creates a MenuItemView. This is used by the various AddXXX methods. |
| MenuItemView(MenuItemView* parent, int command, Type type); |
| |
| private: |
| // Called by the two constructors to initialize this menu item. |
| void Init(MenuItemView* parent, |
| int command, |
| MenuItemView::Type type, |
| MenuDelegate* delegate); |
| |
| // All the AddXXX methods funnel into this. |
| MenuItemView* AppendMenuItemInternal(int item_id, |
| const std::wstring& label, |
| const SkBitmap& icon, |
| Type type); |
| |
| // Returns the descendant with the specified command. |
| MenuItemView* GetDescendantByID(int id); |
| |
| // Invoked by the MenuController when the menu closes as the result of |
| // drag and drop run. |
| void DropMenuClosed(bool notify_delegate); |
| |
| // The RunXXX methods call into this to set up the necessary state before |
| // running. |
| void PrepareForRun(bool has_mnemonics); |
| |
| // Returns the flags passed to DrawStringInt. |
| int GetDrawStringFlags(); |
| |
| // If this menu item has no children a child is added showing it has no |
| // children. Otherwise AddEmtpyMenuIfNecessary is recursively invoked on |
| // child menu items that have children. |
| void AddEmptyMenus(); |
| |
| // Undoes the work of AddEmptyMenus. |
| void RemoveEmptyMenus(); |
| |
| // Given bounds within our View, this helper routine mirrors the bounds if |
| // necessary. |
| void AdjustBoundsForRTLUI(RECT* rect) const; |
| |
| // Actual paint implementation. If for_drag is true, portions of the menu |
| // are not rendered. |
| void Paint(ChromeCanvas* canvas, bool for_drag); |
| |
| // Destroys the window used to display this menu and recursively destroys |
| // the windows used to display all descendants. |
| void DestroyAllMenuHosts(); |
| |
| // Returns the various margins. |
| int GetTopMargin(); |
| int GetBottomMargin(); |
| |
| // The delegate. This is only valid for the root menu item. You shouldn't |
| // use this directly, instead use GetDelegate() which walks the tree as |
| // as necessary. |
| MenuDelegate* delegate_; |
| |
| // Returns the controller for the run operation, or NULL if the menu isn't |
| // showing. |
| MenuController* controller_; |
| |
| // Used to detect when Cancel was invoked. |
| bool canceled_; |
| |
| // Our parent. |
| MenuItemView* parent_menu_item_; |
| |
| // Type of menu. NOTE: MenuItemView doesn't itself represent SEPARATOR, |
| // that is handled by an entirely different view class. |
| Type type_; |
| |
| // Whether we're selected. |
| bool selected_; |
| |
| // Command id. |
| int command_; |
| |
| // Submenu, created via CreateSubmenu. |
| SubmenuView* submenu_; |
| |
| // Font. |
| ChromeFont font_; |
| |
| // Title. |
| std::wstring title_; |
| |
| // Icon. |
| SkBitmap icon_; |
| |
| // Does the title have a mnemonic? |
| bool has_mnemonics_; |
| |
| bool has_icons_; |
| |
| DISALLOW_EVIL_CONSTRUCTORS(MenuItemView); |
| }; |
| |
| // SubmenuView ---------------------------------------------------------------- |
| |
| // SubmenuView is the parent of all menu items. |
| // |
| // SubmenuView has the following responsibilities: |
| // . It positions and sizes all child views (any type of View may be added, |
| // not just MenuItemViews). |
| // . Forwards the appropriate events to the MenuController. This allows the |
| // MenuController to update the selection as the user moves the mouse around. |
| // . Renders the drop indicator during a drop operation. |
| // . Shows and hides the window (a WidgetWin) when the menu is shown on |
| // screen. |
| // |
| // SubmenuView is itself contained in a MenuScrollViewContainer. |
| // MenuScrollViewContainer handles showing as much of the SubmenuView as the |
| // screen allows. If the SubmenuView is taller than the screen, scroll buttons |
| // are provided that allow the user to see all the menu items. |
| class SubmenuView : public View { |
| public: |
| // Creates a SubmenuView for the specified menu item. |
| explicit SubmenuView(MenuItemView* parent); |
| ~SubmenuView(); |
| |
| // Returns the number of child views that are MenuItemViews. |
| // MenuItemViews are identified by ID. |
| int GetMenuItemCount(); |
| |
| // Returns the MenuItemView at the specified index. |
| MenuItemView* GetMenuItemAt(int index); |
| |
| // Positions and sizes the child views. This tiles the views vertically, |
| // giving each child the available width. |
| virtual void Layout(); |
| virtual gfx::Size GetPreferredSize(); |
| |
| // View method. Overriden to schedule a paint. We do this so that when |
| // scrolling occurs, everything is repainted correctly. |
| virtual void DidChangeBounds(const gfx::Rect& previous, |
| const gfx::Rect& current); |
| |
| // Painting. |
| void PaintChildren(ChromeCanvas* canvas); |
| |
| // Drag and drop methods. These are forwarded to the MenuController. |
| virtual bool CanDrop(const OSExchangeData& data); |
| virtual void OnDragEntered(const DropTargetEvent& event); |
| virtual int OnDragUpdated(const DropTargetEvent& event); |
| virtual void OnDragExited(); |
| virtual int OnPerformDrop(const DropTargetEvent& event); |
| |
| // Scrolls on menu item boundaries. |
| virtual bool OnMouseWheel(const MouseWheelEvent& e); |
| |
| // Returns true if the menu is showing. |
| bool IsShowing(); |
| |
| // Shows the menu at the specified location. Coordinates are in screen |
| // coordinates. max_width gives the max width the view should be. |
| void ShowAt(HWND parent, const gfx::Rect& bounds, bool do_capture); |
| |
| // Closes the menu, destroying the host. |
| void Close(); |
| |
| // Hides the hosting window. |
| // |
| // The hosting window is hidden first, then deleted (Close) when the menu is |
| // done running. This is done to avoid deletion ordering dependencies. In |
| // particular, during drag and drop (and when a modal dialog is shown as |
| // a result of choosing a context menu) it is possible that an event is |
| // being processed by the host, so that host is on the stack when we need to |
| // close the window. If we closed the window immediately (and deleted it), |
| // when control returned back to host we would crash as host was deleted. |
| void Hide(); |
| |
| // If mouse capture was grabbed, it is released. Does nothing if mouse was |
| // not captured. |
| void ReleaseCapture(); |
| |
| // Returns the parent menu item we're showing children for. |
| MenuItemView* GetMenuItem() const { return parent_menu_item_; } |
| |
| // Overriden to return true. This prevents tab from doing anything. |
| virtual bool CanProcessTabKeyEvents() { return true; } |
| |
| // Set the drop item and position. |
| void SetDropMenuItem(MenuItemView* item, |
| MenuDelegate::DropPosition position); |
| |
| // Returns whether the selection should be shown for the specified item. |
| // The selection is NOT shown during drag and drop when the drop is over |
| // the menu. |
| bool GetShowSelection(MenuItemView* item); |
| |
| // Returns the container for the SubmenuView. |
| MenuScrollViewContainer* GetScrollViewContainer(); |
| |
| // Returns the host of the menu. Returns NULL if not showing. |
| MenuHost* host() const { return host_; } |
| |
| private: |
| // Paints the drop indicator. This is only invoked if item is non-NULL and |
| // position is not DROP_NONE. |
| void PaintDropIndicator(ChromeCanvas* canvas, |
| MenuItemView* item, |
| MenuDelegate::DropPosition position); |
| |
| void SchedulePaintForDropIndicator(MenuItemView* item, |
| MenuDelegate::DropPosition position); |
| |
| // Calculates the location of th edrop indicator. |
| gfx::Rect CalculateDropIndicatorBounds(MenuItemView* item, |
| MenuDelegate::DropPosition position); |
| |
| // Parent menu item. |
| MenuItemView* parent_menu_item_; |
| |
| // WidgetWin subclass used to show the children. |
| MenuHost* host_; |
| |
| // If non-null, indicates a drop is in progress and drop_item is the item |
| // the drop is over. |
| MenuItemView* drop_item_; |
| |
| // Position of the drop. |
| MenuDelegate::DropPosition drop_position_; |
| |
| // Ancestor of the SubmenuView, lazily created. |
| MenuScrollViewContainer* scroll_view_container_; |
| |
| DISALLOW_EVIL_CONSTRUCTORS(SubmenuView); |
| }; |
| |
| // MenuController ------------------------------------------------------------- |
| |
| // MenuController manages showing, selecting and drag/drop for menus. |
| // All relevant events are forwarded to the MenuController from SubmenuView |
| // and MenuHost. |
| |
| class MenuController : public MessageLoopForUI::Dispatcher { |
| public: |
| friend class MenuHostRootView; |
| friend class MenuItemView; |
| friend class MenuScrollTask; |
| |
| // 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(HWND parent, |
| MenuItemView* root, |
| const gfx::Rect& bounds, |
| MenuItemView::AnchorPosition position, |
| int* mouse_event_flags); |
| |
| // Whether or not Run blocks. |
| bool IsBlockingRun() const { return blocking_run_; } |
| |
| // Sets the selection to menu_item, a value of NULL unselects everything. |
| // If open_submenu is true and menu_item has a submenu, the submenu is shown. |
| // If update_immediately is true, submenus are opened immediately, otherwise |
| // submenus are only opened after a timer fires. |
| // |
| // Internally this updates pending_state_ immediatley, and if |
| // update_immediately is true, CommitPendingSelection is invoked to |
| // show/hide submenus and update state_. |
| void SetSelection(MenuItemView* menu_item, |
| bool open_submenu, |
| bool update_immediately); |
| |
| // Cancels the current Run. If all is true, any nested loops are canceled |
| // as well. This immediately hides all menus. |
| void Cancel(bool all); |
| |
| // An alternative to Cancel(true) that can be used with a OneShotTimer. |
| void CancelAll() { return Cancel(true); } |
| |
| // Various events, forwarded from the submenu. |
| // |
| // NOTE: the coordinates of the events are in that of the |
| // MenuScrollViewContainer. |
| void OnMousePressed(SubmenuView* source, const MouseEvent& event); |
| void OnMouseDragged(SubmenuView* source, const MouseEvent& event); |
| void OnMouseReleased(SubmenuView* source, const MouseEvent& event); |
| void OnMouseMoved(SubmenuView* source, const MouseEvent& event); |
| void OnMouseEntered(SubmenuView* source, const MouseEvent& event); |
| bool CanDrop(SubmenuView* source, const OSExchangeData& data); |
| void OnDragEntered(SubmenuView* source, const DropTargetEvent& event); |
| int OnDragUpdated(SubmenuView* source, const DropTargetEvent& event); |
| void OnDragExited(SubmenuView* source); |
| int OnPerformDrop(SubmenuView* source, const DropTargetEvent& event); |
| |
| // Invoked from the scroll buttons of the MenuScrollViewContainer. |
| void OnDragEnteredScrollButton(SubmenuView* source, bool is_up); |
| void OnDragExitedScrollButton(SubmenuView* source); |
| |
| private: |
| // Tracks selection information. |
| struct State { |
| State() : item(NULL), submenu_open(false) {} |
| |
| // 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. |
| MenuItemView::AnchorPosition 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; |
| }; |
| |
| // Used by GetMenuPartByScreenCoordinate 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), 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. |
| MenuItemView* menu; |
| |
| // If type is SCROLL_*, this is the submenu the mouse is over. |
| SubmenuView* submenu; |
| }; |
| |
| // Sets the active MenuController. |
| static void SetActiveInstance(MenuController* controller); |
| |
| // Dispatcher method. This returns true if the menu was canceled, or |
| // if the message is such that the menu should be closed. |
| virtual bool Dispatch(const MSG& msg); |
| |
| // Key processing. The return value of these is returned from Dispatch. |
| // In other words, if these return false (which they do if escape was |
| // pressed, or a matching mnemonic was found) the message loop returns. |
| bool OnKeyDown(const MSG& msg); |
| bool OnChar(const MSG& msg); |
| |
| // Creates a MenuController. If blocking is true, Run blocks the caller |
| explicit MenuController(bool blocking); |
| |
| ~MenuController(); |
| |
| // 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); |
| |
| // 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. |
| MenuPart GetMenuPartByScreenCoordinate(SubmenuView* source, |
| int source_x, |
| int source_y); |
| |
| // 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 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); |
| |
| // 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); |
| |
| // Returns the depth of the menu. |
| static int MenuDepth(MenuItemView* item); |
| |
| // Selects the next/previous menu item. |
| void IncrementSelection(int delta); |
| |
| // 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 true if window is the window used to show item, or any of |
| // items ancestors. |
| bool IsMenuWindow(MenuItemView* item, HWND window); |
| |
| // Selects by mnemonic, and if that doesn't work tries the first character of |
| // the title. Returns true if a match was selected and the menu should exit. |
| bool SelectByChar(wchar_t key); |
| |
| // If there is a window at the location of the event, a new mouse event is |
| // generated and posted to it. |
| void RepostEvent(SubmenuView* source, const MouseEvent& 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(); |
| |
| // 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_; |
| |
| // If true, all nested run loops should be exited. |
| bool exit_all_; |
| |
| // 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 mouse event flags when the user clicked on a menu. Is 0 if the |
| // user did not use the mousee to select the menu. |
| int result_mouse_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. |
| std::list<State> menu_stack_; |
| |
| // As the mouse moves around submenus are not opened immediately. Instead |
| // they open after this timer fires. |
| base::OneShotTimer<MenuController> 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<MenuController> cancel_all_timer_; |
| |
| // Drop target. |
| MenuItemView* drop_target_; |
| MenuDelegate::DropPosition drop_position_; |
| |
| // Owner of child windows. |
| HWND owner_; |
| |
| // Indicates a possible drag operation. |
| bool possible_drag_; |
| |
| // Location the mouse was pressed at. Used to detect d&d. |
| int press_x_; |
| int press_y_; |
| |
| // 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_; |
| int drop_x_; |
| int drop_y_; |
| int last_drop_operation_; |
| |
| // If true, the mouse is over some menu. |
| bool any_menu_contains_mouse_; |
| |
| // 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_; |
| |
| DISALLOW_EVIL_CONSTRUCTORS(MenuController); |
| }; |
| |
| } // namespace views |
| |
| #endif // CHROME_VIEWS_CHROME_MENU_H__ |