Add ShellAcceleratorController that manages global keyboard accelerators.

- Create ShellAcceleratorController that manages global keyboard accelerators and also processes several accelerators as a target.
- Create ShellAcceleratorFilter, which is used by DesktopEventFilter to handle accelerators.
- Add a function to Shell for accessing ShellAcceleratorController.

The 1st attempt (http://crrev.com/110637) broke aura_shell_unittests and this fixes it.
The differences are
- moving ShellAcceleratorFilter to Shell from DesktopEventFilter, and
- adding the ShellAcceleratorFilter in Shell initialization code to DesktopEventFilter.

BUG=97255
TEST=Manual, aura_shell_unittests succeeds.


Review URL: http://codereview.chromium.org/8561012

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@111100 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/ui/aura/desktop.cc b/ui/aura/desktop.cc
index 74b5e4e127..056490b 100644
--- a/ui/aura/desktop.cc
+++ b/ui/aura/desktop.cc
@@ -26,10 +26,7 @@
 #include "ui/base/hit_test.h"
 #include "ui/gfx/compositor/compositor.h"
 #include "ui/gfx/compositor/layer.h"
-#include "ui/gfx/compositor/layer_animation_sequence.h"
 #include "ui/gfx/compositor/layer_animator.h"
-#include "ui/gfx/compositor/screen_rotation.h"
-#include "ui/gfx/interpolated_transform.h"
 
 #ifdef USE_WEBKIT_COMPOSITOR
 #include "ui/gfx/compositor/compositor_cc.h"
@@ -99,50 +96,6 @@
   }
 }
 
-#if !defined(NDEBUG)
-bool MaybeFullScreen(DesktopHost* host, KeyEvent* event) {
-  if (event->key_code() == ui::VKEY_F11) {
-    host->ToggleFullScreen();
-    return true;
-  }
-  return false;
-}
-
-bool MaybeRotate(Desktop* desktop, KeyEvent* event) {
-  if ((event->flags() & ui::EF_CONTROL_DOWN) &&
-      event->key_code() == ui::VKEY_HOME) {
-    static int i = 0;
-    int delta = 0;
-    switch (i) {
-      case 0: delta = 90; break;
-      case 1: delta = 90; break;
-      case 2: delta = 90; break;
-      case 3: delta = 90; break;
-      case 4: delta = -90; break;
-      case 5: delta = -90; break;
-      case 6: delta = -90; break;
-      case 7: delta = -90; break;
-      case 8: delta = -90; break;
-      case 9: delta = 180; break;
-      case 10: delta = 180; break;
-      case 11: delta = 90; break;
-      case 12: delta = 180; break;
-      case 13: delta = 180; break;
-    }
-    i = (i + 1) % 14;
-    desktop->layer()->GetAnimator()->set_preemption_strategy(
-        ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
-    scoped_ptr<ui::LayerAnimationSequence> screen_rotation(
-        new ui::LayerAnimationSequence(new ui::ScreenRotation(delta)));
-    screen_rotation->AddObserver(desktop);
-    desktop->layer()->GetAnimator()->ScheduleAnimation(
-        screen_rotation.release());
-    return true;
-  }
-  return false;
-}
-#endif
-
 }  // namespace
 
 Desktop* Desktop::instance_ = NULL;
@@ -285,14 +238,6 @@
 }
 
 bool Desktop::DispatchKeyEvent(KeyEvent* event) {
-#if !defined(NDEBUG)
-  // TODO(beng): replace this hack with global keyboard event handlers.
-  if (event->type() == ui::ET_KEY_PRESSED) {
-    if (MaybeFullScreen(host_.get(), event) || MaybeRotate(this, event))
-      return true;
-  }
-#endif
-
   if (focused_window_) {
     KeyEvent translated_event(*event);
     return ProcessKeyEvent(focused_window_, &translated_event);
@@ -470,6 +415,12 @@
     OnHostResized(host_->GetSize());
 }
 
+#if !defined(NDEBUG)
+void Desktop::ToggleFullScreen() {
+  host_->ToggleFullScreen();
+}
+#endif
+
 void Desktop::HandleMouseMoved(const MouseEvent& event, Window* target) {
   if (target == mouse_moved_handler_)
     return;
diff --git a/ui/aura/desktop.h b/ui/aura/desktop.h
index 3f27d4b..7e8d0dc 100644
--- a/ui/aura/desktop.h
+++ b/ui/aura/desktop.h
@@ -142,6 +142,11 @@
   // Overridden from Window:
   virtual void SetTransform(const ui::Transform& transform) OVERRIDE;
 
+#if !defined(NDEBUG)
+  // Toggles the host's full screen state.
+  void ToggleFullScreen();
+#endif
+
  private:
   // Called whenever the mouse moves, tracks the current |mouse_moved_handler_|,
   // sending exited and entered events as its value changes.
diff --git a/ui/aura_shell/aura_shell.gyp b/ui/aura_shell/aura_shell.gyp
index dd42888..ca63de3 100644
--- a/ui/aura_shell/aura_shell.gyp
+++ b/ui/aura_shell/aura_shell.gyp
@@ -84,6 +84,10 @@
         'shelf_layout_controller.h',
         'shell.cc',
         'shell.h',
+        'shell_accelerator_controller.cc',
+        'shell_accelerator_controller.h',
+        'shell_accelerator_filter.cc',
+        'shell_accelerator_filter.h',
         'shell_delegate.h',
         'shell_factory.h',
         'shell_window_ids.h',
diff --git a/ui/aura_shell/shell.cc b/ui/aura_shell/shell.cc
index 0e1fb45..49e515f 100644
--- a/ui/aura_shell/shell.cc
+++ b/ui/aura_shell/shell.cc
@@ -22,6 +22,8 @@
 #include "ui/aura_shell/modal_container_layout_manager.h"
 #include "ui/aura_shell/shadow_controller.h"
 #include "ui/aura_shell/shelf_layout_controller.h"
+#include "ui/aura_shell/shell_accelerator_controller.h"
+#include "ui/aura_shell/shell_accelerator_filter.h"
 #include "ui/aura_shell/shell_delegate.h"
 #include "ui/aura_shell/shell_factory.h"
 #include "ui/aura_shell/shell_window_ids.h"
@@ -100,6 +102,7 @@
 
 Shell::Shell(ShellDelegate* delegate)
     : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
+      accelerator_controller_(new ShellAcceleratorController),
       delegate_(delegate) {
   aura::Desktop::GetInstance()->SetEventFilter(
       new internal::DesktopEventFilter);
@@ -108,6 +111,8 @@
 }
 
 Shell::~Shell() {
+  RemoveDesktopEventFilter(accelerator_filter_.get());
+
   // Drag drop controller needs a valid shell instance. We destroy it first.
   drag_drop_controller_.reset();
 
@@ -191,6 +196,11 @@
   // Force a layout.
   desktop_layout->OnWindowResized();
 
+  // Initialize ShellAcceleratorFilter
+  accelerator_filter_.reset(new internal::ShellAcceleratorFilter);
+  AddDesktopEventFilter(accelerator_filter_.get());
+
+  // Initialize drag drop controller.
   drag_drop_controller_.reset(new internal::DragDropController);
   aura::Desktop::GetInstance()->SetProperty(aura::kDesktopDragDropClientKey,
       static_cast<aura::DragDropClient*>(drag_drop_controller_.get()));
diff --git a/ui/aura_shell/shell.h b/ui/aura_shell/shell.h
index 0ab72bc..228c00f 100644
--- a/ui/aura_shell/shell.h
+++ b/ui/aura_shell/shell.h
@@ -27,6 +27,7 @@
 namespace aura_shell {
 
 class Launcher;
+class ShellAcceleratorController;
 class ShellDelegate;
 
 namespace internal {
@@ -34,6 +35,7 @@
 class DragDropController;
 class ShadowController;
 class ShelfLayoutController;
+class ShellAcceleratorFilter;
 class WorkspaceController;
 }
 
@@ -66,6 +68,10 @@
   // Toggles app list.
   void ToggleAppList();
 
+  ShellAcceleratorController* accelerator_controller() {
+    return accelerator_controller_.get();
+  }
+
   ShellDelegate* delegate() { return delegate_.get(); }
   Launcher* launcher() { return launcher_.get(); }
 
@@ -91,6 +97,8 @@
 
   base::WeakPtrFactory<Shell> method_factory_;
 
+  scoped_ptr<ShellAcceleratorController> accelerator_controller_;
+
   scoped_ptr<ShellDelegate> delegate_;
 
   scoped_ptr<Launcher> launcher_;
@@ -102,6 +110,9 @@
   scoped_ptr<internal::ShelfLayoutController> shelf_layout_controller_;
   scoped_ptr<internal::ShadowController> shadow_controller_;
 
+  // An event filter that pre-handles global accelerators.
+  scoped_ptr<internal::ShellAcceleratorFilter> accelerator_filter_;
+
   DISALLOW_COPY_AND_ASSIGN(Shell);
 };
 
diff --git a/ui/aura_shell/shell_accelerator_controller.cc b/ui/aura_shell/shell_accelerator_controller.cc
new file mode 100644
index 0000000..9c67a9d5
--- /dev/null
+++ b/ui/aura_shell/shell_accelerator_controller.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2011 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.
+
+#include "ui/aura_shell/shell_accelerator_controller.h"
+
+#include "ui/aura/desktop.h"
+#include "ui/aura/event.h"
+#include "ui/aura_shell/shell.h"
+#include "ui/base/accelerator_manager.h"
+#include "ui/base/models/accelerator.h"
+#include "ui/gfx/compositor/layer_animation_sequence.h"
+#include "ui/gfx/compositor/layer_animator.h"
+#include "ui/gfx/compositor/screen_rotation.h"
+
+namespace {
+
+// Acceleraters handled by ShellAcceleratorController.
+struct AcceleratorData {
+  ui::KeyboardCode keycode;
+  bool shift;
+  bool ctrl;
+  bool alt;
+} kAcceleratorData[] = {
+  { ui::VKEY_F11, false, false, false },
+  { ui::VKEY_HOME, false, true, false },
+};
+
+// Registers the accelerators with ShellAcceleratorController.
+void RegisterAccelerators(aura_shell::ShellAcceleratorController* controller) {
+  for (size_t i = 0; i < arraysize(kAcceleratorData); ++i) {
+    controller->Register(ui::Accelerator(kAcceleratorData[i].keycode,
+                                         kAcceleratorData[i].shift,
+                                         kAcceleratorData[i].ctrl,
+                                         kAcceleratorData[i].alt),
+                         controller);
+  }
+}
+
+#if !defined(NDEBUG)
+// Rotates the screen.
+void RotateScreen() {
+  static int i = 0;
+  int delta = 0;
+  switch (i) {
+    case 0: delta = 90; break;
+    case 1: delta = 90; break;
+    case 2: delta = 90; break;
+    case 3: delta = 90; break;
+    case 4: delta = -90; break;
+    case 5: delta = -90; break;
+    case 6: delta = -90; break;
+    case 7: delta = -90; break;
+    case 8: delta = -90; break;
+    case 9: delta = 180; break;
+    case 10: delta = 180; break;
+    case 11: delta = 90; break;
+    case 12: delta = 180; break;
+    case 13: delta = 180; break;
+  }
+  i = (i + 1) % 14;
+  aura::Desktop::GetInstance()->layer()->GetAnimator()->set_preemption_strategy(
+      ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
+  scoped_ptr<ui::LayerAnimationSequence> screen_rotation(
+      new ui::LayerAnimationSequence(new ui::ScreenRotation(delta)));
+  screen_rotation->AddObserver(aura::Desktop::GetInstance());
+  aura::Desktop::GetInstance()->layer()->GetAnimator()->ScheduleAnimation(
+      screen_rotation.release());
+}
+#endif
+
+}  // namespace
+
+namespace aura_shell {
+
+////////////////////////////////////////////////////////////////////////////////
+// ShellAcceleratorController, public:
+
+ShellAcceleratorController::ShellAcceleratorController()
+    : accelerator_manager_(new ui::AcceleratorManager) {
+  RegisterAccelerators(this);
+}
+
+ShellAcceleratorController::~ShellAcceleratorController() {
+}
+
+void ShellAcceleratorController::Register(
+    const ui::Accelerator& accelerator,
+    ui::AcceleratorTarget* target) {
+  accelerator_manager_->Register(accelerator, target);
+}
+
+void ShellAcceleratorController::Unregister(
+    const ui::Accelerator& accelerator,
+    ui::AcceleratorTarget* target) {
+  accelerator_manager_->Unregister(accelerator, target);
+}
+
+void ShellAcceleratorController::UnregisterAll(
+    ui::AcceleratorTarget* target) {
+  accelerator_manager_->UnregisterAll(target);
+}
+
+bool ShellAcceleratorController::Process(const ui::Accelerator& accelerator) {
+  return accelerator_manager_->Process(accelerator);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ShellAcceleratorController, ui::AcceleratorTarget implementation:
+
+bool ShellAcceleratorController::AcceleratorPressed(
+    const ui::Accelerator& accelerator) {
+#if !defined(NDEBUG)
+  if (accelerator.key_code() == ui::VKEY_F11) {
+    aura::Desktop::GetInstance()->ToggleFullScreen();
+    return true;
+  } else if (accelerator.key_code() == ui::VKEY_HOME &&
+             accelerator.IsCtrlDown()) {
+    RotateScreen();
+    return true;
+  }
+#endif
+  return false;
+}
+
+}  // namespace aura_shell
diff --git a/ui/aura_shell/shell_accelerator_controller.h b/ui/aura_shell/shell_accelerator_controller.h
new file mode 100644
index 0000000..14f1f3a
--- /dev/null
+++ b/ui/aura_shell/shell_accelerator_controller.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2011 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 UI_AURA_SHELL_SHELL_ACCELERATOR_CONTROLLER_H_
+#define UI_AURA_SHELL_SHELL_ACCELERATOR_CONTROLLER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/aura_shell/aura_shell_export.h"
+#include "ui/base/models/accelerator.h"
+
+namespace ui {
+class AcceleratorManager;
+}  // namespace ui
+
+namespace aura_shell {
+
+// ShellAcceleratorController provides functions for registering or
+// unregistering global keyboard accelerators, which are handled earlier than
+// any windows. It also implements several handlers as an accelerator target.
+class AURA_SHELL_EXPORT ShellAcceleratorController
+    : public ui::AcceleratorTarget {
+ public:
+  ShellAcceleratorController();
+  virtual ~ShellAcceleratorController();
+
+  // Register a global keyboard accelerator for the specified target. If
+  // multiple targets are registered for an accelerator, a target registered
+  // later has higher priority.
+  void Register(const ui::Accelerator& accelerator,
+                ui::AcceleratorTarget* target);
+
+  // Unregister the specified keyboard accelerator for the specified target.
+  void Unregister(const ui::Accelerator& accelerator,
+                  ui::AcceleratorTarget* target);
+
+  // Unregister all keyboard accelerators for the specified target.
+  void UnregisterAll(ui::AcceleratorTarget* target);
+
+  // Activate the target associated with the specified accelerator.
+  // First, AcceleratorPressed handler of the most recently registered target
+  // is called, and if that handler processes the event (i.e. returns true),
+  // this method immediately returns. If not, we do the same thing on the next
+  // target, and so on.
+  // Returns true if an accelerator was activated.
+  bool Process(const ui::Accelerator& accelerator);
+
+  // Overridden from ui::AcceleratorTarget:
+  virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE;
+
+ private:
+  scoped_ptr<ui::AcceleratorManager> accelerator_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShellAcceleratorController);
+};
+
+}  // namespace aura_shell
+
+#endif  // UI_AURA_SHELL_SHELL_ACCELERATOR_CONTROLLER_H_
diff --git a/ui/aura_shell/shell_accelerator_filter.cc b/ui/aura_shell/shell_accelerator_filter.cc
new file mode 100644
index 0000000..3a9cde5b
--- /dev/null
+++ b/ui/aura_shell/shell_accelerator_filter.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2011 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.
+
+#include "ui/aura_shell/shell_accelerator_filter.h"
+
+#include "ui/aura/desktop.h"
+#include "ui/aura/event.h"
+#include "ui/aura_shell/shell.h"
+#include "ui/aura_shell/shell_accelerator_controller.h"
+#include "ui/base/accelerator_manager.h"
+#include "ui/base/models/accelerator.h"
+
+namespace {
+const int kModifierFlagMask = (ui::EF_SHIFT_DOWN |
+                               ui::EF_CONTROL_DOWN |
+                               ui::EF_ALT_DOWN);
+}  // namespace
+
+namespace aura_shell {
+namespace internal {
+
+////////////////////////////////////////////////////////////////////////////////
+// ShellAcceleratorFilter, public:
+
+ShellAcceleratorFilter::ShellAcceleratorFilter()
+    : EventFilter(aura::Desktop::GetInstance()) {
+}
+
+ShellAcceleratorFilter::~ShellAcceleratorFilter() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ShellAcceleratorFilter, EventFilter implementation:
+
+bool ShellAcceleratorFilter::PreHandleKeyEvent(aura::Window* target,
+                                               aura::KeyEvent* event) {
+  if (event->type() == ui::ET_KEY_PRESSED &&
+      Shell::GetInstance()->accelerator_controller()->Process(
+          ui::Accelerator(event->key_code(),
+                          event->flags() & kModifierFlagMask))) {
+    return true;
+  }
+  return false;
+}
+
+bool ShellAcceleratorFilter::PreHandleMouseEvent(aura::Window* target,
+                                                 aura::MouseEvent* event) {
+  return false;
+}
+
+ui::TouchStatus ShellAcceleratorFilter::PreHandleTouchEvent(
+    aura::Window* target,
+    aura::TouchEvent* event) {
+  return ui::TOUCH_STATUS_UNKNOWN;
+}
+
+}  // namespace internal
+}  // namespace aura_shell
diff --git a/ui/aura_shell/shell_accelerator_filter.h b/ui/aura_shell/shell_accelerator_filter.h
new file mode 100644
index 0000000..3e35ba6
--- /dev/null
+++ b/ui/aura_shell/shell_accelerator_filter.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2011 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 UI_AURA_SHELL_SHELL_ACCELERATOR_FILTER_H_
+#define UI_AURA_SHELL_SHELL_ACCELERATOR_FILTER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "ui/aura/event_filter.h"
+#include "ui/aura_shell/aura_shell_export.h"
+
+namespace aura_shell {
+namespace internal {
+
+// ShellAcceleratorFilter filters key events for ShellAcceleratorControler
+// handling global keyboard accelerators.
+class AURA_SHELL_EXPORT ShellAcceleratorFilter : public aura::EventFilter {
+ public:
+  ShellAcceleratorFilter();
+  virtual ~ShellAcceleratorFilter();
+
+  // Overridden from aura::EventFilter:
+  virtual bool PreHandleKeyEvent(aura::Window* target,
+                                 aura::KeyEvent* event) OVERRIDE;
+  virtual bool PreHandleMouseEvent(aura::Window* target,
+                                   aura::MouseEvent* event) OVERRIDE;
+  virtual ui::TouchStatus PreHandleTouchEvent(aura::Window* target,
+                                              aura::TouchEvent* event) OVERRIDE;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ShellAcceleratorFilter);
+};
+
+}  // namespace internal
+}  // namespace aura_shell
+
+#endif  // UI_AURA_SHELL_SHELL_ACCELERATOR_FILTER_H_