1// Copyright 2014 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_message_pump_dispatcher_win.h"
6
7#include <windowsx.h>
8
9#include "ui/events/event_utils.h"
10#include "ui/events/keycodes/keyboard_code_conversion.h"
11#include "ui/events/keycodes/keyboard_codes.h"
12#include "ui/views/controls/menu/menu_controller.h"
13#include "ui/views/controls/menu/menu_item_view.h"
14
15namespace views {
16namespace internal {
17
18MenuMessagePumpDispatcher::MenuMessagePumpDispatcher(MenuController* controller)
19    : menu_controller_(controller) {}
20
21MenuMessagePumpDispatcher::~MenuMessagePumpDispatcher() {}
22
23uint32_t MenuMessagePumpDispatcher::Dispatch(const MSG& msg) {
24  DCHECK(menu_controller_->IsBlockingRun());
25
26  bool should_quit = false;
27  bool should_perform_default = true;
28  if (menu_controller_->exit_type() == MenuController::EXIT_ALL ||
29      menu_controller_->exit_type() == MenuController::EXIT_DESTROYED) {
30    should_quit = true;
31  } else {
32    // NOTE: we don't get WM_ACTIVATE or anything else interesting in here.
33    switch (msg.message) {
34      case WM_CONTEXTMENU: {
35        MenuItemView* item = menu_controller_->pending_state_.item;
36        if (item && item->GetRootMenuItem() != item) {
37          gfx::Point screen_loc(0, item->height());
38          View::ConvertPointToScreen(item, &screen_loc);
39          ui::MenuSourceType source_type = ui::MENU_SOURCE_MOUSE;
40          if (GET_X_LPARAM(msg.lParam) == -1 && GET_Y_LPARAM(msg.lParam) == -1)
41            source_type = ui::MENU_SOURCE_KEYBOARD;
42          item->GetDelegate()->ShowContextMenu(
43              item, item->GetCommand(), screen_loc, source_type);
44        }
45        should_quit = false;
46        should_perform_default = false;
47        break;
48      }
49
50      // NOTE: focus wasn't changed when the menu was shown. As such, don't
51      // dispatch key events otherwise the focused window will get the events.
52      case WM_KEYDOWN: {
53        bool result =
54            menu_controller_->OnKeyDown(ui::KeyboardCodeFromNative(msg));
55        TranslateMessage(&msg);
56        should_perform_default = false;
57        should_quit = !result;
58        break;
59      }
60      case WM_CHAR: {
61        should_quit = menu_controller_->SelectByChar(
62            static_cast<base::char16>(msg.wParam));
63        should_perform_default = false;
64        break;
65      }
66      case WM_KEYUP:
67      case WM_SYSKEYUP:
68        // We may have been shown on a system key, as such don't do anything
69        // here. If another system key is pushed we'll get a WM_SYSKEYDOWN and
70        // close the menu.
71        should_quit = false;
72        should_perform_default = false;
73        break;
74
75      case WM_CANCELMODE:
76      case WM_SYSKEYDOWN:
77        // Exit immediately on system keys.
78        menu_controller_->Cancel(MenuController::EXIT_ALL);
79        should_quit = true;
80        should_perform_default = false;
81        break;
82
83      default:
84        break;
85    }
86  }
87
88  if (should_quit || menu_controller_->exit_type() != MenuController::EXIT_NONE)
89    menu_controller_->TerminateNestedMessageLoop();
90  return should_perform_default ? POST_DISPATCH_PERFORM_DEFAULT
91                                : POST_DISPATCH_NONE;
92}
93
94}  // namespace internal
95}  // namespace views
96