summaryrefslogtreecommitdiffstats
path: root/chrome/views/chrome_menu.h
blob: a1ed4edeba0e1a37f84643ddfb8b61fc4fd2d913 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_VIEWS_CHROME_MENU_H__
#define CHROME_VIEWS_CHROME_MENU_H__

#include <list>

#include "base/gfx/point.h"
#include "base/gfx/rect.h"
#include "base/message_loop.h"
#include "base/task.h"
#include "chrome/common/drag_drop_types.h"
#include "chrome/common/gfx/chrome_font.h"
#include "chrome/views/controller.h"
#include "chrome/views/view.h"
#include "skia/include/SkBitmap.h"

class Timer;

namespace ChromeViews {

class HWNDViewContainer;
class MenuController;
class MenuItemView;
class SubmenuView;

namespace {
class MenuHost;
class MenuHostRootView;
class MenuScrollTask;
class MenuScrollViewContainer;
}

// MenuDelegate --------------------------------------------------------------

// Delegate for the menu.

class MenuDelegate : Controller {
 public:
  // Used during drag and drop to indicate where the drop indicator should
  // be rendered.
  enum DropPosition {
    // Indicates a drop is not allowed here.
    DROP_NONE,

    // Indicates the drop should occur before the item.
    DROP_BEFORE,

    // Indicates the drop should occur after the item.
    DROP_AFTER,

    // Indicates the drop should occur on the item.
    DROP_ON
  };

  // Whether or not an item should be shown as checked.
  // TODO(sky): need checked support.
  virtual bool IsItemChecked(int id) const {
    return false;
  }

  // The string shown for the menu item. This is only invoked when an item is
  // added with an empty label.
  virtual std::wstring GetLabel(int id) const {
    return std::wstring();
  }

  // Shows the context menu with the specified id. This is invoked when the
  // user does the appropriate gesture to show a context menu. The id
  // identifies the id of the menu to show the context menu for.
  // is_mouse_gesture is true if this is the result of a mouse gesture.
  // If this is not the result of a mouse gesture x/y is the recommended
  // location to display the content menu at. In either case, x/y is in
  // screen coordinates.
  virtual void ShowContextMenu(MenuItemView* source,
                               int id,
                               int x,
                               int y,
                               bool is_mouse_gesture) {
  }

  // Controller
  virtual bool SupportsCommand(int id) const {
    return true;
  }
  virtual bool IsCommandEnabled(int id) const {
    return true;
  }
  virtual bool GetContextualLabel(int id, std::wstring* out) const {
    return false;
  }
  virtual void ExecuteCommand(int id) {
  }

  // Executes the specified command. mouse_event_flags give the flags of the
  // mouse event that triggered this to be invoked (ChromeViews::MouseEvent
  // flags). mouse_event_flags is 0 if this is triggered by a user gesture
  // other than a mouse event.
  virtual void ExecuteCommand(int id, int mouse_event_flags) {
    ExecuteCommand(id);
  }

  // Returns true if the specified mouse event is one the user can use
  // to trigger, or accept, the mouse. Defaults to only left mouse buttons.
  virtual bool IsTriggerableEvent(const MouseEvent& e) {
    return e.IsLeftMouseButton();
  }

  // Invoked to determine if drops can be accepted for a submenu. This is
  // ONLY invoked for menus that have submenus and indicates whether or not
  // a drop can occur on any of the child items of the item. For example,
  // consider the following menu structure:
  //
  // A
  //   B
  //   C
  //
  // Where A has a submenu with children B and C. This is ONLY invoked for
  // A, not B and C.
  //
  // To restrict which children can be dropped on override GetDropOperation.
  virtual bool CanDrop(MenuItemView* menu, const OSExchangeData& data) {
    return false;
  }

  // Returns the drop operation for the specified target menu item. This is
  // only invoked if CanDrop returned true for the parent menu. position
  // is set based on the location of the mouse, reset to specify a different
  // position.
  //
  // If a drop should not be allowed, returned DragDropTypes::DRAG_NONE.
  virtual int GetDropOperation(MenuItemView* item,
                               const DropTargetEvent& event,
                               DropPosition* position) {
    NOTREACHED() << "If you override CanDrop, you need to override this too";
    return DragDropTypes::DRAG_NONE;
  }

  // Invoked to perform the drop operation. This is ONLY invoked if
  // canDrop returned true for the parent menu item, and GetDropOperation
  // returned an operation other than DragDropTypes::DRAG_NONE.
  //
  // menu indicates the menu the drop occurred on.
  virtual int OnPerformDrop(MenuItemView* menu,
                            DropPosition position,
                            const DropTargetEvent& event) {
    NOTREACHED() << "If you override CanDrop, you need to override this too";
    return DragDropTypes::DRAG_NONE;
  }

  // Invoked to determine if it is possible for the user to drag the specified
  // menu item.
  virtual bool CanDrag(MenuItemView* menu) {
    return false;
  }

  // Invoked to write the data for a drag operation to data. sender is the
  // MenuItemView being dragged.
  virtual void WriteDragData(MenuItemView* sender, OSExchangeData* data) {
    NOTREACHED() << "If you override CanDrag, you must override this too.";
  }

  // Invoked to determine the drag operations for a drag session of sender.
  // See DragDropTypes for possible values.
  virtual int GetDragOperations(MenuItemView* sender) {
    NOTREACHED() << "If you override CanDrag, you must override this too.";
    return 0;
  }

  // Notification the menu has closed. This is only sent when running the
  // menu for a drop.
  virtual void DropMenuClosed(MenuItemView* menu) {
  }

  // Notification that the user has highlighted the specified item.
  virtual void SelectionChanged(MenuItemView* menu) {
  }
};

// MenuItemView --------------------------------------------------------------

// MenuItemView represents a single menu item with a label and optional icon.
// Each MenuItemView may also contain a submenu, which in turn may contain
// any number of child MenuItemViews.
//
// To use a menu create an initial MenuItemView using the constructor that
// takes a MenuDelegate, then create any number of child menu items by way
// of the various AddXXX methods.
//
// MenuItemView is itself a View, which means you can add Views to each
// MenuItemView. This normally NOT want you want, rather add other child Views
// to the submenu of the MenuItemView.
//
// There are two ways to show a MenuItemView:
// 1. Use RunMenuAt. This blocks the caller, executing the selected command
//    on success.
// 2. Use RunMenuForDropAt. This is intended for use during a drop session
//    and does NOT block the caller. Instead the delegate is notified when the
//    menu closes via the DropMenuClosed method.

class MenuItemView : public View {
  friend class MenuController;

 public:
  // ID used to identify menu items.
  static const int kMenuItemViewID;

  // Used to determine whether a drop is on an item or before/after it. If
  // a drop occurs kDropBetweenPixels from the top/bottom it is considered
  // before/after the menu item, otherwise it is on the item.
  static const int kDropBetweenPixels;

  // If true SetNestableTasksAllowed(true) is invoked before MessageLoop::Run
  // is invoked. This is only useful for testing and defaults to false.
  static bool allow_task_nesting_during_run_;

  // Different types of menu items.
  enum Type {
    NORMAL,
    SUBMENU,
    CHECKBOX,
    RADIO,
    SEPARATOR
  };

  // Where the menu should be anchored to.
  enum AnchorPosition {
    TOPLEFT,
    TOPRIGHT
  };

  // Constructor for use with the top level menu item. This menu is never
  // shown to the user, rather its use as the parent for all menu items.
  explicit MenuItemView(MenuDelegate* delegate);

  virtual ~MenuItemView();

  // Run methods. See description above class for details. Both Run methods take
  // a rectangle, which is used to position the menu.
  void RunMenuAt(HWND parent,
                 const gfx::Rect& bounds,
                 AnchorPosition anchor,
                 bool show_mnemonics);
  void RunMenuForDropAt(HWND parent,
                        const gfx::Rect& bounds,
                        AnchorPosition anchor);

  // Hides and cancels the menu. This does nothing if the menu is not open.
  void Cancel();

  // Adds an item to this menu.
  // item_id    The id of the item, used to identify it in delegate callbacks
  //            or (if delegate is NULL) to identify the command associated
  //            with this item with the controller specified in the ctor. Note
  //            that this value should not be 0 as this has a special meaning
  //            ("NULL command, no item selected")
  // label      The text label shown.
  // type       The type of item.
  void AppendMenuItem(int item_id,
                      const std::wstring& label,
                      Type type) {
    AppendMenuItemInternal(item_id, label, SkBitmap(), type);
  }

  // Append a submenu to this menu.
  // The returned pointer is owned by this menu.
  MenuItemView* AppendSubMenu(int item_id,
                              const std::wstring& label) {
    return AppendMenuItemInternal(item_id, label, SkBitmap(), SUBMENU);
  }

  // Append a submenu with an icon to this menu.
  // The returned pointer is owned by this menu.
  MenuItemView* AppendSubMenuWithIcon(int item_id,
                                      const std::wstring& label,
                                      const SkBitmap& icon) {
    return AppendMenuItemInternal(item_id, label, icon, SUBMENU);
  }

  // This is a convenience for standard text label menu items where the label
  // is provided with this call.
  void AppendMenuItemWithLabel(int item_id,
                               const std::wstring& label) {
    AppendMenuItem(item_id, label, NORMAL);
  }

  // This is a convenience for text label menu items where the label is
  // provided by the delegate.
  void AppendDelegateMenuItem(int item_id) {
    AppendMenuItem(item_id, std::wstring(), NORMAL);
  }

  // Adds a separator to this menu
  void AppendSeparator() {
    AppendMenuItemInternal(0, std::wstring(), SkBitmap(), SEPARATOR);
  }

  // Appends a menu item with an icon. This is for the menu item which
  // needs an icon. Calling this function forces the Menu class to draw
  // the menu, instead of relying on Windows.
  void AppendMenuItemWithIcon(int item_id,
                              const std::wstring& label,
                              const SkBitmap& icon) {
    AppendMenuItemInternal(item_id, label, icon, NORMAL);
  }

  // Returns the view that contains child menu items. If the submenu has
  // not been creates, this creates it.
  virtual SubmenuView* CreateSubmenu();

  // Returns true if this menu item has a submenu.
  virtual bool HasSubmenu() const { return (submenu_ != NULL); }

  // Returns the view containing child menu items.
  virtual SubmenuView* GetSubmenu() const { return submenu_; }

  // Returns the parent menu item.
  MenuItemView* GetParentMenuItem() const { return parent_menu_item_; }

  // Sets the font.
  void SetFont(const ChromeFont& font) { font_ = font; }

  // Sets the title
  void SetTitle(const std::wstring& title) {
    title_ = title;
  }

  // Returns the title.
  const std::wstring& GetTitle() const { return title_; }

  // Sets whether this item is selected. This is invoked as the user moves
  // the mouse around the menu while open.
  void SetSelected(bool selected);

  // Returns true if the item is selected.
  bool IsSelected() const { return selected_; }

  // Sets the icon for the descendant identified by item_id.
  void SetIcon(const SkBitmap& icon, int item_id);

  // Sets the icon of this menu item.
  void SetIcon(const SkBitmap& icon);

  // Returns the icon.
  const SkBitmap& GetIcon() const { return icon_; }

  // Sets the command id of this menu item.
  void SetCommand(int command) { command_ = command; }

  // Returns the command id of this item.
  int GetCommand() const { return command_; }

  // Paints the menu item.
  virtual void Paint(ChromeCanvas* canvas);

  // Returns the preferred size of this item.
  virtual void GetPreferredSize(CSize* out);

  // Returns the object responsible for controlling showing the menu.
  MenuController* GetMenuController();

  // Returns the delegate. This returns the delegate of the root menu item.
  MenuDelegate* GetDelegate();

  // Returns the root parent, or this if this has no parent.
  MenuItemView* GetRootMenuItem();

  // Returns the mnemonic for this MenuItemView, or 0 if this MenuItemView
  // doesn't have a mnemonic.
  wchar_t GetMnemonic();

 protected:
  // Creates a MenuItemView. This is used by the various AddXXX methods.
  MenuItemView(MenuItemView* parent, int command, Type type);

 private:
  // Called by the two constructors to initialize this menu item.
  void Init(MenuItemView* parent,
            int command,
            MenuItemView::Type type,
            MenuDelegate* delegate);

  // All the AddXXX methods funnel into this.
  MenuItemView* AppendMenuItemInternal(int item_id,
                                       const std::wstring& label,
                                       const SkBitmap& icon,
                                       Type type);

  // Returns the descendant with the specified command.
  MenuItemView* GetDescendantByID(int id);

  // Invoked by the MenuController when the menu closes as the result of
  // drag and drop run.
  void DropMenuClosed(bool notify_delegate);

  // The RunXXX methods call into this to set up the necessary state before
  // running.
  void PrepareForRun(bool show_mnemonics);

  // Returns the flags passed to DrawStringInt.
  int GetDrawStringFlags();

  // If this menu item has no children a child is added showing it has no
  // children. Otherwise AddEmtpyMenuIfNecessary is recursively invoked on
  // child menu items that have children.
  void AddEmptyMenus();

  // Undoes the work of AddEmptyMenus.
  void RemoveEmptyMenus();

  // Given bounds within our View, this helper routine mirrors the bounds if
  // necessary.
  void AdjustBoundsForRTLUI(RECT* rect) const;

  // Actual paint implementation. If for_drag is true, portions of the menu
  // are not rendered.
  void Paint(ChromeCanvas* canvas, bool for_drag);

  // Destroys the window used to display this menu and recursively destroys
  // the windows used to display all descendants.
  void DestroyAllMenuHosts();

  // The delegate. This is only valid for the root menu item. You shouldn't
  // use this directly, instead use GetDelegate() which walks the tree as
  // as necessary.
  MenuDelegate* delegate_;

  // Returns the controller for the run operation, or NULL if the menu isn't
  // showing.
  MenuController* controller_;

  // Used to detect when Cancel was invoked.
  bool canceled_;

  // Our parent.
  MenuItemView* parent_menu_item_;

  // Type of menu. NOTE: MenuItemView doesn't itself represent SEPARATOR,
  // that is handled by an entirely different view class.
  Type type_;

  // Whether we're selected.
  bool selected_;

  // Command id.
  int command_;

  // Submenu, created via CreateSubmenu.
  SubmenuView* submenu_;

  // Font.
  ChromeFont font_;

  // Title.
  std::wstring title_;

  // Icon.
  SkBitmap icon_;

  // Whether mnemonics should be shown.
  bool show_mnemonics_;

  DISALLOW_EVIL_CONSTRUCTORS(MenuItemView);
};

// SubmenuView ----------------------------------------------------------------

// SubmenuView is the parent of all menu items.
//
// SubmenuView has the following responsibilities:
// . It positions and sizes all child views (any type of View may be added,
//   not just MenuItemViews).
// . Forwards the appropriate events to the MenuController. This allows the
//   MenuController to update the selection as the user moves the mouse around.
// . Renders the drop indicator during a drop operation.
// . Shows and hides the window (an HWNDViewContainer) when the menu is
//   shown on screen.
//
// SubmenuView is itself contained in a MenuScrollViewContainer.
// MenuScrollViewContainer handles showing as much of the SubmenuView as the
// screen allows. If the SubmenuView is taller than the screen, scroll buttons
// are provided that allow the user to see all the menu items.
class SubmenuView : public View {
 public:
  // Creates a SubmenuView for the specified menu item.
  explicit SubmenuView(MenuItemView* parent);
  ~SubmenuView();

  // Returns the number of child views that are MenuItemViews.
  // MenuItemViews are identified by ID.
  int GetMenuItemCount();

  // Returns the MenuItemView at the specified index.
  MenuItemView* GetMenuItemAt(int index);

  // Positions and sizes the child views. This tiles the views vertically,
  // giving each child the available width.
  virtual void Layout();
  virtual void GetPreferredSize(CSize* out);

  // View method. Overriden to schedule a paint. We do this so that when
  // scrolling occurs, everything is repainted correctly.
  virtual void DidChangeBounds(const CRect& previous, const CRect& current);

  // Painting.
  void PaintChildren(ChromeCanvas* canvas);

  // Drag and drop methods. These are forwarded to the MenuController.
  virtual bool CanDrop(const OSExchangeData& data);
  virtual void OnDragEntered(const DropTargetEvent& event);
  virtual int OnDragUpdated(const DropTargetEvent& event);
  virtual void OnDragExited();
  virtual int OnPerformDrop(const DropTargetEvent& event);

  // Scrolls on menu item boundaries.
  virtual bool OnMouseWheel(const MouseWheelEvent& e);

  // Returns true if the menu is showing.
  bool IsShowing() const { return (host_ != NULL); }

  // Shows the menu at the specified location. Coordinates are in screen
  // coordinates. max_width gives the max width the view should be.
  void ShowAt(HWND parent, const gfx::Rect& bounds, bool do_capture);

  // Closes the menu. If destroy_host is true, the host is deleted.
  void Close(bool destroy_host);

  // If mouse capture was grabbed, it is released. Does nothing if mouse was
  // not captured.
  void ReleaseCapture();

  // Returns the parent menu item we're showing children for.
  MenuItemView* GetMenuItem() const { return parent_menu_item_; }

  // Overriden to return true. This prevents tab from doing anything.
  virtual bool CanProcessTabKeyEvents() { return true; }

  // Set the drop item and position.
  void SetDropMenuItem(MenuItemView* item,
                       MenuDelegate::DropPosition position);

  // Returns whether the selection should be shown for the specified item.
  // The selection is NOT shown during drag and drop when the drop is over
  // the menu.
  bool GetShowSelection(MenuItemView* item);

  // Returns the container for the SubmenuView.
  MenuScrollViewContainer* GetScrollViewContainer();

  // Returns the host of the menu. Returns NULL if not showing.
  MenuHost* host() const { return host_; }

 private:
  // Paints the drop indicator. This is only invoked if item is non-NULL and
  // position is not DROP_NONE.
  void PaintDropIndicator(ChromeCanvas* canvas,
                          MenuItemView* item,
                          MenuDelegate::DropPosition position);

  void SchedulePaintForDropIndicator(MenuItemView* item,
                                     MenuDelegate::DropPosition position);

  // Calculates the location of th edrop indicator.
  gfx::Rect CalculateDropIndicatorBounds(MenuItemView* item,
                                         MenuDelegate::DropPosition position);

  // Parent menu item.
  MenuItemView* parent_menu_item_;

  // HWNDViewContainer subclass used to show the children.
  MenuHost* host_;

  // If non-null, indicates a drop is in progress and drop_item is the item
  // the drop is over.
  MenuItemView* drop_item_;

  // Position of the drop.
  MenuDelegate::DropPosition drop_position_;

  // Ancestor of the SubmenuView, lazily created.
  MenuScrollViewContainer* scroll_view_container_;

  DISALLOW_EVIL_CONSTRUCTORS(SubmenuView);
};

// MenuController -------------------------------------------------------------

// MenuController manages showing, selecting and drag/drop for menus.
// All relevant events are forwarded to the MenuController from SubmenuView
// and MenuHost.

class MenuController : public MessageLoop::Dispatcher {
 public:
  friend class MenuHostRootView;
  friend class MenuItemView;
  friend class ShowSubmenusTask;
  friend class MenuScrollTask;

  // If a menu is currently active, this returns the controller for it.
  static MenuController* GetActiveInstance();

  // Runs the menu at the specified location. If the menu was configured to
  // block, the selected item is returned. If the menu does not block this
  // returns NULL immediately.
  MenuItemView* Run(HWND parent,
                    MenuItemView* root,
                    const gfx::Rect& bounds,
                    MenuItemView::AnchorPosition position,
                    int* mouse_event_flags);

  // Whether or not Run blocks.
  bool IsBlockingRun() const { return blocking_run_; }

  // Sets the selection to menu_item, a value of NULL unselects everything.
  // If open_submenu is true and menu_item has a submenu, the submenu is shown.
  // If update_immediately is true, submenus are opened immediately, otherwise
  // submenus are only opened after a timer fires.
  //
  // Internally this updates pending_state_ immediatley, and if
  // update_immediately is true, CommitPendingSelection is invoked to
  // show/hide submenus and update state_.
  void SetSelection(MenuItemView* menu_item,
                    bool open_submenu,
                    bool update_immediately);

  // Cancels the current Run. If all is true, any nested loops are canceled
  // as well. This immediatley hides all menus.
  void Cancel(bool all);

  // Various events, forwarded from the submenu.
  //
  // NOTE: the coordinates of the events are in that of the
  // MenuScrollViewContainer.
  void OnMousePressed(SubmenuView* source, const MouseEvent& event);
  void OnMouseDragged(SubmenuView* source, const MouseEvent& event);
  void OnMouseReleased(SubmenuView* source, const MouseEvent& event);
  void OnMouseMoved(SubmenuView* source, const MouseEvent& event);
  void OnMouseEntered(SubmenuView* source, const MouseEvent& event);
  bool CanDrop(SubmenuView* source, const OSExchangeData& data);
  void OnDragEntered(SubmenuView* source, const DropTargetEvent& event);
  int OnDragUpdated(SubmenuView* source, const DropTargetEvent& event);
  void OnDragExited(SubmenuView* source);
  int OnPerformDrop(SubmenuView* source, const DropTargetEvent& event);

  // Invoked from the scroll buttons of the MenuScrollViewContainer.
  void OnDragEnteredScrollButton(SubmenuView* source, bool is_up);
  void OnDragExitedScrollButton(SubmenuView* source);

 private:
  // As the mouse moves around submenus are not opened immediately. Instead
  // they open after this timer fires.
  class ShowSubmenusTask : public Task {
   public:
    explicit ShowSubmenusTask(MenuController* controller)
        : controller_(controller) {}

    virtual void Run() {
      controller_->CommitPendingSelection();
    }

   private:
    MenuController* controller_;

    DISALLOW_EVIL_CONSTRUCTORS(ShowSubmenusTask);
  };

  // Task used to invoke Cancel(true). This is used during drag and drop
  // to hide the menu after the mouse moves out of the of the menu. This is
  // necessitated by the lack of an ability to detect when the drag has
  // completed from the drop side.
  class CancelAllTask : public Task {
   public:
    explicit CancelAllTask(MenuController* controller)
        : controller_(controller) {}

    virtual void Run() {
      controller_->Cancel(true);
    }

   private:
    MenuController* controller_;

    DISALLOW_EVIL_CONSTRUCTORS(CancelAllTask);
  };

  // Tracks selection information.
  struct State {
    State() : item(NULL), submenu_open(false) {}

    // The selected menu item.
    MenuItemView* item;

    // If item has a submenu this indicates if the submenu is showing.
    bool submenu_open;

    // Bounds passed to the run menu. Used for positioning the first menu.
    gfx::Rect initial_bounds;

    // Position of the initial menu.
    MenuItemView::AnchorPosition anchor;

    // The direction child menus have opened in.
    std::list<bool> open_leading;

    // Bounds for the monitor we're showing on.
    gfx::Rect monitor_bounds;
  };

  // Used by GetMenuPartByScreenCoordinate to indicate the menu part at a
  // particular location.
  struct MenuPart {
    // Type of part.
    enum Type {
      NONE,
      MENU_ITEM,
      SCROLL_UP,
      SCROLL_DOWN
    };

    MenuPart() : type(NONE), menu(NULL), submenu(NULL) {}

    // Convenience for testing type == SCROLL_DOWN or type == SCROLL_UP.
    bool is_scroll() const { return type == SCROLL_DOWN || type == SCROLL_UP; }

    // Type of part.
    Type type;

    // If type is MENU_ITEM, this is the menu item the mouse is over, otherwise
    // this is NULL.
    // NOTE: if type is MENU_ITEM and the mouse is not over a valid menu item
    //       but is over a menu (for example, the mouse is over a separator or
    //       empty menu), this is NULL.
    MenuItemView* menu;

    // If type is SCROLL_*, this is the submenu the mouse is over.
    SubmenuView* submenu;
  };

  // Sets the active MenuController.
  static void SetActiveInstance(MenuController* controller);

  // Dispatcher method. This returns true if the menu was canceled, or
  // if the message is such that the menu should be closed.
  virtual bool Dispatch(const MSG& msg);

  // Key processing. The return value of these is returned from Dispatch.
  // In other words, if these return false (which they do if escape was
  // pressed, or a matching mnemonic was found) the message loop returns.
  bool OnKeyDown(const MSG& msg);
  bool OnChar(const MSG& msg);

  // Creates a MenuController. If blocking is true, Run blocks the caller
  explicit MenuController(bool blocking);

  ~MenuController();

  // Invoked when the user accepts the selected item. This is only used
  // when blocking. This schedules the loop to quit.
  void Accept(MenuItemView* item, int mouse_event_flags);

  // Closes all menus, including any menus of nested invocations of Run.
  void CloseAllNestedMenus();

  // Gets the enabled menu item at the specified location.
  // If over_any_menu is non-null it is set to indicate whether the location
  // is over any menu. It is possible for this to return NULL, but
  // over_any_menu to be true. For example, the user clicked on a separator.
  MenuItemView* GetMenuItemAt(View* menu, int x, int y);

  // If there is an empty menu item at the specified location, it is returned.
  MenuItemView* GetEmptyMenuItemAt(View* source, int x, int y);

  // Returns true if the coordinate is over the scroll buttons of the
  // SubmenuView's MenuScrollViewContainer. If true is returned, part is set to
  // indicate which scroll button the coordinate is.
  bool IsScrollButtonAt(SubmenuView* source,
                        int x,
                        int y,
                        MenuPart::Type* part);

  // Returns the target for the mouse event.
  MenuPart GetMenuPartByScreenCoordinate(SubmenuView* source,
                                         int source_x,
                                         int source_y);

  // Implementation of GetMenuPartByScreenCoordinate for a single menu. Returns
  // true if the supplied SubmenuView contains the location in terms of the
  // screen. If it does, part is set appropriately and true is returned.
  bool GetMenuPartByScreenCoordinateImpl(SubmenuView* menu,
                                         const CPoint& screen_loc,
                                         MenuPart* part);

  // Returns true if the SubmenuView contains the specified location. This does
  // NOT included the scroll buttons, only the submenu view.
  bool DoesSubmenuContainLocation(SubmenuView* submenu,
                                  const CPoint& screen_loc);

  // Opens/Closes the necessary menus such that state_ matches that of
  // pending_state_. This is invoked if submenus are not opened immediately,
  // but after a delay.
  void CommitPendingSelection();

  // If item has a submenu, it is closed. This does NOT update the selection
  // in anyway.
  void CloseMenu(MenuItemView* item);

  // If item has a submenu, it is opened. This does NOT update the selection
  // in anyway.
  void OpenMenu(MenuItemView* item);

  // Builds the paths of the two menu items into the two paths, and
  // sets first_diff_at to the location of the first difference between the
  // two paths.
  void BuildPathsAndCalculateDiff(MenuItemView* old_item,
                                  MenuItemView* new_item,
                                  std::vector<MenuItemView*>* old_path,
                                  std::vector<MenuItemView*>* new_path,
                                  size_t* first_diff_at);

  // Builds the path for the specified item.
  void BuildMenuItemPath(MenuItemView* item, std::vector<MenuItemView*>* path);

  // Starts/stops the timer that commits the pending state to state
  // (opens/closes submenus).
  void StartShowTimer();
  void StopShowTimer();

  // Starts/stops the timer cancel the menu. This is used during drag and
  // drop when the drop enters/exits the menu.
  void StartCancelAllTimer();
  void StopCancelAllTimer();

  // Calculates the bounds of the menu to show. is_leading is set to match the
  // direction the menu opened in.
  gfx::Rect CalculateMenuBounds(MenuItemView* item,
                                bool prefer_leading,
                                bool* is_leading);

  // Returns the depth of the menu.
  static int MenuDepth(MenuItemView* item);

  // Selects the next/previous menu item.
  void IncrementSelection(int delta);

  // If the selected item has a submenu and it isn't currently open, the
  // the selection is changed such that the menu opens immediately.
  void OpenSubmenuChangeSelectionIfCan();

  // If possible, closes the submenu.
  void CloseSubmenu();

  // Returns true if window is the window used to show item, or any of
  // items ancestors.
  bool IsMenuWindow(MenuItemView* item, HWND window);

  // Selects by mnemonic, and if that doesn't work tries the first character of
  // the title. Returns true if a match was selected and the menu should exit.
  bool SelectByChar(wchar_t key);

  // If there is a window at the location of the event, a new mouse event is
  // generated and posted to it.
  void RepostEvent(SubmenuView* source, const MouseEvent& event);

  // Sets the drop target to new_item.
  void SetDropMenuItem(MenuItemView* new_item,
                       MenuDelegate::DropPosition position);

  // Starts/stops scrolling as appropriate. part gives the part the mouse is
  // over.
  void UpdateScrolling(const MenuPart& part);

  // Stops scrolling.
  void StopScrolling();

  // The active instance.
  static MenuController* active_instance_;

  // If true, Run blocks. If false, Run doesn't block and this is used for
  // drag and drop. Note that the semantics for drag and drop are slightly
  // different: cancel timer is kicked off any time the drag moves outside the
  // menu, mouse events do nothing...
  bool blocking_run_;

  // If true, we're showing.
  bool showing_;

  // If true, all nested run loops should be exited.
  bool exit_all_;

  // Whether we did a capture. We do a capture only if we're blocking and
  // the mouse was down when Run.
  bool did_capture_;

  // As the user drags the mouse around pending_state_ changes immediately.
  // When the user stops moving/dragging the mouse (or clicks the mouse)
  // pending_state_ is committed to state_, potentially resulting in
  // opening or closing submenus. This gives a slight delayed effect to
  // submenus as the user moves the mouse around. This is done so that as the
  // user moves the mouse all submenus don't immediately pop.
  State pending_state_;
  State state_;

  // If the user accepted the selection, this is the result.
  MenuItemView* result_;

  // The mouse event flags when the user clicked on a menu. Is 0 if the
  // user did not use the mousee to select the menu.
  int result_mouse_event_flags_;

  // If not empty, it means we're nested. When Run is invoked from within
  // Run, the current state (state_) is pushed onto menu_stack_. This allows
  // MenuController to restore the state when the nested run returns.
  std::list<State> menu_stack_;

  // Used to comming pending to state.
  ShowSubmenusTask show_task_;
  Timer* show_timer_;

  // Used to cancel all menus.
  CancelAllTask cancel_all_task_;
  Timer* cancel_all_timer_;

  // Drop target.
  MenuItemView* drop_target_;
  MenuDelegate::DropPosition drop_position_;

  // Owner of child windows.
  HWND owner_;

  // Indicates a possible drag operation.
  bool possible_drag_;

  // Location the mouse was pressed at. Used to detect d&d.
  int press_x_;
  int press_y_;

  // We get a slew of drag updated messages as the mouse is over us. To avoid
  // continually processing whether we can drop, we cache the coordinates.
  bool valid_drop_coordinates_;
  int drop_x_;
  int drop_y_;
  int last_drop_operation_;

  // If true, we've invoked DoDrag on the delegate.
  //
  // This is used to determine whether the windows used to show menus
  // are hidden or destroyed.  As drag and drop is initiated during a
  // mouse gesture, and the call to initiate the drag is blocking, we
  // can't destroy the window immediately (otherwise when the drag and
  // drop call returns the window that initiated the call and is on
  // the stack has been deleted).  During drag and drop (in_drag_ is
  // true) windows are hidden, once the drag and drop call completes
  // MenuItemView takes care of destroying the windows.
  bool in_drag_;

  // If true, the mouse is over some menu.
  bool any_menu_contains_mouse_;

  // If true, we're in the middle of invoking ShowAt on a submenu.
  bool showing_submenu_;

  // Task for scrolling the menu. If non-null indicates a scroll is currently
  // underway.
  scoped_ptr<MenuScrollTask> scroll_task_;

  DISALLOW_EVIL_CONSTRUCTORS(MenuController);
};

} // namespace

#endif  // CHROME_VIEWS_CHROME_MENU_H__