- add sources.
[platform/framework/web/crosswalk.git] / src / ui / views / controls / menu / menu_controller.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/views/controls/menu/menu_controller.h"
6
7 #if defined(OS_WIN)
8 #include <windowsx.h>
9 #endif
10
11 #include "base/i18n/case_conversion.h"
12 #include "base/i18n/rtl.h"
13 #include "base/run_loop.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/time/time.h"
16 #include "ui/base/dragdrop/drag_utils.h"
17 #include "ui/base/dragdrop/os_exchange_data.h"
18 #include "ui/base/l10n/l10n_util.h"
19 #include "ui/events/event_constants.h"
20 #include "ui/events/event_utils.h"
21 #include "ui/events/keycodes/keyboard_codes.h"
22 #include "ui/gfx/canvas.h"
23 #include "ui/gfx/native_widget_types.h"
24 #include "ui/gfx/screen.h"
25 #include "ui/gfx/vector2d.h"
26 #include "ui/native_theme/native_theme.h"
27 #include "ui/views/controls/button/menu_button.h"
28 #include "ui/views/controls/menu/menu_config.h"
29 #include "ui/views/controls/menu/menu_controller_delegate.h"
30 #include "ui/views/controls/menu/menu_host_root_view.h"
31 #include "ui/views/controls/menu/menu_scroll_view_container.h"
32 #include "ui/views/controls/menu/submenu_view.h"
33 #include "ui/views/drag_utils.h"
34 #include "ui/views/event_utils.h"
35 #include "ui/views/focus/view_storage.h"
36 #include "ui/views/mouse_constants.h"
37 #include "ui/views/view_constants.h"
38 #include "ui/views/views_delegate.h"
39 #include "ui/views/widget/root_view.h"
40 #include "ui/views/widget/tooltip_manager.h"
41 #include "ui/views/widget/widget.h"
42
43 #if defined(USE_AURA)
44 #include "ui/aura/env.h"
45 #include "ui/aura/root_window.h"
46 #endif
47
48 #if defined(OS_WIN)
49 #include "ui/views/win/hwnd_util.h"
50 #endif
51
52 #if defined(USE_X11)
53 #include <X11/Xlib.h>
54 #endif
55
56 using base::Time;
57 using base::TimeDelta;
58 using ui::OSExchangeData;
59
60 // Period of the scroll timer (in milliseconds).
61 static const int kScrollTimerMS = 30;
62
63 // Amount of time from when the drop exits the menu and the menu is hidden.
64 static const int kCloseOnExitTime = 1200;
65
66 // If a context menu is invoked by touch, we shift the menu by this offset so
67 // that the finger does not obscure the menu.
68 static const int kCenteredContextMenuYOffset = -15;
69
70 namespace views {
71
72 namespace {
73
74 // When showing context menu on mouse down, the user might accidentally select
75 // the menu item on the subsequent mouse up. To prevent this, we add the
76 // following delay before the user is able to select an item.
77 static int menu_selection_hold_time_ms = kMinimumMsPressedToActivate;
78
79 // The spacing offset for the bubble tip.
80 const int kBubbleTipSizeLeftRight = 12;
81 const int kBubbleTipSizeTopBottom = 11;
82
83 // The maximum distance (in DIPS) that the mouse can be moved before it should
84 // trigger a mouse menu item activation (regardless of how long the menu has
85 // been showing).
86 const float kMaximumLengthMovedToActivate = 4.0f;
87
88 // Returns true if the mnemonic of |menu| matches key.
89 bool MatchesMnemonic(MenuItemView* menu, char16 key) {
90   return menu->GetMnemonic() == key;
91 }
92
93 // Returns true if |menu| doesn't have a mnemonic and first character of the its
94 // title is |key|.
95 bool TitleMatchesMnemonic(MenuItemView* menu, char16 key) {
96   if (menu->GetMnemonic())
97     return false;
98
99   string16 lower_title = base::i18n::ToLower(menu->title());
100   return !lower_title.empty() && lower_title[0] == key;
101 }
102
103 }  // namespace
104
105 // Returns the first descendant of |view| that is hot tracked.
106 static View* GetFirstHotTrackedView(View* view) {
107   if (!view)
108     return NULL;
109
110   if (!strcmp(view->GetClassName(), CustomButton::kViewClassName)) {
111     CustomButton* button = static_cast<CustomButton*>(view);
112     if (button->IsHotTracked())
113       return button;
114   }
115
116   for (int i = 0; i < view->child_count(); ++i) {
117     View* hot_view = GetFirstHotTrackedView(view->child_at(i));
118     if (hot_view)
119       return hot_view;
120   }
121   return NULL;
122 }
123
124 // Recurses through the child views of |view| returning the first view starting
125 // at |start| that is focusable. A value of -1 for |start| indicates to start at
126 // the first view (if |forward| is false, iterating starts at the last view). If
127 // |forward| is true the children are considered first to last, otherwise last
128 // to first.
129 static View* GetFirstFocusableView(View* view, int start, bool forward) {
130   if (forward) {
131     for (int i = start == -1 ? 0 : start; i < view->child_count(); ++i) {
132       View* deepest = GetFirstFocusableView(view->child_at(i), -1, forward);
133       if (deepest)
134         return deepest;
135     }
136   } else {
137     for (int i = start == -1 ? view->child_count() - 1 : start; i >= 0; --i) {
138       View* deepest = GetFirstFocusableView(view->child_at(i), -1, forward);
139       if (deepest)
140         return deepest;
141     }
142   }
143   return view->IsFocusable() ? view : NULL;
144 }
145
146 // Returns the first child of |start| that is focusable.
147 static View* GetInitialFocusableView(View* start, bool forward) {
148   return GetFirstFocusableView(start, -1, forward);
149 }
150
151 // Returns the next view after |start_at| that is focusable. Returns NULL if
152 // there are no focusable children of |ancestor| after |start_at|.
153 static View* GetNextFocusableView(View* ancestor,
154                                   View* start_at,
155                                   bool forward) {
156   DCHECK(ancestor->Contains(start_at));
157   View* parent = start_at;
158   do {
159     View* new_parent = parent->parent();
160     int index = new_parent->GetIndexOf(parent);
161     index += forward ? 1 : -1;
162     if (forward || index != -1) {
163       View* next = GetFirstFocusableView(new_parent, index, forward);
164       if (next)
165         return next;
166     }
167     parent = new_parent;
168   } while (parent != ancestor);
169   return NULL;
170 }
171
172 // MenuScrollTask --------------------------------------------------------------
173
174 // MenuScrollTask is used when the SubmenuView does not all fit on screen and
175 // the mouse is over the scroll up/down buttons. MenuScrollTask schedules
176 // itself with a RepeatingTimer. When Run is invoked MenuScrollTask scrolls
177 // appropriately.
178
179 class MenuController::MenuScrollTask {
180  public:
181   MenuScrollTask() : submenu_(NULL), is_scrolling_up_(false), start_y_(0) {
182     pixels_per_second_ = MenuItemView::pref_menu_height() * 20;
183   }
184
185   void Update(const MenuController::MenuPart& part) {
186     if (!part.is_scroll()) {
187       StopScrolling();
188       return;
189     }
190     DCHECK(part.submenu);
191     SubmenuView* new_menu = part.submenu;
192     bool new_is_up = (part.type == MenuController::MenuPart::SCROLL_UP);
193     if (new_menu == submenu_ && is_scrolling_up_ == new_is_up)
194       return;
195
196     start_scroll_time_ = base::Time::Now();
197     start_y_ = part.submenu->GetVisibleBounds().y();
198     submenu_ = new_menu;
199     is_scrolling_up_ = new_is_up;
200
201     if (!scrolling_timer_.IsRunning()) {
202       scrolling_timer_.Start(FROM_HERE,
203                              TimeDelta::FromMilliseconds(kScrollTimerMS),
204                              this, &MenuScrollTask::Run);
205     }
206   }
207
208   void StopScrolling() {
209     if (scrolling_timer_.IsRunning()) {
210       scrolling_timer_.Stop();
211       submenu_ = NULL;
212     }
213   }
214
215   // The menu being scrolled. Returns null if not scrolling.
216   SubmenuView* submenu() const { return submenu_; }
217
218  private:
219   void Run() {
220     DCHECK(submenu_);
221     gfx::Rect vis_rect = submenu_->GetVisibleBounds();
222     const int delta_y = static_cast<int>(
223         (base::Time::Now() - start_scroll_time_).InMilliseconds() *
224         pixels_per_second_ / 1000);
225     vis_rect.set_y(is_scrolling_up_ ?
226         std::max(0, start_y_ - delta_y) :
227         std::min(submenu_->height() - vis_rect.height(), start_y_ + delta_y));
228     submenu_->ScrollRectToVisible(vis_rect);
229   }
230
231   // SubmenuView being scrolled.
232   SubmenuView* submenu_;
233
234   // Direction scrolling.
235   bool is_scrolling_up_;
236
237   // Timer to periodically scroll.
238   base::RepeatingTimer<MenuScrollTask> scrolling_timer_;
239
240   // Time we started scrolling at.
241   base::Time start_scroll_time_;
242
243   // How many pixels to scroll per second.
244   int pixels_per_second_;
245
246   // Y-coordinate of submenu_view_ when scrolling started.
247   int start_y_;
248
249   DISALLOW_COPY_AND_ASSIGN(MenuScrollTask);
250 };
251
252 // MenuController:SelectByCharDetails ----------------------------------------
253
254 struct MenuController::SelectByCharDetails {
255   SelectByCharDetails()
256       : first_match(-1),
257         has_multiple(false),
258         index_of_item(-1),
259         next_match(-1) {
260   }
261
262   // Index of the first menu with the specified mnemonic.
263   int first_match;
264
265   // If true there are multiple menu items with the same mnemonic.
266   bool has_multiple;
267
268   // Index of the selected item; may remain -1.
269   int index_of_item;
270
271   // If there are multiple matches this is the index of the item after the
272   // currently selected item whose mnemonic matches. This may remain -1 even
273   // though there are matches.
274   int next_match;
275 };
276
277 // MenuController:State ------------------------------------------------------
278
279 MenuController::State::State()
280     : item(NULL),
281       submenu_open(false),
282       anchor(MenuItemView::TOPLEFT),
283       context_menu(false) {}
284
285 MenuController::State::~State() {}
286
287 // MenuController ------------------------------------------------------------
288
289 // static
290 MenuController* MenuController::active_instance_ = NULL;
291
292 // static
293 MenuController* MenuController::GetActiveInstance() {
294   return active_instance_;
295 }
296
297 MenuItemView* MenuController::Run(Widget* parent,
298                                   MenuButton* button,
299                                   MenuItemView* root,
300                                   const gfx::Rect& bounds,
301                                   MenuItemView::AnchorPosition position,
302                                   bool context_menu,
303                                   int* result_event_flags) {
304   exit_type_ = EXIT_NONE;
305   possible_drag_ = false;
306   drag_in_progress_ = false;
307   closing_event_time_ = base::TimeDelta();
308   menu_start_time_ = base::TimeTicks::Now();
309   menu_start_mouse_press_loc_ = gfx::Point();
310
311   // If we are shown on mouse press, we will eat the subsequent mouse down and
312   // the parent widget will not be able to reset its state (it might have mouse
313   // capture from the mouse down). So we clear its state here.
314   if (parent) {
315     View* root_view = parent->GetRootView();
316     if (root_view) {
317       root_view->SetMouseHandler(NULL);
318       const ui::Event* event =
319           static_cast<internal::RootView*>(root_view)->current_event();
320       if (event && event->type() == ui::ET_MOUSE_PRESSED) {
321         gfx::Point screen_loc(
322             static_cast<const ui::MouseEvent*>(event)->location());
323         View::ConvertPointToScreen(
324             static_cast<View*>(event->target()), &screen_loc);
325         menu_start_mouse_press_loc_ = screen_loc;
326       }
327     }
328   }
329
330   bool nested_menu = showing_;
331   if (showing_) {
332     // Only support nesting of blocking_run menus, nesting of
333     // blocking/non-blocking shouldn't be needed.
334     DCHECK(blocking_run_);
335
336     // We're already showing, push the current state.
337     menu_stack_.push_back(state_);
338
339     // The context menu should be owned by the same parent.
340     DCHECK_EQ(owner_, parent);
341   } else {
342     showing_ = true;
343   }
344
345   // Reset current state.
346   pending_state_ = State();
347   state_ = State();
348   UpdateInitialLocation(bounds, position, context_menu);
349
350   if (owner_)
351     owner_->RemoveObserver(this);
352   owner_ = parent;
353   if (owner_)
354     owner_->AddObserver(this);
355
356   // Set the selection, which opens the initial menu.
357   SetSelection(root, SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
358
359   if (!blocking_run_) {
360     // Start the timer to hide the menu. This is needed as we get no
361     // notification when the drag has finished.
362     StartCancelAllTimer();
363     return NULL;
364   }
365
366   if (button)
367     menu_button_ = button;
368
369   // Make sure Chrome doesn't attempt to shut down while the menu is showing.
370   if (ViewsDelegate::views_delegate)
371     ViewsDelegate::views_delegate->AddRef();
372
373   // We need to turn on nestable tasks as in some situations (pressing alt-f for
374   // one) the menus are run from a task. If we don't do this and are invoked
375   // from a task none of the tasks we schedule are processed and the menu
376   // appears totally broken.
377   message_loop_depth_++;
378   DCHECK_LE(message_loop_depth_, 2);
379   RunMessageLoop(nested_menu);
380   message_loop_depth_--;
381
382   if (ViewsDelegate::views_delegate)
383     ViewsDelegate::views_delegate->ReleaseRef();
384
385   // Close any open menus.
386   SetSelection(NULL, SELECTION_UPDATE_IMMEDIATELY | SELECTION_EXIT);
387
388   if (nested_menu) {
389     DCHECK(!menu_stack_.empty());
390     // We're running from within a menu, restore the previous state.
391     // The menus are already showing, so we don't have to show them.
392     state_ = menu_stack_.back();
393     pending_state_ = menu_stack_.back();
394     menu_stack_.pop_back();
395   } else {
396     showing_ = false;
397     did_capture_ = false;
398   }
399
400   MenuItemView* result = result_;
401   // In case we're nested, reset result_.
402   result_ = NULL;
403
404   if (result_event_flags)
405     *result_event_flags = accept_event_flags_;
406
407   if (exit_type_ == EXIT_OUTERMOST) {
408     SetExitType(EXIT_NONE);
409   } else {
410     if (nested_menu && result) {
411       // We're nested and about to return a value. The caller might enter
412       // another blocking loop. We need to make sure all menus are hidden
413       // before that happens otherwise the menus will stay on screen.
414       CloseAllNestedMenus();
415       SetSelection(NULL, SELECTION_UPDATE_IMMEDIATELY | SELECTION_EXIT);
416
417       // Set exit_all_, which makes sure all nested loops exit immediately.
418       if (exit_type_ != EXIT_DESTROYED)
419         SetExitType(EXIT_ALL);
420     }
421   }
422
423   // If we stopped running because one of the menus was destroyed chances are
424   // the button was also destroyed.
425   if (exit_type_ != EXIT_DESTROYED && menu_button_) {
426     menu_button_->SetState(CustomButton::STATE_NORMAL);
427     menu_button_->SchedulePaint();
428   }
429
430   return result;
431 }
432
433 void MenuController::Cancel(ExitType type) {
434   // If the menu has already been destroyed, no further cancellation is
435   // needed.  We especially don't want to set the |exit_type_| to a lesser
436   // value.
437   if (exit_type_ == EXIT_DESTROYED || exit_type_ == type)
438     return;
439
440   if (!showing_) {
441     // This occurs if we're in the process of notifying the delegate for a drop
442     // and the delegate cancels us.
443     return;
444   }
445
446   MenuItemView* selected = state_.item;
447   SetExitType(type);
448
449   SendMouseCaptureLostToActiveView();
450
451   // Hide windows immediately.
452   SetSelection(NULL, SELECTION_UPDATE_IMMEDIATELY | SELECTION_EXIT);
453
454   if (!blocking_run_) {
455     // If we didn't block the caller we need to notify the menu, which
456     // triggers deleting us.
457     DCHECK(selected);
458     showing_ = false;
459     delegate_->DropMenuClosed(
460         internal::MenuControllerDelegate::NOTIFY_DELEGATE,
461         selected->GetRootMenuItem());
462     // WARNING: the call to MenuClosed deletes us.
463     return;
464   }
465 }
466
467 void MenuController::OnMousePressed(SubmenuView* source,
468                                     const ui::MouseEvent& event) {
469   SetSelectionOnPointerDown(source, event);
470 }
471
472 void MenuController::OnMouseDragged(SubmenuView* source,
473                                     const ui::MouseEvent& event) {
474   MenuPart part = GetMenuPart(source, event.location());
475   UpdateScrolling(part);
476
477   if (!blocking_run_)
478     return;
479
480   if (possible_drag_) {
481     if (View::ExceededDragThreshold(event.location() - press_pt_))
482       StartDrag(source, press_pt_);
483     return;
484   }
485   MenuItemView* mouse_menu = NULL;
486   if (part.type == MenuPart::MENU_ITEM) {
487     if (!part.menu)
488       part.menu = source->GetMenuItem();
489     else
490       mouse_menu = part.menu;
491     SetSelection(part.menu ? part.menu : state_.item, SELECTION_OPEN_SUBMENU);
492   } else if (part.type == MenuPart::NONE) {
493     ShowSiblingMenu(source, event.location());
494   }
495   UpdateActiveMouseView(source, event, mouse_menu);
496 }
497
498 void MenuController::OnMouseReleased(SubmenuView* source,
499                                      const ui::MouseEvent& event) {
500   if (!blocking_run_)
501     return;
502
503   DCHECK(state_.item);
504   possible_drag_ = false;
505   DCHECK(blocking_run_);
506   MenuPart part = GetMenuPart(source, event.location());
507   if (event.IsRightMouseButton() && part.type == MenuPart::MENU_ITEM) {
508     MenuItemView* menu = part.menu;
509     // |menu| is NULL means this event is from an empty menu or a separator.
510     // If it is from an empty menu, use parent context menu instead of that.
511     if (menu == NULL &&
512         part.submenu->child_count() == 1 &&
513         part.submenu->child_at(0)->id() == MenuItemView::kEmptyMenuItemViewID) {
514       menu = part.parent;
515     }
516
517     if (menu != NULL && ShowContextMenu(menu, source, event,
518                                         ui::MENU_SOURCE_MOUSE))
519       return;
520   }
521
522   // We can use Ctrl+click or the middle mouse button to recursively open urls
523   // for selected folder menu items. If it's only a left click, show the
524   // contents of the folder.
525   if (!part.is_scroll() && part.menu &&
526       !(part.menu->HasSubmenu() &&
527         (event.flags() & ui::EF_LEFT_MOUSE_BUTTON))) {
528     if (GetActiveMouseView()) {
529       SendMouseReleaseToActiveView(source, event);
530       return;
531     }
532     // If a mouse release was received quickly after showing.
533     base::TimeDelta time_shown = base::TimeTicks::Now() - menu_start_time_;
534     if (time_shown.InMilliseconds() < menu_selection_hold_time_ms) {
535       // And it wasn't far from the mouse press location.
536       gfx::Point screen_loc(event.location());
537       View::ConvertPointToScreen(source->GetScrollViewContainer(), &screen_loc);
538       gfx::Vector2d moved = screen_loc - menu_start_mouse_press_loc_;
539       if (moved.Length() < kMaximumLengthMovedToActivate) {
540         // Ignore the mouse release as it was likely this menu was shown under
541         // the mouse and the action was just a normal click.
542         return;
543       }
544     }
545     if (part.menu->GetDelegate()->ShouldExecuteCommandWithoutClosingMenu(
546             part.menu->GetCommand(), event)) {
547       part.menu->GetDelegate()->ExecuteCommand(part.menu->GetCommand(),
548                                                event.flags());
549       return;
550     }
551     if (!part.menu->NonIconChildViewsCount() &&
552         part.menu->GetDelegate()->IsTriggerableEvent(part.menu, event)) {
553       base::TimeDelta shown_time = base::TimeTicks::Now() - menu_start_time_;
554       if (!state_.context_menu || !View::ShouldShowContextMenuOnMousePress() ||
555           shown_time.InMilliseconds() > menu_selection_hold_time_ms) {
556         Accept(part.menu, event.flags());
557       }
558       return;
559     }
560   } else if (part.type == MenuPart::MENU_ITEM) {
561     // User either clicked on empty space, or a menu that has children.
562     SetSelection(part.menu ? part.menu : state_.item,
563                  SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
564   }
565   SendMouseCaptureLostToActiveView();
566 }
567
568 void MenuController::OnMouseMoved(SubmenuView* source,
569                                   const ui::MouseEvent& event) {
570   HandleMouseLocation(source, event.location());
571 }
572
573 void MenuController::OnMouseEntered(SubmenuView* source,
574                                     const ui::MouseEvent& event) {
575   // MouseEntered is always followed by a mouse moved, so don't need to
576   // do anything here.
577 }
578
579 #if defined(USE_AURA)
580 bool MenuController::OnMouseWheel(SubmenuView* source,
581                                   const ui::MouseWheelEvent& event) {
582   MenuPart part = GetMenuPart(source, event.location());
583   return part.submenu && part.submenu->OnMouseWheel(event);
584 }
585 #endif
586
587 void MenuController::OnGestureEvent(SubmenuView* source,
588                                     ui::GestureEvent* event) {
589   MenuPart part = GetMenuPart(source, event->location());
590   if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
591     SetSelectionOnPointerDown(source, *event);
592     event->StopPropagation();
593   } else if (event->type() == ui::ET_GESTURE_LONG_PRESS) {
594     if (part.type == MenuPart::MENU_ITEM && part.menu) {
595       if (ShowContextMenu(part.menu, source, *event, ui::MENU_SOURCE_TOUCH))
596         event->StopPropagation();
597     }
598   } else if (event->type() == ui::ET_GESTURE_TAP) {
599     if (!part.is_scroll() && part.menu &&
600         !(part.menu->HasSubmenu())) {
601       if (part.menu->GetDelegate()->IsTriggerableEvent(
602           part.menu, *event)) {
603         Accept(part.menu, event->flags());
604       }
605       event->StopPropagation();
606     } else if (part.type == MenuPart::MENU_ITEM) {
607       // User either tapped on empty space, or a menu that has children.
608       SetSelection(part.menu ? part.menu : state_.item,
609                    SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
610       event->StopPropagation();
611     }
612   } else if (event->type() == ui::ET_GESTURE_TAP_CANCEL &&
613              part.menu &&
614              part.type == MenuPart::MENU_ITEM) {
615     // Move the selection to the parent menu so that the selection in the
616     // current menu is unset. Make sure the submenu remains open by sending the
617     // appropriate SetSelectionTypes flags.
618     SetSelection(part.menu->GetParentMenuItem(),
619         SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
620     event->StopPropagation();
621   }
622
623   if (event->stopped_propagation())
624       return;
625
626   if (!part.submenu)
627     return;
628   part.submenu->OnGestureEvent(event);
629 }
630
631 bool MenuController::GetDropFormats(
632       SubmenuView* source,
633       int* formats,
634       std::set<OSExchangeData::CustomFormat>* custom_formats) {
635   return source->GetMenuItem()->GetDelegate()->GetDropFormats(
636       source->GetMenuItem(), formats, custom_formats);
637 }
638
639 bool MenuController::AreDropTypesRequired(SubmenuView* source) {
640   return source->GetMenuItem()->GetDelegate()->AreDropTypesRequired(
641       source->GetMenuItem());
642 }
643
644 bool MenuController::CanDrop(SubmenuView* source, const OSExchangeData& data) {
645   return source->GetMenuItem()->GetDelegate()->CanDrop(source->GetMenuItem(),
646                                                        data);
647 }
648
649 void MenuController::OnDragEntered(SubmenuView* source,
650                                    const ui::DropTargetEvent& event) {
651   valid_drop_coordinates_ = false;
652 }
653
654 int MenuController::OnDragUpdated(SubmenuView* source,
655                                   const ui::DropTargetEvent& event) {
656   StopCancelAllTimer();
657
658   gfx::Point screen_loc(event.location());
659   View::ConvertPointToScreen(source, &screen_loc);
660   if (valid_drop_coordinates_ && screen_loc == drop_pt_)
661     return last_drop_operation_;
662   drop_pt_ = screen_loc;
663   valid_drop_coordinates_ = true;
664
665   MenuItemView* menu_item = GetMenuItemAt(source, event.x(), event.y());
666   bool over_empty_menu = false;
667   if (!menu_item) {
668     // See if we're over an empty menu.
669     menu_item = GetEmptyMenuItemAt(source, event.x(), event.y());
670     if (menu_item)
671       over_empty_menu = true;
672   }
673   MenuDelegate::DropPosition drop_position = MenuDelegate::DROP_NONE;
674   int drop_operation = ui::DragDropTypes::DRAG_NONE;
675   if (menu_item) {
676     gfx::Point menu_item_loc(event.location());
677     View::ConvertPointToTarget(source, menu_item, &menu_item_loc);
678     MenuItemView* query_menu_item;
679     if (!over_empty_menu) {
680       int menu_item_height = menu_item->height();
681       if (menu_item->HasSubmenu() &&
682           (menu_item_loc.y() > kDropBetweenPixels &&
683            menu_item_loc.y() < (menu_item_height - kDropBetweenPixels))) {
684         drop_position = MenuDelegate::DROP_ON;
685       } else {
686         drop_position = (menu_item_loc.y() < menu_item_height / 2) ?
687             MenuDelegate::DROP_BEFORE : MenuDelegate::DROP_AFTER;
688       }
689       query_menu_item = menu_item;
690     } else {
691       query_menu_item = menu_item->GetParentMenuItem();
692       drop_position = MenuDelegate::DROP_ON;
693     }
694     drop_operation = menu_item->GetDelegate()->GetDropOperation(
695         query_menu_item, event, &drop_position);
696
697     // If the menu has a submenu, schedule the submenu to open.
698     SetSelection(menu_item, menu_item->HasSubmenu() ? SELECTION_OPEN_SUBMENU :
699                  SELECTION_DEFAULT);
700
701     if (drop_position == MenuDelegate::DROP_NONE ||
702         drop_operation == ui::DragDropTypes::DRAG_NONE)
703       menu_item = NULL;
704   } else {
705     SetSelection(source->GetMenuItem(), SELECTION_OPEN_SUBMENU);
706   }
707   SetDropMenuItem(menu_item, drop_position);
708   last_drop_operation_ = drop_operation;
709   return drop_operation;
710 }
711
712 void MenuController::OnDragExited(SubmenuView* source) {
713   StartCancelAllTimer();
714
715   if (drop_target_) {
716     StopShowTimer();
717     SetDropMenuItem(NULL, MenuDelegate::DROP_NONE);
718   }
719 }
720
721 int MenuController::OnPerformDrop(SubmenuView* source,
722                                   const ui::DropTargetEvent& event) {
723   DCHECK(drop_target_);
724   // NOTE: the delegate may delete us after invoking OnPerformDrop, as such
725   // we don't call cancel here.
726
727   MenuItemView* item = state_.item;
728   DCHECK(item);
729
730   MenuItemView* drop_target = drop_target_;
731   MenuDelegate::DropPosition drop_position = drop_position_;
732
733   // Close all menus, including any nested menus.
734   SetSelection(NULL, SELECTION_UPDATE_IMMEDIATELY | SELECTION_EXIT);
735   CloseAllNestedMenus();
736
737   // Set state such that we exit.
738   showing_ = false;
739   SetExitType(EXIT_ALL);
740
741   // If over an empty menu item, drop occurs on the parent.
742   if (drop_target->id() == MenuItemView::kEmptyMenuItemViewID)
743     drop_target = drop_target->GetParentMenuItem();
744
745   if (!IsBlockingRun()) {
746     delegate_->DropMenuClosed(
747         internal::MenuControllerDelegate::DONT_NOTIFY_DELEGATE,
748         item->GetRootMenuItem());
749   }
750
751   // WARNING: the call to MenuClosed deletes us.
752
753   return drop_target->GetDelegate()->OnPerformDrop(
754       drop_target, drop_position, event);
755 }
756
757 void MenuController::OnDragEnteredScrollButton(SubmenuView* source,
758                                                bool is_up) {
759   MenuPart part;
760   part.type = is_up ? MenuPart::SCROLL_UP : MenuPart::SCROLL_DOWN;
761   part.submenu = source;
762   UpdateScrolling(part);
763
764   // Do this to force the selection to hide.
765   SetDropMenuItem(source->GetMenuItemAt(0), MenuDelegate::DROP_NONE);
766
767   StopCancelAllTimer();
768 }
769
770 void MenuController::OnDragExitedScrollButton(SubmenuView* source) {
771   StartCancelAllTimer();
772   SetDropMenuItem(NULL, MenuDelegate::DROP_NONE);
773   StopScrolling();
774 }
775
776 void MenuController::UpdateSubmenuSelection(SubmenuView* submenu) {
777   if (submenu->IsShowing()) {
778     gfx::Point point = GetScreen()->GetCursorScreenPoint();
779     const SubmenuView* root_submenu =
780         submenu->GetMenuItem()->GetRootMenuItem()->GetSubmenu();
781     View::ConvertPointFromScreen(
782         root_submenu->GetWidget()->GetRootView(), &point);
783     HandleMouseLocation(submenu, point);
784   }
785 }
786
787 void MenuController::OnWidgetDestroying(Widget* widget) {
788   DCHECK_EQ(owner_, widget);
789   owner_->RemoveObserver(this);
790   owner_ = NULL;
791 }
792
793 // static
794 void MenuController::TurnOffMenuSelectionHoldForTest() {
795   menu_selection_hold_time_ms = -1;
796 }
797
798 void MenuController::SetSelection(MenuItemView* menu_item,
799                                   int selection_types) {
800   size_t paths_differ_at = 0;
801   std::vector<MenuItemView*> current_path;
802   std::vector<MenuItemView*> new_path;
803   BuildPathsAndCalculateDiff(pending_state_.item, menu_item, &current_path,
804                              &new_path, &paths_differ_at);
805
806   size_t current_size = current_path.size();
807   size_t new_size = new_path.size();
808
809   bool pending_item_changed = pending_state_.item != menu_item;
810   if (pending_item_changed && pending_state_.item) {
811     View* current_hot_view = GetFirstHotTrackedView(pending_state_.item);
812     if (current_hot_view && !strcmp(current_hot_view->GetClassName(),
813                                     CustomButton::kViewClassName)) {
814       CustomButton* button = static_cast<CustomButton*>(current_hot_view);
815       button->SetHotTracked(false);
816     }
817   }
818
819   // Notify the old path it isn't selected.
820   MenuDelegate* current_delegate =
821       current_path.empty() ? NULL : current_path.front()->GetDelegate();
822   for (size_t i = paths_differ_at; i < current_size; ++i) {
823     if (current_delegate &&
824         current_path[i]->GetType() == MenuItemView::SUBMENU) {
825       current_delegate->WillHideMenu(current_path[i]);
826     }
827     current_path[i]->SetSelected(false);
828   }
829
830   // Notify the new path it is selected.
831   for (size_t i = paths_differ_at; i < new_size; ++i) {
832     new_path[i]->ScrollRectToVisible(new_path[i]->GetLocalBounds());
833     new_path[i]->SetSelected(true);
834   }
835
836   if (menu_item && menu_item->GetDelegate())
837     menu_item->GetDelegate()->SelectionChanged(menu_item);
838
839   DCHECK(menu_item || (selection_types & SELECTION_EXIT) != 0);
840
841   pending_state_.item = menu_item;
842   pending_state_.submenu_open = (selection_types & SELECTION_OPEN_SUBMENU) != 0;
843
844   // Stop timers.
845   StopCancelAllTimer();
846   // Resets show timer only when pending menu item is changed.
847   if (pending_item_changed)
848     StopShowTimer();
849
850   if (selection_types & SELECTION_UPDATE_IMMEDIATELY)
851     CommitPendingSelection();
852   else if (pending_item_changed)
853     StartShowTimer();
854
855   // Notify an accessibility focus event on all menu items except for the root.
856   if (menu_item &&
857       (MenuDepth(menu_item) != 1 ||
858        menu_item->GetType() != MenuItemView::SUBMENU)) {
859     menu_item->NotifyAccessibilityEvent(
860         ui::AccessibilityTypes::EVENT_FOCUS, true);
861   }
862 }
863
864 void MenuController::SetSelectionOnPointerDown(SubmenuView* source,
865                                                const ui::LocatedEvent& event) {
866   if (!blocking_run_)
867     return;
868
869   DCHECK(!GetActiveMouseView());
870
871   MenuPart part = GetMenuPart(source, event.location());
872   if (part.is_scroll())
873     return;  // Ignore presses on scroll buttons.
874
875   // When this menu is opened through a touch event, a simulated right-click
876   // is sent before the menu appears.  Ignore it.
877   if ((event.flags() & ui::EF_RIGHT_MOUSE_BUTTON) &&
878       (event.flags() & ui::EF_FROM_TOUCH))
879     return;
880
881   if (part.type == MenuPart::NONE ||
882       (part.type == MenuPart::MENU_ITEM && part.menu &&
883        part.menu->GetRootMenuItem() != state_.item->GetRootMenuItem())) {
884     // Remember the time when we repost the event. The owner can then use this
885     // to figure out if this menu was finished with the same click which is
886     // sent to it thereafter. Note that the time stamp front he event cannot be
887     // used since the reposting will set a new timestamp when the event gets
888     // processed. As such it is better to take the current time which will be
889     // closer to the time when it arrives again in the menu handler.
890     closing_event_time_ = ui::EventTimeForNow();
891
892     // Mouse wasn't pressed over any menu, or the active menu, cancel.
893
894 #if defined(OS_WIN)
895     // We're going to close and we own the mouse capture. We need to repost the
896     // mouse down, otherwise the window the user clicked on won't get the event.
897     if (!state_.item) {
898       // We some times get an event after closing all the menus. Ignore it. Make
899       // sure the menu is in fact not visible. If the menu is visible, then
900       // we're in a bad state where we think the menu isn't visibile but it is.
901       DCHECK(!source->GetWidget()->IsVisible());
902     } else {
903       RepostEvent(source, event);
904     }
905 #endif
906
907     // And close.
908     ExitType exit_type = EXIT_ALL;
909     if (!menu_stack_.empty()) {
910       // We're running nested menus. Only exit all if the mouse wasn't over one
911       // of the menus from the last run.
912       gfx::Point screen_loc(event.location());
913       View::ConvertPointToScreen(source->GetScrollViewContainer(), &screen_loc);
914       MenuPart last_part = GetMenuPartByScreenCoordinateUsingMenu(
915           menu_stack_.back().item, screen_loc);
916       if (last_part.type != MenuPart::NONE)
917         exit_type = EXIT_OUTERMOST;
918     }
919     Cancel(exit_type);
920
921 #if defined(USE_AURA) && !defined(OS_WIN)
922     // We're going to exit the menu and want to repost the event so that is
923     // is handled normally after the context menu has exited. We call
924     // RepostEvent after Cancel so that mouse capture has been released so
925     // that finding the event target is unaffected by the current capture.
926     RepostEvent(source, event);
927 #endif
928
929     return;
930   }
931
932   // On a press we immediately commit the selection, that way a submenu
933   // pops up immediately rather than after a delay.
934   int selection_types = SELECTION_UPDATE_IMMEDIATELY;
935   if (!part.menu) {
936     part.menu = part.parent;
937     selection_types |= SELECTION_OPEN_SUBMENU;
938   } else {
939     if (part.menu->GetDelegate()->CanDrag(part.menu)) {
940       possible_drag_ = true;
941       press_pt_ = event.location();
942     }
943     if (part.menu->HasSubmenu())
944       selection_types |= SELECTION_OPEN_SUBMENU;
945   }
946   SetSelection(part.menu, selection_types);
947 }
948
949 void MenuController::StartDrag(SubmenuView* source,
950                                const gfx::Point& location) {
951   MenuItemView* item = state_.item;
952   DCHECK(item);
953   // Points are in the coordinates of the submenu, need to map to that of
954   // the selected item. Additionally source may not be the parent of
955   // the selected item, so need to map to screen first then to item.
956   gfx::Point press_loc(location);
957   View::ConvertPointToScreen(source->GetScrollViewContainer(), &press_loc);
958   View::ConvertPointToTarget(NULL, item, &press_loc);
959   gfx::Point widget_loc(press_loc);
960   View::ConvertPointToWidget(item, &widget_loc);
961   scoped_ptr<gfx::Canvas> canvas(GetCanvasForDragImage(
962       source->GetWidget(), gfx::Size(item->width(), item->height())));
963   item->PaintButton(canvas.get(), MenuItemView::PB_FOR_DRAG);
964
965   OSExchangeData data;
966   item->GetDelegate()->WriteDragData(item, &data);
967   drag_utils::SetDragImageOnDataObject(*canvas, item->size(),
968                                        press_loc.OffsetFromOrigin(),
969                                        &data);
970   StopScrolling();
971   int drag_ops = item->GetDelegate()->GetDragOperations(item);
972   drag_in_progress_ = true;
973   // TODO(varunjain): Properly determine and send DRAG_EVENT_SOURCE below.
974   item->GetWidget()->RunShellDrag(NULL, data, widget_loc, drag_ops,
975       ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
976   drag_in_progress_ = false;
977
978   if (GetActiveInstance() == this) {
979     if (showing_) {
980       // We're still showing, close all menus.
981       CloseAllNestedMenus();
982       Cancel(EXIT_ALL);
983     }  // else case, drop was on us.
984   }  // else case, someone canceled us, don't do anything
985 }
986
987 #if defined(OS_WIN)
988 bool MenuController::Dispatch(const MSG& msg) {
989   DCHECK(blocking_run_);
990
991   if (exit_type_ == EXIT_ALL || exit_type_ == EXIT_DESTROYED) {
992     // We must translate/dispatch the message here, otherwise we would drop
993     // the message on the floor.
994     TranslateMessage(&msg);
995     DispatchMessage(&msg);
996     return false;
997   }
998
999   // NOTE: we don't get WM_ACTIVATE or anything else interesting in here.
1000   switch (msg.message) {
1001     case WM_CONTEXTMENU: {
1002       MenuItemView* item = pending_state_.item;
1003       if (item && item->GetRootMenuItem() != item) {
1004         gfx::Point screen_loc(0, item->height());
1005         View::ConvertPointToScreen(item, &screen_loc);
1006         ui::MenuSourceType source_type = ui::MENU_SOURCE_MOUSE;
1007         if (GET_X_LPARAM(msg.lParam) == -1 && GET_Y_LPARAM(msg.lParam) == -1)
1008           source_type = ui::MENU_SOURCE_KEYBOARD;
1009         item->GetDelegate()->ShowContextMenu(item, item->GetCommand(),
1010                                              screen_loc, source_type);
1011       }
1012       return true;
1013     }
1014
1015     // NOTE: focus wasn't changed when the menu was shown. As such, don't
1016     // dispatch key events otherwise the focused window will get the events.
1017     case WM_KEYDOWN: {
1018       bool result = OnKeyDown(ui::KeyboardCodeFromNative(msg));
1019       TranslateMessage(&msg);
1020       return result;
1021     }
1022     case WM_CHAR:
1023       return !SelectByChar(static_cast<char16>(msg.wParam));
1024     case WM_KEYUP:
1025       return true;
1026
1027     case WM_SYSKEYUP:
1028       // We may have been shown on a system key, as such don't do anything
1029       // here. If another system key is pushed we'll get a WM_SYSKEYDOWN and
1030       // close the menu.
1031       return true;
1032
1033     case WM_CANCELMODE:
1034     case WM_SYSKEYDOWN:
1035       // Exit immediately on system keys.
1036       Cancel(EXIT_ALL);
1037       return false;
1038
1039     default:
1040       break;
1041   }
1042   TranslateMessage(&msg);
1043   DispatchMessage(&msg);
1044   return exit_type_ == EXIT_NONE;
1045 }
1046 #elif defined(USE_AURA)
1047 bool MenuController::Dispatch(const base::NativeEvent& event) {
1048   if (exit_type_ == EXIT_ALL || exit_type_ == EXIT_DESTROYED) {
1049     aura::Env::GetInstance()->GetDispatcher()->Dispatch(event);
1050     return false;
1051   }
1052   // Activates mnemonics only when it it pressed without modifiers except for
1053   // caps and shift.
1054   int flags = ui::EventFlagsFromNative(event) &
1055       ~ui::EF_CAPS_LOCK_DOWN & ~ui::EF_SHIFT_DOWN;
1056   if (flags == ui::EF_NONE) {
1057     switch (ui::EventTypeFromNative(event)) {
1058       case ui::ET_KEY_PRESSED:
1059         if (!OnKeyDown(ui::KeyboardCodeFromNative(event)))
1060           return false;
1061
1062         return !SelectByChar(ui::KeyboardCodeFromNative(event));
1063       case ui::ET_KEY_RELEASED:
1064         return true;
1065       default:
1066         break;
1067     }
1068   }
1069
1070   aura::Env::GetInstance()->GetDispatcher()->Dispatch(event);
1071   return exit_type_ == EXIT_NONE;
1072 }
1073 #endif
1074
1075 bool MenuController::OnKeyDown(ui::KeyboardCode key_code) {
1076   DCHECK(blocking_run_);
1077
1078   switch (key_code) {
1079     case ui::VKEY_UP:
1080       IncrementSelection(-1);
1081       break;
1082
1083     case ui::VKEY_DOWN:
1084       IncrementSelection(1);
1085       break;
1086
1087     // Handling of VK_RIGHT and VK_LEFT is different depending on the UI
1088     // layout.
1089     case ui::VKEY_RIGHT:
1090       if (base::i18n::IsRTL())
1091         CloseSubmenu();
1092       else
1093         OpenSubmenuChangeSelectionIfCan();
1094       break;
1095
1096     case ui::VKEY_LEFT:
1097       if (base::i18n::IsRTL())
1098         OpenSubmenuChangeSelectionIfCan();
1099       else
1100         CloseSubmenu();
1101       break;
1102
1103     case ui::VKEY_SPACE:
1104       if (SendAcceleratorToHotTrackedView() == ACCELERATOR_PROCESSED_EXIT)
1105         return false;
1106       break;
1107
1108     case ui::VKEY_F4:
1109       if (!accept_on_f4_)
1110         break;
1111       // Fallthrough to accept on F4, so combobox menus match Windows behavior.
1112     case ui::VKEY_RETURN:
1113       if (pending_state_.item) {
1114         if (pending_state_.item->HasSubmenu()) {
1115           OpenSubmenuChangeSelectionIfCan();
1116         } else {
1117           SendAcceleratorResultType result = SendAcceleratorToHotTrackedView();
1118           if (result == ACCELERATOR_NOT_PROCESSED &&
1119               pending_state_.item->enabled()) {
1120             Accept(pending_state_.item, 0);
1121             return false;
1122           } else if (result == ACCELERATOR_PROCESSED_EXIT) {
1123             return false;
1124           }
1125         }
1126       }
1127       break;
1128
1129     case ui::VKEY_ESCAPE:
1130       if (!state_.item->GetParentMenuItem() ||
1131           (!state_.item->GetParentMenuItem()->GetParentMenuItem() &&
1132            (!state_.item->HasSubmenu() ||
1133             !state_.item->GetSubmenu()->IsShowing()))) {
1134         // User pressed escape and only one menu is shown, cancel it.
1135         Cancel(EXIT_OUTERMOST);
1136         return false;
1137       }
1138       CloseSubmenu();
1139       break;
1140
1141 #if defined(OS_WIN)
1142     case VK_APPS:
1143       break;
1144 #endif
1145
1146     default:
1147       break;
1148   }
1149   return true;
1150 }
1151
1152 MenuController::MenuController(ui::NativeTheme* theme,
1153                                bool blocking,
1154                                internal::MenuControllerDelegate* delegate)
1155     : blocking_run_(blocking),
1156       showing_(false),
1157       exit_type_(EXIT_NONE),
1158       did_capture_(false),
1159       result_(NULL),
1160       accept_event_flags_(0),
1161       drop_target_(NULL),
1162       drop_position_(MenuDelegate::DROP_UNKNOWN),
1163       owner_(NULL),
1164       possible_drag_(false),
1165       drag_in_progress_(false),
1166       valid_drop_coordinates_(false),
1167       last_drop_operation_(MenuDelegate::DROP_UNKNOWN),
1168       showing_submenu_(false),
1169       menu_button_(NULL),
1170       active_mouse_view_id_(ViewStorage::GetInstance()->CreateStorageID()),
1171       delegate_(delegate),
1172       message_loop_depth_(0),
1173       menu_config_(theme),
1174       closing_event_time_(base::TimeDelta()),
1175       menu_start_time_(base::TimeTicks()),
1176       accept_on_f4_(false) {
1177   active_instance_ = this;
1178 }
1179
1180 MenuController::~MenuController() {
1181   DCHECK(!showing_);
1182   if (owner_)
1183     owner_->RemoveObserver(this);
1184   if (active_instance_ == this)
1185     active_instance_ = NULL;
1186   StopShowTimer();
1187   StopCancelAllTimer();
1188 }
1189
1190 MenuController::SendAcceleratorResultType
1191     MenuController::SendAcceleratorToHotTrackedView() {
1192   View* hot_view = GetFirstHotTrackedView(pending_state_.item);
1193   if (!hot_view)
1194     return ACCELERATOR_NOT_PROCESSED;
1195
1196   ui::Accelerator accelerator(ui::VKEY_RETURN, ui::EF_NONE);
1197   hot_view->AcceleratorPressed(accelerator);
1198   if (!strcmp(hot_view->GetClassName(), CustomButton::kViewClassName)) {
1199     CustomButton* button = static_cast<CustomButton*>(hot_view);
1200     button->SetHotTracked(true);
1201   }
1202   return (exit_type_ == EXIT_NONE) ?
1203       ACCELERATOR_PROCESSED : ACCELERATOR_PROCESSED_EXIT;
1204 }
1205
1206 void MenuController::UpdateInitialLocation(
1207     const gfx::Rect& bounds,
1208     MenuItemView::AnchorPosition position,
1209     bool context_menu) {
1210   pending_state_.context_menu = context_menu;
1211   pending_state_.initial_bounds = bounds;
1212   if (bounds.height() > 1) {
1213     // Inset the bounds slightly, otherwise drag coordinates don't line up
1214     // nicely and menus close prematurely.
1215     pending_state_.initial_bounds.Inset(0, 1);
1216   }
1217
1218   // Reverse anchor position for RTL languages.
1219   if (base::i18n::IsRTL() &&
1220       (position == MenuItemView::TOPRIGHT ||
1221        position == MenuItemView::TOPLEFT)) {
1222     pending_state_.anchor = position == MenuItemView::TOPRIGHT ?
1223         MenuItemView::TOPLEFT : MenuItemView::TOPRIGHT;
1224   } else {
1225     pending_state_.anchor = position;
1226   }
1227
1228   // Calculate the bounds of the monitor we'll show menus on. Do this once to
1229   // avoid repeated system queries for the info.
1230   pending_state_.monitor_bounds = GetScreen()->GetDisplayNearestPoint(
1231       bounds.origin()).work_area();
1232 #if defined(USE_ASH)
1233   if (!pending_state_.monitor_bounds.Contains(bounds)) {
1234     // Use the monitor area if the work area doesn't contain the bounds. This
1235     // handles showing a menu from the launcher.
1236     gfx::Rect monitor_area = GetScreen()->GetDisplayNearestPoint(
1237         bounds.origin()).bounds();
1238     if (monitor_area.Contains(bounds))
1239       pending_state_.monitor_bounds = monitor_area;
1240   }
1241 #endif
1242 }
1243
1244 void MenuController::Accept(MenuItemView* item, int event_flags) {
1245   DCHECK(IsBlockingRun());
1246   result_ = item;
1247   if (item && !menu_stack_.empty() &&
1248       !item->GetDelegate()->ShouldCloseAllMenusOnExecute(item->GetCommand())) {
1249     SetExitType(EXIT_OUTERMOST);
1250   } else {
1251     SetExitType(EXIT_ALL);
1252   }
1253   accept_event_flags_ = event_flags;
1254 }
1255
1256 bool MenuController::ShowSiblingMenu(SubmenuView* source,
1257                                      const gfx::Point& mouse_location) {
1258   if (!menu_stack_.empty() || !menu_button_)
1259     return false;
1260
1261   View* source_view = source->GetScrollViewContainer();
1262   if (mouse_location.x() >= 0 &&
1263       mouse_location.x() < source_view->width() &&
1264       mouse_location.y() >= 0 &&
1265       mouse_location.y() < source_view->height()) {
1266     // The mouse is over the menu, no need to continue.
1267     return false;
1268   }
1269
1270   gfx::NativeWindow window_under_mouse = GetScreen()->GetWindowUnderCursor();
1271   // TODO(oshima): Replace with views only API.
1272   if (!owner_ || window_under_mouse != owner_->GetNativeWindow())
1273     return false;
1274
1275   // The user moved the mouse outside the menu and over the owning window. See
1276   // if there is a sibling menu we should show.
1277   gfx::Point screen_point(mouse_location);
1278   View::ConvertPointToScreen(source_view, &screen_point);
1279   MenuItemView::AnchorPosition anchor;
1280   bool has_mnemonics;
1281   MenuButton* button = NULL;
1282   MenuItemView* alt_menu = source->GetMenuItem()->GetDelegate()->
1283       GetSiblingMenu(source->GetMenuItem()->GetRootMenuItem(),
1284                      screen_point, &anchor, &has_mnemonics, &button);
1285   if (!alt_menu || (state_.item && state_.item->GetRootMenuItem() == alt_menu))
1286     return false;
1287
1288   delegate_->SiblingMenuCreated(alt_menu);
1289
1290   if (!button) {
1291     // If the delegate returns a menu, they must also return a button.
1292     NOTREACHED();
1293     return false;
1294   }
1295
1296   // There is a sibling menu, update the button state, hide the current menu
1297   // and show the new one.
1298   menu_button_->SetState(CustomButton::STATE_NORMAL);
1299   menu_button_->SchedulePaint();
1300   menu_button_ = button;
1301   menu_button_->SetState(CustomButton::STATE_PRESSED);
1302   menu_button_->SchedulePaint();
1303
1304   // Need to reset capture when we show the menu again, otherwise we aren't
1305   // going to get any events.
1306   did_capture_ = false;
1307   gfx::Point screen_menu_loc;
1308   View::ConvertPointToScreen(button, &screen_menu_loc);
1309
1310   // It is currently not possible to show a submenu recursively in a bubble.
1311   DCHECK(!MenuItemView::IsBubble(anchor));
1312   // Subtract 1 from the height to make the popup flush with the button border.
1313   UpdateInitialLocation(gfx::Rect(screen_menu_loc.x(), screen_menu_loc.y(),
1314                                   button->width(), button->height() - 1),
1315                         anchor, state_.context_menu);
1316   alt_menu->PrepareForRun(
1317       false, has_mnemonics,
1318       source->GetMenuItem()->GetRootMenuItem()->show_mnemonics_);
1319   alt_menu->controller_ = this;
1320   SetSelection(alt_menu, SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
1321   return true;
1322 }
1323
1324 bool MenuController::ShowContextMenu(MenuItemView* menu_item,
1325                                      SubmenuView* source,
1326                                      const ui::LocatedEvent& event,
1327                                      ui::MenuSourceType source_type) {
1328   // Set the selection immediately, making sure the submenu is only open
1329   // if it already was.
1330   int selection_types = SELECTION_UPDATE_IMMEDIATELY;
1331   if (state_.item == pending_state_.item && state_.submenu_open)
1332     selection_types |= SELECTION_OPEN_SUBMENU;
1333   SetSelection(pending_state_.item, selection_types);
1334   gfx::Point loc(event.location());
1335   View::ConvertPointToScreen(source->GetScrollViewContainer(), &loc);
1336
1337   if (menu_item->GetDelegate()->ShowContextMenu(
1338           menu_item, menu_item->GetCommand(), loc, source_type)) {
1339     SendMouseCaptureLostToActiveView();
1340     return true;
1341   }
1342   return false;
1343 }
1344
1345 void MenuController::CloseAllNestedMenus() {
1346   for (std::list<State>::iterator i = menu_stack_.begin();
1347        i != menu_stack_.end(); ++i) {
1348     MenuItemView* last_item = i->item;
1349     for (MenuItemView* item = last_item; item;
1350          item = item->GetParentMenuItem()) {
1351       CloseMenu(item);
1352       last_item = item;
1353     }
1354     i->submenu_open = false;
1355     i->item = last_item;
1356   }
1357 }
1358
1359 MenuItemView* MenuController::GetMenuItemAt(View* source, int x, int y) {
1360   // Walk the view hierarchy until we find a menu item (or the root).
1361   View* child_under_mouse = source->GetEventHandlerForPoint(gfx::Point(x, y));
1362   while (child_under_mouse &&
1363          child_under_mouse->id() != MenuItemView::kMenuItemViewID) {
1364     child_under_mouse = child_under_mouse->parent();
1365   }
1366   if (child_under_mouse && child_under_mouse->enabled() &&
1367       child_under_mouse->id() == MenuItemView::kMenuItemViewID) {
1368     return static_cast<MenuItemView*>(child_under_mouse);
1369   }
1370   return NULL;
1371 }
1372
1373 MenuItemView* MenuController::GetEmptyMenuItemAt(View* source, int x, int y) {
1374   View* child_under_mouse = source->GetEventHandlerForPoint(gfx::Point(x, y));
1375   if (child_under_mouse &&
1376       child_under_mouse->id() == MenuItemView::kEmptyMenuItemViewID) {
1377     return static_cast<MenuItemView*>(child_under_mouse);
1378   }
1379   return NULL;
1380 }
1381
1382 bool MenuController::IsScrollButtonAt(SubmenuView* source,
1383                                       int x,
1384                                       int y,
1385                                       MenuPart::Type* part) {
1386   MenuScrollViewContainer* scroll_view = source->GetScrollViewContainer();
1387   View* child_under_mouse =
1388       scroll_view->GetEventHandlerForPoint(gfx::Point(x, y));
1389   if (child_under_mouse && child_under_mouse->enabled()) {
1390     if (child_under_mouse == scroll_view->scroll_up_button()) {
1391       *part = MenuPart::SCROLL_UP;
1392       return true;
1393     }
1394     if (child_under_mouse == scroll_view->scroll_down_button()) {
1395       *part = MenuPart::SCROLL_DOWN;
1396       return true;
1397     }
1398   }
1399   return false;
1400 }
1401
1402 MenuController::MenuPart MenuController::GetMenuPart(
1403     SubmenuView* source,
1404     const gfx::Point& source_loc) {
1405   gfx::Point screen_loc(source_loc);
1406   View::ConvertPointToScreen(source->GetScrollViewContainer(), &screen_loc);
1407   return GetMenuPartByScreenCoordinateUsingMenu(state_.item, screen_loc);
1408 }
1409
1410 MenuController::MenuPart MenuController::GetMenuPartByScreenCoordinateUsingMenu(
1411     MenuItemView* item,
1412     const gfx::Point& screen_loc) {
1413   MenuPart part;
1414   for (; item; item = item->GetParentMenuItem()) {
1415     if (item->HasSubmenu() && item->GetSubmenu()->IsShowing() &&
1416         GetMenuPartByScreenCoordinateImpl(item->GetSubmenu(), screen_loc,
1417                                           &part)) {
1418       return part;
1419     }
1420   }
1421   return part;
1422 }
1423
1424 bool MenuController::GetMenuPartByScreenCoordinateImpl(
1425     SubmenuView* menu,
1426     const gfx::Point& screen_loc,
1427     MenuPart* part) {
1428   // Is the mouse over the scroll buttons?
1429   gfx::Point scroll_view_loc = screen_loc;
1430   View* scroll_view_container = menu->GetScrollViewContainer();
1431   View::ConvertPointToTarget(NULL, scroll_view_container, &scroll_view_loc);
1432   if (scroll_view_loc.x() < 0 ||
1433       scroll_view_loc.x() >= scroll_view_container->width() ||
1434       scroll_view_loc.y() < 0 ||
1435       scroll_view_loc.y() >= scroll_view_container->height()) {
1436     // Point isn't contained in menu.
1437     return false;
1438   }
1439   if (IsScrollButtonAt(menu, scroll_view_loc.x(), scroll_view_loc.y(),
1440                        &(part->type))) {
1441     part->submenu = menu;
1442     return true;
1443   }
1444
1445   // Not over the scroll button. Check the actual menu.
1446   if (DoesSubmenuContainLocation(menu, screen_loc)) {
1447     gfx::Point menu_loc = screen_loc;
1448     View::ConvertPointToTarget(NULL, menu, &menu_loc);
1449     part->menu = GetMenuItemAt(menu, menu_loc.x(), menu_loc.y());
1450     part->type = MenuPart::MENU_ITEM;
1451     part->submenu = menu;
1452     if (!part->menu)
1453       part->parent = menu->GetMenuItem();
1454     return true;
1455   }
1456
1457   // While the mouse isn't over a menu item or the scroll buttons of menu, it
1458   // is contained by menu and so we return true. If we didn't return true other
1459   // menus would be searched, even though they are likely obscured by us.
1460   return true;
1461 }
1462
1463 bool MenuController::DoesSubmenuContainLocation(SubmenuView* submenu,
1464                                                 const gfx::Point& screen_loc) {
1465   gfx::Point view_loc = screen_loc;
1466   View::ConvertPointToTarget(NULL, submenu, &view_loc);
1467   gfx::Rect vis_rect = submenu->GetVisibleBounds();
1468   return vis_rect.Contains(view_loc.x(), view_loc.y());
1469 }
1470
1471 void MenuController::CommitPendingSelection() {
1472   StopShowTimer();
1473
1474   size_t paths_differ_at = 0;
1475   std::vector<MenuItemView*> current_path;
1476   std::vector<MenuItemView*> new_path;
1477   BuildPathsAndCalculateDiff(state_.item, pending_state_.item, &current_path,
1478                              &new_path, &paths_differ_at);
1479
1480   // Hide the old menu.
1481   for (size_t i = paths_differ_at; i < current_path.size(); ++i) {
1482     if (current_path[i]->HasSubmenu()) {
1483       current_path[i]->GetSubmenu()->Hide();
1484     }
1485   }
1486
1487   // Copy pending to state_, making sure to preserve the direction menus were
1488   // opened.
1489   std::list<bool> pending_open_direction;
1490   state_.open_leading.swap(pending_open_direction);
1491   state_ = pending_state_;
1492   state_.open_leading.swap(pending_open_direction);
1493
1494   int menu_depth = MenuDepth(state_.item);
1495   if (menu_depth == 0) {
1496     state_.open_leading.clear();
1497   } else {
1498     int cached_size = static_cast<int>(state_.open_leading.size());
1499     DCHECK_GE(menu_depth, 0);
1500     while (cached_size-- >= menu_depth)
1501       state_.open_leading.pop_back();
1502   }
1503
1504   if (!state_.item) {
1505     // Nothing to select.
1506     StopScrolling();
1507     return;
1508   }
1509
1510   // Open all the submenus preceeding the last menu item (last menu item is
1511   // handled next).
1512   if (new_path.size() > 1) {
1513     for (std::vector<MenuItemView*>::iterator i = new_path.begin();
1514          i != new_path.end() - 1; ++i) {
1515       OpenMenu(*i);
1516     }
1517   }
1518
1519   if (state_.submenu_open) {
1520     // The submenu should be open, open the submenu if the item has a submenu.
1521     if (state_.item->HasSubmenu()) {
1522       OpenMenu(state_.item);
1523     } else {
1524       state_.submenu_open = false;
1525     }
1526   } else if (state_.item->HasSubmenu() &&
1527              state_.item->GetSubmenu()->IsShowing()) {
1528     state_.item->GetSubmenu()->Hide();
1529   }
1530
1531   if (scroll_task_.get() && scroll_task_->submenu()) {
1532     // Stop the scrolling if none of the elements of the selection contain
1533     // the menu being scrolled.
1534     bool found = false;
1535     for (MenuItemView* item = state_.item; item && !found;
1536          item = item->GetParentMenuItem()) {
1537       found = (item->HasSubmenu() && item->GetSubmenu()->IsShowing() &&
1538                item->GetSubmenu() == scroll_task_->submenu());
1539     }
1540     if (!found)
1541       StopScrolling();
1542   }
1543 }
1544
1545 void MenuController::CloseMenu(MenuItemView* item) {
1546   DCHECK(item);
1547   if (!item->HasSubmenu())
1548     return;
1549   item->GetSubmenu()->Hide();
1550 }
1551
1552 void MenuController::OpenMenu(MenuItemView* item) {
1553   DCHECK(item);
1554   if (item->GetSubmenu()->IsShowing()) {
1555     return;
1556   }
1557
1558   OpenMenuImpl(item, true);
1559   did_capture_ = true;
1560 }
1561
1562 void MenuController::OpenMenuImpl(MenuItemView* item, bool show) {
1563   // TODO(oshima|sky): Don't show the menu if drag is in progress and
1564   // this menu doesn't support drag drop. See crbug.com/110495.
1565   if (show) {
1566     int old_count = item->GetSubmenu()->child_count();
1567     item->GetDelegate()->WillShowMenu(item);
1568     if (old_count != item->GetSubmenu()->child_count()) {
1569       // If the number of children changed then we may need to add empty items.
1570       item->AddEmptyMenus();
1571     }
1572   }
1573   bool prefer_leading =
1574       state_.open_leading.empty() ? true : state_.open_leading.back();
1575   bool resulting_direction;
1576   gfx::Rect bounds = MenuItemView::IsBubble(state_.anchor) ?
1577       CalculateBubbleMenuBounds(item, prefer_leading, &resulting_direction) :
1578       CalculateMenuBounds(item, prefer_leading, &resulting_direction);
1579   state_.open_leading.push_back(resulting_direction);
1580   bool do_capture = (!did_capture_ && blocking_run_);
1581   showing_submenu_ = true;
1582   if (show) {
1583     // Menus are the only place using kGroupingPropertyKey, so any value (other
1584     // than 0) is fine.
1585     const int kGroupingId = 1001;
1586     item->GetSubmenu()->ShowAt(owner_, bounds, do_capture);
1587     item->GetSubmenu()->GetWidget()->SetNativeWindowProperty(
1588         TooltipManager::kGroupingPropertyKey,
1589         reinterpret_cast<void*>(kGroupingId));
1590   } else {
1591     item->GetSubmenu()->Reposition(bounds);
1592   }
1593   showing_submenu_ = false;
1594 }
1595
1596 void MenuController::MenuChildrenChanged(MenuItemView* item) {
1597   DCHECK(item);
1598   // Menu shouldn't be updated during drag operation.
1599   DCHECK(!GetActiveMouseView());
1600
1601   // If the current item or pending item is a descendant of the item
1602   // that changed, move the selection back to the changed item.
1603   const MenuItemView* ancestor = state_.item;
1604   while (ancestor && ancestor != item)
1605     ancestor = ancestor->GetParentMenuItem();
1606   if (!ancestor) {
1607     ancestor = pending_state_.item;
1608     while (ancestor && ancestor != item)
1609       ancestor = ancestor->GetParentMenuItem();
1610     if (!ancestor)
1611       return;
1612   }
1613   SetSelection(item, SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
1614   if (item->HasSubmenu())
1615     OpenMenuImpl(item, false);
1616 }
1617
1618 void MenuController::BuildPathsAndCalculateDiff(
1619     MenuItemView* old_item,
1620     MenuItemView* new_item,
1621     std::vector<MenuItemView*>* old_path,
1622     std::vector<MenuItemView*>* new_path,
1623     size_t* first_diff_at) {
1624   DCHECK(old_path && new_path && first_diff_at);
1625   BuildMenuItemPath(old_item, old_path);
1626   BuildMenuItemPath(new_item, new_path);
1627
1628   size_t common_size = std::min(old_path->size(), new_path->size());
1629
1630   // Find the first difference between the two paths, when the loop
1631   // returns, diff_i is the first index where the two paths differ.
1632   for (size_t i = 0; i < common_size; ++i) {
1633     if ((*old_path)[i] != (*new_path)[i]) {
1634       *first_diff_at = i;
1635       return;
1636     }
1637   }
1638
1639   *first_diff_at = common_size;
1640 }
1641
1642 void MenuController::BuildMenuItemPath(MenuItemView* item,
1643                                        std::vector<MenuItemView*>* path) {
1644   if (!item)
1645     return;
1646   BuildMenuItemPath(item->GetParentMenuItem(), path);
1647   path->push_back(item);
1648 }
1649
1650 void MenuController::StartShowTimer() {
1651   show_timer_.Start(FROM_HERE,
1652                     TimeDelta::FromMilliseconds(menu_config_.show_delay),
1653                     this, &MenuController::CommitPendingSelection);
1654 }
1655
1656 void MenuController::StopShowTimer() {
1657   show_timer_.Stop();
1658 }
1659
1660 void MenuController::StartCancelAllTimer() {
1661   cancel_all_timer_.Start(FROM_HERE,
1662                           TimeDelta::FromMilliseconds(kCloseOnExitTime),
1663                           this, &MenuController::CancelAll);
1664 }
1665
1666 void MenuController::StopCancelAllTimer() {
1667   cancel_all_timer_.Stop();
1668 }
1669
1670 gfx::Rect MenuController::CalculateMenuBounds(MenuItemView* item,
1671                                               bool prefer_leading,
1672                                               bool* is_leading) {
1673   DCHECK(item);
1674
1675   SubmenuView* submenu = item->GetSubmenu();
1676   DCHECK(submenu);
1677
1678   gfx::Size pref = submenu->GetScrollViewContainer()->GetPreferredSize();
1679
1680   // Don't let the menu go too wide.
1681   pref.set_width(std::min(pref.width(),
1682                             item->GetDelegate()->GetMaxWidthForMenu(item)));
1683   if (!state_.monitor_bounds.IsEmpty())
1684     pref.set_width(std::min(pref.width(), state_.monitor_bounds.width()));
1685
1686   // Assume we can honor prefer_leading.
1687   *is_leading = prefer_leading;
1688
1689   int x, y;
1690
1691   const MenuConfig& menu_config = item->GetMenuConfig();
1692
1693   if (!item->GetParentMenuItem()) {
1694     // First item, position relative to initial location.
1695     x = state_.initial_bounds.x();
1696
1697     // Offsets for context menu prevent menu items being selected by
1698     // simply opening the menu (bug 142992).
1699     if (menu_config.offset_context_menus && state_.context_menu)
1700       x += 1;
1701
1702     y = state_.initial_bounds.bottom();
1703     if (state_.anchor == MenuItemView::TOPRIGHT) {
1704       x = x + state_.initial_bounds.width() - pref.width();
1705       if (menu_config.offset_context_menus && state_.context_menu)
1706         x -= 1;
1707     } else if (state_.anchor == MenuItemView::BOTTOMCENTER) {
1708       x = x - (pref.width() - state_.initial_bounds.width()) / 2;
1709       if (pref.height() >
1710           state_.initial_bounds.y() + kCenteredContextMenuYOffset) {
1711         // Menu does not fit above the anchor. We move it to below.
1712         y = state_.initial_bounds.y() - kCenteredContextMenuYOffset;
1713       } else {
1714         y = std::max(0, state_.initial_bounds.y() - pref.height()) +
1715             kCenteredContextMenuYOffset;
1716       }
1717     }
1718
1719     if (!state_.monitor_bounds.IsEmpty() &&
1720         y + pref.height() > state_.monitor_bounds.bottom()) {
1721       // The menu doesn't fit fully below the button on the screen. The menu
1722       // position with respect to the bounds will be preserved if it has
1723       // already been drawn. When the requested positioning is below the bounds
1724       // it will shrink the menu to make it fit below.
1725       // If the requested positioning is best fit, it will first try to fit the
1726       // menu below. If that does not fit it will try to place it above. If
1727       // that will not fit it will place it at the bottom of the work area and
1728       // moving it off the initial_bounds region to avoid overlap.
1729       // In all other requested position styles it will be flipped above and
1730       // the height will be shrunken to the usable height.
1731       if (item->actual_menu_position() == MenuItemView::POSITION_BELOW_BOUNDS) {
1732         pref.set_height(std::min(pref.height(),
1733                                  state_.monitor_bounds.bottom() - y));
1734       } else if (item->actual_menu_position() ==
1735                  MenuItemView::POSITION_BEST_FIT) {
1736         MenuItemView::MenuPosition orientation =
1737             MenuItemView::POSITION_BELOW_BOUNDS;
1738         if (state_.monitor_bounds.height() < pref.height()) {
1739           // Handle very tall menus.
1740           pref.set_height(state_.monitor_bounds.height());
1741           y = state_.monitor_bounds.y();
1742         } else if (state_.monitor_bounds.y() + pref.height() <
1743             state_.initial_bounds.y()) {
1744           // Flipping upwards if there is enough space.
1745           y = state_.initial_bounds.y() - pref.height();
1746           orientation = MenuItemView::POSITION_ABOVE_BOUNDS;
1747         } else {
1748           // It is allowed to move the menu a bit around in order to get the
1749           // best fit and to avoid showing scroll elements.
1750           y = state_.monitor_bounds.bottom() - pref.height();
1751         }
1752         if (orientation == MenuItemView::POSITION_BELOW_BOUNDS) {
1753           // The menu should never overlap the owning button. So move it.
1754           // We use the anchor view style to determine the preferred position
1755           // relative to the owning button.
1756           if (state_.anchor == MenuItemView::TOPLEFT) {
1757             // The menu starts with the same x coordinate as the owning button.
1758             if (x + state_.initial_bounds.width() + pref.width() >
1759                 state_.monitor_bounds.right())
1760               x -= pref.width();  // Move the menu to the left of the button.
1761             else
1762               x += state_.initial_bounds.width(); // Move the menu right.
1763           } else {
1764             // The menu should end with the same x coordinate as the owning
1765             // button.
1766             if (state_.monitor_bounds.x() >
1767                 state_.initial_bounds.x() - pref.width())
1768               x = state_.initial_bounds.right();  // Move right of the button.
1769             else
1770               x = state_.initial_bounds.x() - pref.width(); // Move left.
1771           }
1772         }
1773         item->set_actual_menu_position(orientation);
1774       } else {
1775         pref.set_height(std::min(pref.height(),
1776             state_.initial_bounds.y() - state_.monitor_bounds.y()));
1777         y = state_.initial_bounds.y() - pref.height();
1778         item->set_actual_menu_position(MenuItemView::POSITION_ABOVE_BOUNDS);
1779       }
1780     } else if (item->actual_menu_position() ==
1781                MenuItemView::POSITION_ABOVE_BOUNDS) {
1782       pref.set_height(std::min(pref.height(),
1783           state_.initial_bounds.y() - state_.monitor_bounds.y()));
1784       y = state_.initial_bounds.y() - pref.height();
1785     } else {
1786       item->set_actual_menu_position(MenuItemView::POSITION_BELOW_BOUNDS);
1787     }
1788     if (state_.monitor_bounds.width() != 0 &&
1789         menu_config.offset_context_menus && state_.context_menu) {
1790       if (x + pref.width() > state_.monitor_bounds.right())
1791         x = state_.initial_bounds.x() - pref.width() - 1;
1792       if (x < state_.monitor_bounds.x())
1793         x = state_.monitor_bounds.x();
1794     }
1795   } else {
1796     // Not the first menu; position it relative to the bounds of the menu
1797     // item.
1798     gfx::Point item_loc;
1799     View::ConvertPointToScreen(item, &item_loc);
1800
1801     // We must make sure we take into account the UI layout. If the layout is
1802     // RTL, then a 'leading' menu is positioned to the left of the parent menu
1803     // item and not to the right.
1804     bool layout_is_rtl = base::i18n::IsRTL();
1805     bool create_on_the_right = (prefer_leading && !layout_is_rtl) ||
1806                                (!prefer_leading && layout_is_rtl);
1807     int submenu_horizontal_inset = menu_config.submenu_horizontal_inset;
1808
1809     if (create_on_the_right) {
1810       x = item_loc.x() + item->width() - submenu_horizontal_inset;
1811       if (state_.monitor_bounds.width() != 0 &&
1812           x + pref.width() > state_.monitor_bounds.right()) {
1813         if (layout_is_rtl)
1814           *is_leading = true;
1815         else
1816           *is_leading = false;
1817         x = item_loc.x() - pref.width() + submenu_horizontal_inset;
1818       }
1819     } else {
1820       x = item_loc.x() - pref.width() + submenu_horizontal_inset;
1821       if (state_.monitor_bounds.width() != 0 && x < state_.monitor_bounds.x()) {
1822         if (layout_is_rtl)
1823           *is_leading = false;
1824         else
1825           *is_leading = true;
1826         x = item_loc.x() + item->width() - submenu_horizontal_inset;
1827       }
1828     }
1829     y = item_loc.y() - menu_config.menu_vertical_border_size;
1830     if (state_.monitor_bounds.width() != 0) {
1831       pref.set_height(std::min(pref.height(), state_.monitor_bounds.height()));
1832       if (y + pref.height() > state_.monitor_bounds.bottom())
1833         y = state_.monitor_bounds.bottom() - pref.height();
1834       if (y < state_.monitor_bounds.y())
1835         y = state_.monitor_bounds.y();
1836     }
1837   }
1838
1839   if (state_.monitor_bounds.width() != 0) {
1840     if (x + pref.width() > state_.monitor_bounds.right())
1841       x = state_.monitor_bounds.right() - pref.width();
1842     if (x < state_.monitor_bounds.x())
1843       x = state_.monitor_bounds.x();
1844   }
1845   return gfx::Rect(x, y, pref.width(), pref.height());
1846 }
1847
1848 gfx::Rect MenuController::CalculateBubbleMenuBounds(MenuItemView* item,
1849                                                     bool prefer_leading,
1850                                                     bool* is_leading) {
1851   DCHECK(item);
1852   DCHECK(!item->GetParentMenuItem());
1853
1854   // Assume we can honor prefer_leading.
1855   *is_leading = prefer_leading;
1856
1857   SubmenuView* submenu = item->GetSubmenu();
1858   DCHECK(submenu);
1859
1860   gfx::Size pref = submenu->GetScrollViewContainer()->GetPreferredSize();
1861   const gfx::Rect& owner_bounds = pending_state_.initial_bounds;
1862
1863   // First the size gets reduced to the possible space.
1864   if (!state_.monitor_bounds.IsEmpty()) {
1865     int max_width = state_.monitor_bounds.width();
1866     int max_height = state_.monitor_bounds.height();
1867     // In case of bubbles, the maximum width is limited by the space
1868     // between the display corner and the target area + the tip size.
1869     if (state_.anchor == MenuItemView::BUBBLE_LEFT) {
1870       max_width = owner_bounds.x() - state_.monitor_bounds.x() +
1871                   kBubbleTipSizeLeftRight;
1872     } else if (state_.anchor == MenuItemView::BUBBLE_RIGHT) {
1873       max_width = state_.monitor_bounds.right() - owner_bounds.right() +
1874                   kBubbleTipSizeLeftRight;
1875     } else if (state_.anchor == MenuItemView::BUBBLE_ABOVE) {
1876       max_height = owner_bounds.y() - state_.monitor_bounds.y() +
1877                    kBubbleTipSizeTopBottom;
1878     } else if (state_.anchor == MenuItemView::BUBBLE_BELOW) {
1879       max_height = state_.monitor_bounds.bottom() - owner_bounds.bottom() +
1880                    kBubbleTipSizeTopBottom;
1881     }
1882     // The space for the menu to cover should never get empty.
1883     DCHECK_GE(max_width, kBubbleTipSizeLeftRight);
1884     DCHECK_GE(max_height, kBubbleTipSizeTopBottom);
1885     pref.set_width(std::min(pref.width(), max_width));
1886     pref.set_height(std::min(pref.height(), max_height));
1887   }
1888   // Also make sure that the menu does not go too wide.
1889   pref.set_width(std::min(pref.width(),
1890                           item->GetDelegate()->GetMaxWidthForMenu(item)));
1891
1892   int x, y;
1893   if (state_.anchor == MenuItemView::BUBBLE_ABOVE ||
1894       state_.anchor == MenuItemView::BUBBLE_BELOW) {
1895     if (state_.anchor == MenuItemView::BUBBLE_ABOVE)
1896       y = owner_bounds.y() - pref.height() + kBubbleTipSizeTopBottom;
1897     else
1898       y = owner_bounds.bottom() - kBubbleTipSizeTopBottom;
1899
1900     x = owner_bounds.CenterPoint().x() - pref.width() / 2;
1901     int x_old = x;
1902     if (x < state_.monitor_bounds.x()) {
1903       x = state_.monitor_bounds.x();
1904     } else if (x + pref.width() > state_.monitor_bounds.right()) {
1905       x = state_.monitor_bounds.right() - pref.width();
1906     }
1907     submenu->GetScrollViewContainer()->SetBubbleArrowOffset(
1908         pref.width() / 2 - x + x_old);
1909   } else {
1910     if (state_.anchor == MenuItemView::BUBBLE_RIGHT)
1911       x = owner_bounds.right() - kBubbleTipSizeLeftRight;
1912     else
1913       x = owner_bounds.x() - pref.width() + kBubbleTipSizeLeftRight;
1914
1915     y = owner_bounds.CenterPoint().y() - pref.height() / 2;
1916     int y_old = y;
1917     if (y < state_.monitor_bounds.y()) {
1918       y = state_.monitor_bounds.y();
1919     } else if (y + pref.height() > state_.monitor_bounds.bottom()) {
1920       y = state_.monitor_bounds.bottom() - pref.height();
1921     }
1922     submenu->GetScrollViewContainer()->SetBubbleArrowOffset(
1923         pref.height() / 2 - y + y_old);
1924   }
1925   return gfx::Rect(x, y, pref.width(), pref.height());
1926 }
1927
1928 // static
1929 int MenuController::MenuDepth(MenuItemView* item) {
1930   return item ? (MenuDepth(item->GetParentMenuItem()) + 1) : 0;
1931 }
1932
1933 void MenuController::IncrementSelection(int delta) {
1934   MenuItemView* item = pending_state_.item;
1935   DCHECK(item);
1936   if (pending_state_.submenu_open && item->HasSubmenu() &&
1937       item->GetSubmenu()->IsShowing()) {
1938     // A menu is selected and open, but none of its children are selected,
1939     // select the first menu item.
1940     if (item->GetSubmenu()->GetMenuItemCount()) {
1941       SetSelection(item->GetSubmenu()->GetMenuItemAt(0), SELECTION_DEFAULT);
1942       return;
1943     }
1944   }
1945
1946   if (item->has_children()) {
1947     View* hot_view = GetFirstHotTrackedView(item);
1948     if (hot_view &&
1949         !strcmp(hot_view->GetClassName(), CustomButton::kViewClassName)) {
1950       CustomButton* button = static_cast<CustomButton*>(hot_view);
1951       button->SetHotTracked(false);
1952       View* to_make_hot = GetNextFocusableView(item, button, delta == 1);
1953       if (to_make_hot &&
1954           !strcmp(to_make_hot->GetClassName(), CustomButton::kViewClassName)) {
1955         CustomButton* button_hot = static_cast<CustomButton*>(to_make_hot);
1956         button_hot->SetHotTracked(true);
1957         return;
1958       }
1959     } else {
1960       View* to_make_hot = GetInitialFocusableView(item, delta == 1);
1961       if (to_make_hot &&
1962           !strcmp(to_make_hot->GetClassName(), CustomButton::kViewClassName)) {
1963         CustomButton* button_hot = static_cast<CustomButton*>(to_make_hot);
1964         button_hot->SetHotTracked(true);
1965         return;
1966       }
1967     }
1968   }
1969
1970   MenuItemView* parent = item->GetParentMenuItem();
1971   if (parent) {
1972     int parent_count = parent->GetSubmenu()->GetMenuItemCount();
1973     if (parent_count > 1) {
1974       for (int i = 0; i < parent_count; ++i) {
1975         if (parent->GetSubmenu()->GetMenuItemAt(i) == item) {
1976           MenuItemView* to_select =
1977               FindNextSelectableMenuItem(parent, i, delta);
1978           if (!to_select)
1979             break;
1980           SetSelection(to_select, SELECTION_DEFAULT);
1981           View* to_make_hot = GetInitialFocusableView(to_select, delta == 1);
1982           if (to_make_hot && !strcmp(to_make_hot->GetClassName(),
1983                                      CustomButton::kViewClassName)) {
1984             CustomButton* button_hot = static_cast<CustomButton*>(to_make_hot);
1985             button_hot->SetHotTracked(true);
1986           }
1987           break;
1988         }
1989       }
1990     }
1991   }
1992 }
1993
1994 MenuItemView* MenuController::FindNextSelectableMenuItem(MenuItemView* parent,
1995                                                          int index,
1996                                                          int delta) {
1997   int start_index = index;
1998   int parent_count = parent->GetSubmenu()->GetMenuItemCount();
1999   // Loop through the menu items skipping any invisible menus. The loop stops
2000   // when we wrap or find a visible child.
2001   do {
2002     index = (index + delta + parent_count) % parent_count;
2003     if (index == start_index)
2004       return NULL;
2005     MenuItemView* child = parent->GetSubmenu()->GetMenuItemAt(index);
2006     if (child->visible())
2007       return child;
2008   } while (index != start_index);
2009   return NULL;
2010 }
2011
2012 void MenuController::OpenSubmenuChangeSelectionIfCan() {
2013   MenuItemView* item = pending_state_.item;
2014   if (item->HasSubmenu() && item->enabled()) {
2015     if (item->GetSubmenu()->GetMenuItemCount() > 0) {
2016       SetSelection(item->GetSubmenu()->GetMenuItemAt(0),
2017                    SELECTION_UPDATE_IMMEDIATELY);
2018     } else {
2019       // No menu items, just show the sub-menu.
2020       SetSelection(item, SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
2021     }
2022   }
2023 }
2024
2025 void MenuController::CloseSubmenu() {
2026   MenuItemView* item = state_.item;
2027   DCHECK(item);
2028   if (!item->GetParentMenuItem())
2029     return;
2030   if (item->HasSubmenu() && item->GetSubmenu()->IsShowing())
2031     SetSelection(item, SELECTION_UPDATE_IMMEDIATELY);
2032   else if (item->GetParentMenuItem()->GetParentMenuItem())
2033     SetSelection(item->GetParentMenuItem(), SELECTION_UPDATE_IMMEDIATELY);
2034 }
2035
2036 MenuController::SelectByCharDetails MenuController::FindChildForMnemonic(
2037     MenuItemView* parent,
2038     char16 key,
2039     bool (*match_function)(MenuItemView* menu, char16 mnemonic)) {
2040   SubmenuView* submenu = parent->GetSubmenu();
2041   DCHECK(submenu);
2042   SelectByCharDetails details;
2043
2044   for (int i = 0, menu_item_count = submenu->GetMenuItemCount();
2045        i < menu_item_count; ++i) {
2046     MenuItemView* child = submenu->GetMenuItemAt(i);
2047     if (child->enabled() && child->visible()) {
2048       if (child == pending_state_.item)
2049         details.index_of_item = i;
2050       if (match_function(child, key)) {
2051         if (details.first_match == -1)
2052           details.first_match = i;
2053         else
2054           details.has_multiple = true;
2055         if (details.next_match == -1 && details.index_of_item != -1 &&
2056             i > details.index_of_item)
2057           details.next_match = i;
2058       }
2059     }
2060   }
2061   return details;
2062 }
2063
2064 bool MenuController::AcceptOrSelect(MenuItemView* parent,
2065                                     const SelectByCharDetails& details) {
2066   // This should only be invoked if there is a match.
2067   DCHECK(details.first_match != -1);
2068   DCHECK(parent->HasSubmenu());
2069   SubmenuView* submenu = parent->GetSubmenu();
2070   DCHECK(submenu);
2071   if (!details.has_multiple) {
2072     // There's only one match, activate it (or open if it has a submenu).
2073     if (submenu->GetMenuItemAt(details.first_match)->HasSubmenu()) {
2074       SetSelection(submenu->GetMenuItemAt(details.first_match),
2075                    SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
2076     } else {
2077       Accept(submenu->GetMenuItemAt(details.first_match), 0);
2078       return true;
2079     }
2080   } else if (details.index_of_item == -1 || details.next_match == -1) {
2081     SetSelection(submenu->GetMenuItemAt(details.first_match),
2082                  SELECTION_DEFAULT);
2083   } else {
2084     SetSelection(submenu->GetMenuItemAt(details.next_match),
2085                  SELECTION_DEFAULT);
2086   }
2087   return false;
2088 }
2089
2090 bool MenuController::SelectByChar(char16 character) {
2091   char16 char_array[] = { character, 0 };
2092   char16 key = base::i18n::ToLower(char_array)[0];
2093   MenuItemView* item = pending_state_.item;
2094   if (!item->HasSubmenu() || !item->GetSubmenu()->IsShowing())
2095     item = item->GetParentMenuItem();
2096   DCHECK(item);
2097   DCHECK(item->HasSubmenu());
2098   DCHECK(item->GetSubmenu());
2099   if (item->GetSubmenu()->GetMenuItemCount() == 0)
2100     return false;
2101
2102   // Look for matches based on mnemonic first.
2103   SelectByCharDetails details =
2104       FindChildForMnemonic(item, key, &MatchesMnemonic);
2105   if (details.first_match != -1)
2106     return AcceptOrSelect(item, details);
2107
2108   // If no mnemonics found, look at first character of titles.
2109   details = FindChildForMnemonic(item, key, &TitleMatchesMnemonic);
2110   if (details.first_match != -1)
2111     return AcceptOrSelect(item, details);
2112
2113   return false;
2114 }
2115
2116 void MenuController::RepostEvent(SubmenuView* source,
2117                                  const ui::LocatedEvent& event) {
2118   gfx::Point screen_loc(event.location());
2119   View::ConvertPointToScreen(source->GetScrollViewContainer(), &screen_loc);
2120
2121   gfx::NativeView native_view = source->GetWidget()->GetNativeView();
2122   gfx::Screen* screen = gfx::Screen::GetScreenFor(native_view);
2123   gfx::NativeWindow window = screen->GetWindowAtScreenPoint(screen_loc);
2124
2125   if (!window)
2126     return;
2127
2128 #if defined(OS_WIN)
2129   // Release the capture.
2130   SubmenuView* submenu = state_.item->GetRootMenuItem()->GetSubmenu();
2131   submenu->ReleaseCapture();
2132
2133   gfx::NativeView view = submenu->GetWidget()->GetNativeView();
2134   if (view) {
2135     DWORD view_tid = GetWindowThreadProcessId(HWNDForNativeView(view), NULL);
2136     if (view_tid != GetWindowThreadProcessId(HWNDForNativeView(window), NULL)) {
2137       // Even though we have mouse capture, windows generates a mouse event if
2138       // the other window is in a separate thread. Only repost an event if
2139       // |view| was created on the same thread, else the target window can get
2140       // double events leading to bad behavior.
2141       return;
2142     }
2143   }
2144 #endif
2145
2146   scoped_ptr<ui::LocatedEvent> clone;
2147   if (event.IsMouseEvent()) {
2148     clone.reset(new ui::MouseEvent(static_cast<const ui::MouseEvent&>(event)));
2149   } else if (event.IsGestureEvent()) {
2150     // TODO(rbyers): Gesture event repost is tricky to get right
2151     // crbug.com/170987.
2152     return;
2153   } else {
2154     NOTREACHED();
2155     return;
2156   }
2157   clone->set_location(screen_loc);
2158
2159   RepostLocatedEvent(window, *clone);
2160 }
2161
2162
2163 void MenuController::SetDropMenuItem(
2164     MenuItemView* new_target,
2165     MenuDelegate::DropPosition new_position) {
2166   if (new_target == drop_target_ && new_position == drop_position_)
2167     return;
2168
2169   if (drop_target_) {
2170     drop_target_->GetParentMenuItem()->GetSubmenu()->SetDropMenuItem(
2171         NULL, MenuDelegate::DROP_NONE);
2172   }
2173   drop_target_ = new_target;
2174   drop_position_ = new_position;
2175   if (drop_target_) {
2176     drop_target_->GetParentMenuItem()->GetSubmenu()->SetDropMenuItem(
2177         drop_target_, drop_position_);
2178   }
2179 }
2180
2181 void MenuController::UpdateScrolling(const MenuPart& part) {
2182   if (!part.is_scroll() && !scroll_task_.get())
2183     return;
2184
2185   if (!scroll_task_.get())
2186     scroll_task_.reset(new MenuScrollTask());
2187   scroll_task_->Update(part);
2188 }
2189
2190 void MenuController::StopScrolling() {
2191   scroll_task_.reset(NULL);
2192 }
2193
2194 void MenuController::UpdateActiveMouseView(SubmenuView* event_source,
2195                                            const ui::MouseEvent& event,
2196                                            View* target_menu) {
2197   View* target = NULL;
2198   gfx::Point target_menu_loc(event.location());
2199   if (target_menu && target_menu->has_children()) {
2200     // Locate the deepest child view to send events to.  This code assumes we
2201     // don't have to walk up the tree to find a view interested in events. This
2202     // is currently true for the cases we are embedding views, but if we embed
2203     // more complex hierarchies it'll need to change.
2204     View::ConvertPointToScreen(event_source->GetScrollViewContainer(),
2205                                &target_menu_loc);
2206     View::ConvertPointToTarget(NULL, target_menu, &target_menu_loc);
2207     target = target_menu->GetEventHandlerForPoint(target_menu_loc);
2208     if (target == target_menu || !target->enabled())
2209       target = NULL;
2210   }
2211   View* active_mouse_view = GetActiveMouseView();
2212   if (target != active_mouse_view) {
2213     SendMouseCaptureLostToActiveView();
2214     active_mouse_view = target;
2215     SetActiveMouseView(active_mouse_view);
2216     if (active_mouse_view) {
2217       gfx::Point target_point(target_menu_loc);
2218       View::ConvertPointToTarget(
2219           target_menu, active_mouse_view, &target_point);
2220       ui::MouseEvent mouse_entered_event(ui::ET_MOUSE_ENTERED,
2221                                          target_point, target_point,
2222                                          0);
2223       active_mouse_view->OnMouseEntered(mouse_entered_event);
2224
2225       ui::MouseEvent mouse_pressed_event(ui::ET_MOUSE_PRESSED,
2226                                          target_point, target_point,
2227                                          event.flags());
2228       active_mouse_view->OnMousePressed(mouse_pressed_event);
2229     }
2230   }
2231
2232   if (active_mouse_view) {
2233     gfx::Point target_point(target_menu_loc);
2234     View::ConvertPointToTarget(target_menu, active_mouse_view, &target_point);
2235     ui::MouseEvent mouse_dragged_event(ui::ET_MOUSE_DRAGGED,
2236                                        target_point, target_point,
2237                                        event.flags());
2238     active_mouse_view->OnMouseDragged(mouse_dragged_event);
2239   }
2240 }
2241
2242 void MenuController::SendMouseReleaseToActiveView(SubmenuView* event_source,
2243                                                   const ui::MouseEvent& event) {
2244   View* active_mouse_view = GetActiveMouseView();
2245   if (!active_mouse_view)
2246     return;
2247
2248   gfx::Point target_loc(event.location());
2249   View::ConvertPointToScreen(event_source->GetScrollViewContainer(),
2250                              &target_loc);
2251   View::ConvertPointToTarget(NULL, active_mouse_view, &target_loc);
2252   ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, target_loc, target_loc,
2253                                event.flags());
2254   // Reset active mouse view before sending mouse released. That way if it calls
2255   // back to us, we aren't in a weird state.
2256   SetActiveMouseView(NULL);
2257   active_mouse_view->OnMouseReleased(release_event);
2258 }
2259
2260 void MenuController::SendMouseCaptureLostToActiveView() {
2261   View* active_mouse_view = GetActiveMouseView();
2262   if (!active_mouse_view)
2263     return;
2264
2265   // Reset the active_mouse_view_ before sending mouse capture lost. That way if
2266   // it calls back to us, we aren't in a weird state.
2267   SetActiveMouseView(NULL);
2268   active_mouse_view->OnMouseCaptureLost();
2269 }
2270
2271 void MenuController::SetActiveMouseView(View* view) {
2272   if (view)
2273     ViewStorage::GetInstance()->StoreView(active_mouse_view_id_, view);
2274   else
2275     ViewStorage::GetInstance()->RemoveView(active_mouse_view_id_);
2276 }
2277
2278 View* MenuController::GetActiveMouseView() {
2279   return ViewStorage::GetInstance()->RetrieveView(active_mouse_view_id_);
2280 }
2281
2282 void MenuController::SetExitType(ExitType type) {
2283   exit_type_ = type;
2284   // Exit nested message loops as soon as possible. We do this as
2285   // MessageLoop::Dispatcher is only invoked before native events, which means
2286   // its entirely possible for a Widget::CloseNow() task to be processed before
2287   // the next native message. By using QuitNow() we ensures the nested message
2288   // loop returns as soon as possible and avoids having deleted views classes
2289   // (such as widgets and rootviews) on the stack when the nested message loop
2290   // stops.
2291   //
2292   // It's safe to invoke QuitNow multiple times, it only effects the current
2293   // loop.
2294   bool quit_now = ShouldQuitNow() && exit_type_ != EXIT_NONE &&
2295       message_loop_depth_;
2296
2297   if (quit_now)
2298     base::MessageLoop::current()->QuitNow();
2299 }
2300
2301 void MenuController::HandleMouseLocation(SubmenuView* source,
2302                                          const gfx::Point& mouse_location) {
2303   if (showing_submenu_)
2304     return;
2305
2306   // Ignore mouse events if we're closing the menu.
2307   if (exit_type_ != EXIT_NONE)
2308     return;
2309
2310   MenuPart part = GetMenuPart(source, mouse_location);
2311
2312   UpdateScrolling(part);
2313
2314   if (!blocking_run_)
2315     return;
2316
2317   if (part.type == MenuPart::NONE && ShowSiblingMenu(source, mouse_location))
2318     return;
2319
2320   if (part.type == MenuPart::MENU_ITEM && part.menu) {
2321     SetSelection(part.menu, SELECTION_OPEN_SUBMENU);
2322   } else if (!part.is_scroll() && pending_state_.item &&
2323              pending_state_.item->GetParentMenuItem() &&
2324              (!pending_state_.item->HasSubmenu() ||
2325               !pending_state_.item->GetSubmenu()->IsShowing())) {
2326     // On exit if the user hasn't selected an item with a submenu, move the
2327     // selection back to the parent menu item.
2328     SetSelection(pending_state_.item->GetParentMenuItem(),
2329                  SELECTION_OPEN_SUBMENU);
2330   }
2331 }
2332
2333 }  // namespace views