accelerator_dispatcher.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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 "ash/accelerators/accelerator_dispatcher.h"
6
7#if defined(USE_X11)
8#include <X11/Xlib.h>
9
10// Xlib defines RootWindow
11#ifdef RootWindow
12#undef RootWindow
13#endif
14#endif  // defined(USE_X11)
15
16#include "ash/accelerators/accelerator_controller.h"
17#include "ash/shell.h"
18#include "ash/wm/event_rewriter_event_filter.h"
19#include "ui/aura/env.h"
20#include "ui/aura/root_window.h"
21#include "ui/base/accelerators/accelerator.h"
22#include "ui/base/events/event.h"
23#include "ui/base/events/event_constants.h"
24#include "ui/base/events/event_utils.h"
25#include "ui/views/controls/menu/menu_controller.h"
26
27namespace ash {
28namespace {
29
30const int kModifierMask = (ui::EF_SHIFT_DOWN |
31                           ui::EF_CONTROL_DOWN |
32                           ui::EF_ALT_DOWN);
33#if defined(OS_WIN)
34bool IsKeyEvent(const MSG& msg) {
35  return
36      msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN ||
37      msg.message == WM_KEYUP || msg.message == WM_SYSKEYUP;
38}
39#elif defined(USE_X11)
40bool IsKeyEvent(const XEvent* xev) {
41  return xev->type == KeyPress || xev->type == KeyRelease;
42}
43#endif
44
45bool IsPossibleAcceleratorNotForMenu(const ui::KeyEvent& key_event) {
46  // For shortcuts generated by Ctrl or Alt plus a letter, number or
47  // the tab key, we want to exit the context menu first and then
48  // repost the event. That allows for the shortcut execution after
49  // the context menu has exited.
50  if (key_event.type() == ui::ET_KEY_PRESSED &&
51      (key_event.flags() & (ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN))) {
52    const ui::KeyboardCode key_code = key_event.key_code();
53    if ((key_code >= ui::VKEY_A && key_code <= ui::VKEY_Z) ||
54        (key_code >= ui::VKEY_0 && key_code <= ui::VKEY_9) ||
55        (key_code == ui::VKEY_TAB)) {
56      return true;
57    }
58  }
59  return false;
60}
61
62}  // namespace
63
64AcceleratorDispatcher::AcceleratorDispatcher(
65    MessageLoop::Dispatcher* nested_dispatcher, aura::Window* associated_window)
66    : nested_dispatcher_(nested_dispatcher),
67      associated_window_(associated_window) {
68  DCHECK(nested_dispatcher_);
69  associated_window_->AddObserver(this);
70}
71
72AcceleratorDispatcher::~AcceleratorDispatcher() {
73  if (associated_window_)
74    associated_window_->RemoveObserver(this);
75}
76
77void AcceleratorDispatcher::OnWindowDestroying(aura::Window* window) {
78  if (associated_window_ == window)
79    associated_window_ = NULL;
80}
81
82bool AcceleratorDispatcher::Dispatch(const base::NativeEvent& event) {
83  if (!associated_window_)
84    return false;
85  if (!ui::IsNoopEvent(event) && !associated_window_->CanReceiveEvents())
86    return aura::Env::GetInstance()->GetDispatcher()->Dispatch(event);
87
88  if (IsKeyEvent(event)) {
89    // Modifiers can be changed by the user preference, so we need to rewrite
90    // the event explicitly.
91    ui::KeyEvent key_event(event, false);
92    ui::EventHandler* event_rewriter =
93        ash::Shell::GetInstance()->event_rewriter_filter();
94    DCHECK(event_rewriter);
95    event_rewriter->OnKeyEvent(&key_event);
96    if (key_event.stopped_propagation())
97      return true;
98
99    if (IsPossibleAcceleratorNotForMenu(key_event)) {
100      if (views::MenuController* menu_controller =
101          views::MenuController::GetActiveInstance()) {
102        menu_controller->CancelAll();
103#if defined(USE_X11)
104        XPutBackEvent(event->xany.display, event);
105#else
106        NOTIMPLEMENTED() << " Repost NativeEvent here.";
107#endif
108        return false;
109      }
110    }
111
112    ash::AcceleratorController* accelerator_controller =
113        ash::Shell::GetInstance()->accelerator_controller();
114    if (accelerator_controller) {
115      ui::Accelerator accelerator(key_event.key_code(),
116                                  key_event.flags() & kModifierMask);
117      if (key_event.type() == ui::ET_KEY_RELEASED)
118        accelerator.set_type(ui::ET_KEY_RELEASED);
119      // Fill out context object so AcceleratorController will know what
120      // was the previous accelerator or if the current accelerator is repeated.
121      Shell::GetInstance()->accelerator_controller()->context()->
122          UpdateContext(accelerator);
123      if (accelerator_controller->Process(accelerator))
124        return true;
125    }
126
127    return nested_dispatcher_->Dispatch(key_event.native_event());
128  }
129
130  return nested_dispatcher_->Dispatch(event);
131}
132
133}  // namespace ash
134