blob: 6233651044b559803d365717433535ad79c6bd5e [file] [log] [blame]
// Copyright (c) 2012 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 "ash/accelerators/accelerator_controller.h"
#include "ash/accelerators/accelerator_table.h"
#include "ash/ash_switches.h"
#include "ash/caps_lock_delegate.h"
#include "ash/desktop_background/desktop_background_controller.h"
#include "ash/focus_cycler.h"
#include "ash/ime_control_delegate.h"
#include "ash/launcher/launcher.h"
#include "ash/launcher/launcher_delegate.h"
#include "ash/launcher/launcher_model.h"
#include "ash/monitor/monitor_controller.h"
#include "ash/monitor/multi_monitor_manager.h"
#include "ash/root_window_controller.h"
#include "ash/screenshot_delegate.h"
#include "ash/shell.h"
#include "ash/shell_delegate.h"
#include "ash/shell_window_ids.h"
#include "ash/system/brightness/brightness_control_delegate.h"
#include "ash/system/tray/system_tray.h"
#include "ash/volume_control_delegate.h"
#include "ash/wm/property_util.h"
#include "ash/wm/window_cycle_controller.h"
#include "ash/wm/window_util.h"
#include "ash/wm/workspace/snap_sizer.h"
#include "base/command_line.h"
#include "ui/aura/event.h"
#include "ui/aura/root_window.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/accelerators/accelerator_manager.h"
#include "ui/compositor/debug_utils.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animation_sequence.h"
#include "ui/compositor/layer_animator.h"
#include "ui/compositor/screen_rotation.h"
#include "ui/oak/oak.h"
#if defined(OS_CHROMEOS)
#include "chromeos/monitor/output_configurator.h"
#endif // defined(OS_CHROMEOS)
namespace ash {
namespace {
bool HandleCycleWindowMRU(WindowCycleController::Direction direction,
bool is_alt_down) {
Shell::GetInstance()->
window_cycle_controller()->HandleCycleWindow(direction, is_alt_down);
// Always report we handled the key, even if the window didn't change.
return true;
}
void HandleCycleWindowLinear(CycleDirection direction) {
Shell::GetInstance()->launcher()->CycleWindowLinear(direction);
}
#if defined(OS_CHROMEOS)
bool HandleLock() {
Shell::GetInstance()->delegate()->LockScreen();
return true;
}
bool HandleFileManager(bool as_dialog) {
Shell::GetInstance()->delegate()->OpenFileManager(as_dialog);
return true;
}
bool HandleCrosh() {
Shell::GetInstance()->delegate()->OpenCrosh();
return true;
}
bool HandleToggleSpokenFeedback() {
Shell::GetInstance()->delegate()->ToggleSpokenFeedback();
return true;
}
#endif
bool HandleExit() {
ShellDelegate* delegate = Shell::GetInstance()->delegate();
if (!delegate)
return false;
delegate->Exit();
return true;
}
bool HandleNewTab() {
Shell::GetInstance()->delegate()->NewTab();
return true;
}
bool HandleNewWindow(bool is_incognito) {
ShellDelegate* delegate = Shell::GetInstance()->delegate();
if (!delegate)
return false;
delegate->NewWindow(is_incognito);
return true;
}
bool HandleRestoreTab() {
Shell::GetInstance()->delegate()->RestoreTab();
return true;
}
bool HandleShowTaskManager() {
Shell::GetInstance()->delegate()->ShowTaskManager();
return true;
}
bool HandleRotatePaneFocus(Shell::Direction direction) {
if (!Shell::GetInstance()->delegate()->RotatePaneFocus(direction)) {
// No browser window is available. Focus the launcher.
Shell* shell = Shell::GetInstance();
switch (direction) {
case Shell::FORWARD:
shell->focus_cycler()->RotateFocus(internal::FocusCycler::FORWARD);
break;
case Shell::BACKWARD:
shell->focus_cycler()->RotateFocus(internal::FocusCycler::BACKWARD);
break;
}
}
return true;
}
// Rotates the default window container.
bool HandleRotateWindows() {
aura::Window* target =
Shell::GetPrimaryRootWindowController()->GetContainer(
internal::kShellWindowId_DefaultContainer);
scoped_ptr<ui::LayerAnimationSequence> screen_rotation(
new ui::LayerAnimationSequence(new ui::ScreenRotation(360)));
target->layer()->GetAnimator()->StartAnimation(
screen_rotation.release());
return true;
}
#if !defined(NDEBUG)
// Rotates the screen.
bool HandleRotateScreen() {
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;
Shell::GetPrimaryRootWindow()->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(Shell::GetPrimaryRootWindow());
Shell::GetPrimaryRootWindow()->layer()->GetAnimator()->StartAnimation(
screen_rotation.release());
return true;
}
bool HandleToggleDesktopBackgroundMode() {
DesktopBackgroundController* desktop_background_controller =
Shell::GetInstance()->desktop_background_controller();
if (desktop_background_controller->desktop_background_mode() ==
DesktopBackgroundController::BACKGROUND_IMAGE) {
desktop_background_controller->SetDesktopBackgroundSolidColorMode(
SK_ColorBLACK);
} else {
ash::Shell::GetInstance()->user_wallpaper_delegate()->
InitializeWallpaper();
}
return true;
}
bool HandleToggleRootWindowFullScreen() {
Shell::GetPrimaryRootWindow()->ToggleFullScreen();
return true;
}
bool HandlePrintLayerHierarchy() {
aura::RootWindow* root_window = Shell::GetPrimaryRootWindow();
ui::PrintLayerHierarchy(root_window->layer(),
root_window->last_mouse_location());
return true;
}
void PrintWindowHierarchy(aura::Window* window, int indent) {
std::string indent_str(indent, ' ');
DLOG(INFO) << indent_str << window->name() << " type " << window->type()
<< (wm::IsActiveWindow(window) ? "active" : "");
for (size_t i = 0; i < window->children().size(); ++i)
PrintWindowHierarchy(window->children()[i], indent + 3);
}
bool HandlePrintWindowHierarchy() {
DLOG(INFO) << "Window hierarchy:";
aura::Window* container =
Shell::GetPrimaryRootWindowController()->GetContainer(
internal::kShellWindowId_DefaultContainer);
PrintWindowHierarchy(container, 0);
return true;
}
#endif // !defined(NDEBUG)
} // namespace
////////////////////////////////////////////////////////////////////////////////
// AcceleratorController, public:
AcceleratorController::AcceleratorController()
: accelerator_manager_(new ui::AcceleratorManager) {
Init();
}
AcceleratorController::~AcceleratorController() {
}
void AcceleratorController::Init() {
for (size_t i = 0; i < kActionsAllowedAtLoginOrLockScreenLength; ++i) {
actions_allowed_at_login_screen_.insert(
kActionsAllowedAtLoginOrLockScreen[i]);
actions_allowed_at_lock_screen_.insert(
kActionsAllowedAtLoginOrLockScreen[i]);
}
for (size_t i = 0; i < kActionsAllowedAtLockScreenLength; ++i) {
actions_allowed_at_lock_screen_.insert(kActionsAllowedAtLockScreen[i]);
}
for (size_t i = 0; i < kAcceleratorDataLength; ++i) {
ui::Accelerator accelerator(kAcceleratorData[i].keycode,
kAcceleratorData[i].modifiers);
accelerator.set_type(kAcceleratorData[i].trigger_on_press ?
ui::ET_KEY_PRESSED : ui::ET_KEY_RELEASED);
Register(accelerator, this);
accelerators_.insert(
std::make_pair(accelerator, kAcceleratorData[i].action));
}
}
void AcceleratorController::Register(const ui::Accelerator& accelerator,
ui::AcceleratorTarget* target) {
accelerator_manager_->Register(accelerator,
ui::AcceleratorManager::kNormalPriority,
target);
}
void AcceleratorController::Unregister(const ui::Accelerator& accelerator,
ui::AcceleratorTarget* target) {
accelerator_manager_->Unregister(accelerator, target);
}
void AcceleratorController::UnregisterAll(ui::AcceleratorTarget* target) {
accelerator_manager_->UnregisterAll(target);
}
bool AcceleratorController::Process(const ui::Accelerator& accelerator) {
if (ime_control_delegate_.get()) {
return accelerator_manager_->Process(
ime_control_delegate_->RemapAccelerator(accelerator));
}
return accelerator_manager_->Process(accelerator);
}
bool AcceleratorController::IsRegistered(
const ui::Accelerator& accelerator) const {
return accelerator_manager_->GetCurrentTarget(accelerator) != NULL;
}
bool AcceleratorController::PerformAction(int action,
const ui::Accelerator& accelerator) {
ash::Shell* shell = ash::Shell::GetInstance();
bool at_login_screen = false;
#if defined(OS_CHROMEOS)
at_login_screen = (shell->delegate() && !shell->delegate()->IsUserLoggedIn());
#endif
bool at_lock_screen = shell->IsScreenLocked();
if (at_login_screen &&
actions_allowed_at_login_screen_.find(action) ==
actions_allowed_at_login_screen_.end()) {
return false;
}
if (at_lock_screen &&
actions_allowed_at_lock_screen_.find(action) ==
actions_allowed_at_lock_screen_.end()) {
return false;
}
// You *MUST* return true when some action is performed. Otherwise, this
// function might be called *twice*, via BrowserView::PreHandleKeyboardEvent
// and BrowserView::HandleKeyboardEvent, for a single accelerator press.
switch (action) {
case CYCLE_BACKWARD_MRU:
return HandleCycleWindowMRU(WindowCycleController::BACKWARD,
accelerator.IsAltDown());
case CYCLE_FORWARD_MRU:
return HandleCycleWindowMRU(WindowCycleController::FORWARD,
accelerator.IsAltDown());
case CYCLE_BACKWARD_LINEAR:
HandleCycleWindowLinear(CYCLE_BACKWARD);
return true;
case CYCLE_FORWARD_LINEAR:
HandleCycleWindowLinear(CYCLE_FORWARD);
return true;
#if defined(OS_CHROMEOS)
case LOCK_SCREEN:
return HandleLock();
case OPEN_FILE_MANAGER_DIALOG:
return HandleFileManager(true /* as_dialog */);
case OPEN_FILE_MANAGER_TAB:
return HandleFileManager(false /* as_dialog */);
case OPEN_CROSH:
return HandleCrosh();
case TOGGLE_SPOKEN_FEEDBACK:
return HandleToggleSpokenFeedback();
case CYCLE_DISPLAY_MODE:
ash::Shell::GetInstance()->output_configurator()->CycleDisplayMode();
return true;
#endif
case EXIT:
return HandleExit();
case NEW_INCOGNITO_WINDOW:
return HandleNewWindow(true /* is_incognito */);
case NEW_TAB:
return HandleNewTab();
case NEW_WINDOW:
return HandleNewWindow(false /* is_incognito */);
case RESTORE_TAB:
return HandleRestoreTab();
case TAKE_SCREENSHOT:
if (screenshot_delegate_.get()) {
std::vector<aura::RootWindow*> root_windows;
ash::Shell::GetInstance()->monitor_controller()->GetAllRootWindows(
&root_windows);
for (size_t i = 0; i < root_windows.size(); ++i)
screenshot_delegate_->HandleTakeScreenshot(root_windows[i]);
}
// Return true to prevent propagation of the key event.
return true;
case TAKE_PARTIAL_SCREENSHOT:
if (screenshot_delegate_.get())
ash::Shell::GetInstance()->delegate()->
StartPartialScreenshot(screenshot_delegate_.get());
// Return true to prevent propagation of the key event because
// this key combination is reserved for partial screenshot.
return true;
case TOGGLE_APP_LIST:
ash::Shell::GetInstance()->ToggleAppList();
return true;
case TOGGLE_CAPS_LOCK:
if (caps_lock_delegate_.get())
return caps_lock_delegate_->HandleToggleCapsLock();
break;
case BRIGHTNESS_DOWN:
if (brightness_control_delegate_.get())
return brightness_control_delegate_->HandleBrightnessDown(accelerator);
break;
case BRIGHTNESS_UP:
if (brightness_control_delegate_.get())
return brightness_control_delegate_->HandleBrightnessUp(accelerator);
break;
case VOLUME_MUTE:
if (volume_control_delegate_.get())
return volume_control_delegate_->HandleVolumeMute(accelerator);
break;
case VOLUME_DOWN:
if (volume_control_delegate_.get())
return volume_control_delegate_->HandleVolumeDown(accelerator);
break;
case VOLUME_UP:
if (volume_control_delegate_.get())
return volume_control_delegate_->HandleVolumeUp(accelerator);
break;
case FOCUS_LAUNCHER:
if (shell->launcher())
return shell->focus_cycler()->FocusWidget(shell->launcher()->widget());
break;
case FOCUS_NEXT_PANE:
return HandleRotatePaneFocus(Shell::FORWARD);
case FOCUS_PREVIOUS_PANE:
return HandleRotatePaneFocus(Shell::BACKWARD);
case FOCUS_SYSTEM_TRAY:
if (shell->system_tray())
return shell->focus_cycler()->FocusWidget(
shell->system_tray()->GetWidget());
break;
case SHOW_KEYBOARD_OVERLAY:
ash::Shell::GetInstance()->delegate()->ShowKeyboardOverlay();
return true;
case SHOW_OAK:
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kAshEnableOak)) {
oak::ShowOakWindow();
return true;
}
break;
case SHOW_TASK_MANAGER:
return HandleShowTaskManager();
case NEXT_IME:
if (ime_control_delegate_.get())
return ime_control_delegate_->HandleNextIme();
break;
case PREVIOUS_IME:
if (ime_control_delegate_.get())
return ime_control_delegate_->HandlePreviousIme();
break;
case SWITCH_IME:
if (ime_control_delegate_.get())
return ime_control_delegate_->HandleSwitchIme(accelerator);
break;
case SELECT_WIN_0:
SwitchToWindow(0);
return true;
case SELECT_WIN_1:
SwitchToWindow(1);
return true;
case SELECT_WIN_2:
SwitchToWindow(2);
return true;
case SELECT_WIN_3:
SwitchToWindow(3);
return true;
case SELECT_WIN_4:
SwitchToWindow(4);
return true;
case SELECT_WIN_5:
SwitchToWindow(5);
return true;
case SELECT_WIN_6:
SwitchToWindow(6);
return true;
case SELECT_WIN_7:
SwitchToWindow(7);
return true;
case SELECT_LAST_WIN:
SwitchToWindow(-1);
return true;
case WINDOW_SNAP_LEFT:
case WINDOW_SNAP_RIGHT: {
aura::Window* window = wm::GetActiveWindow();
if (!window)
break;
internal::SnapSizer sizer(window,
gfx::Point(),
action == WINDOW_SNAP_LEFT ? internal::SnapSizer::LEFT_EDGE :
internal::SnapSizer::RIGHT_EDGE,
shell->GetGridSize());
if (wm::IsWindowFullscreen(window) ||
wm::IsWindowMaximized(window)) {
SetRestoreBounds(window, sizer.GetSnapBounds(window->bounds()));
wm::RestoreWindow(window);
} else {
window->SetBounds(sizer.GetSnapBounds(window->bounds()));
}
return true;
}
case WINDOW_MINIMIZE: {
aura::Window* window = wm::GetActiveWindow();
// Disable the shortcut for minimizing full screen window due to
// crbug.com/131709, which is a crashing issue related to minimizing
// full screen pepper window.
if (window && !wm::IsWindowFullscreen(window)) {
wm::MinimizeWindow(window);
return true;
}
break;
}
case WINDOW_MAXIMIZE_RESTORE: {
aura::Window* window = wm::GetActiveWindow();
if (window) {
if (wm::IsWindowMaximized(window))
wm::RestoreWindow(window);
else
wm::MaximizeWindow(window);
return true;
}
break;
}
case WINDOW_POSITION_CENTER: {
aura::Window* window = wm::GetActiveWindow();
if (window) {
wm::CenterWindow(window);
return true;
}
break;
}
case ROTATE_WINDOWS:
return HandleRotateWindows();
#if !defined(NDEBUG)
case ROTATE_SCREEN:
return HandleRotateScreen();
case TOGGLE_DESKTOP_BACKGROUND_MODE:
return HandleToggleDesktopBackgroundMode();
case TOGGLE_ROOT_WINDOW_FULL_SCREEN:
return HandleToggleRootWindowFullScreen();
case PRINT_LAYER_HIERARCHY:
return HandlePrintLayerHierarchy();
case PRINT_WINDOW_HIERARCHY:
return HandlePrintWindowHierarchy();
case MONITOR_ADD_REMOVE:
internal::MultiMonitorManager::AddRemoveMonitor();
return true;
case MONITOR_CYCLE:
internal::MultiMonitorManager::CycleMonitor();
return true;
case MONITOR_TOGGLE_SCALE:
internal::MultiMonitorManager::ToggleMonitorScale();
return true;
#endif
default:
NOTREACHED() << "Unhandled action " << action;
}
return false;
}
void AcceleratorController::SetBrightnessControlDelegate(
scoped_ptr<BrightnessControlDelegate> brightness_control_delegate) {
brightness_control_delegate_.swap(brightness_control_delegate);
}
void AcceleratorController::SetCapsLockDelegate(
scoped_ptr<CapsLockDelegate> caps_lock_delegate) {
caps_lock_delegate_.swap(caps_lock_delegate);
}
void AcceleratorController::SetImeControlDelegate(
scoped_ptr<ImeControlDelegate> ime_control_delegate) {
ime_control_delegate_.swap(ime_control_delegate);
}
void AcceleratorController::SetScreenshotDelegate(
scoped_ptr<ScreenshotDelegate> screenshot_delegate) {
screenshot_delegate_.swap(screenshot_delegate);
}
void AcceleratorController::SetVolumeControlDelegate(
scoped_ptr<VolumeControlDelegate> volume_control_delegate) {
volume_control_delegate_.swap(volume_control_delegate);
}
////////////////////////////////////////////////////////////////////////////////
// AcceleratorController, ui::AcceleratorTarget implementation:
bool AcceleratorController::AcceleratorPressed(
const ui::Accelerator& accelerator) {
std::map<ui::Accelerator, int>::const_iterator it =
accelerators_.find(accelerator);
DCHECK(it != accelerators_.end());
return PerformAction(static_cast<AcceleratorAction>(it->second), accelerator);
}
void AcceleratorController::SwitchToWindow(int window) {
const LauncherItems& items =
Shell::GetInstance()->launcher()->model()->items();
int item_count =
Shell::GetInstance()->launcher()->model()->item_count();
int indexes_left = window >= 0 ? window : item_count;
int found_index = -1;
// Iterating until we have hit the index we are interested in which
// is true once indexes_left becomes negative.
for (int i = 0; i < item_count && indexes_left >= 0; i++) {
if (items[i].type != TYPE_APP_LIST &&
items[i].type != TYPE_BROWSER_SHORTCUT) {
found_index = i;
indexes_left--;
}
}
// There are two ways how found_index can be valid: a.) the nth item was
// found (which is true when indexes_left is -1) or b.) the last item was
// requested (which is true when index was passed in as a negative number).
if (found_index >= 0 && (indexes_left == -1 || window < 0) &&
items[found_index].status == ash::STATUS_RUNNING) {
// Then set this one as active.
Shell::GetInstance()->launcher()->ActivateLauncherItem(found_index);
}
}
bool AcceleratorController::CanHandleAccelerators() const {
return true;
}
} // namespace ash