Commit cfb8c555 authored by stevet@chromium.org's avatar stevet@chromium.org

Record user actions for Ash accelerators.

Clean up the huge switch of accelerators as well:
* Move a bunch of code into Handle helpers.
* Alphabetize the anon namespace helpers.
* Clean up the OS_CHROMEOS groupings a bit.

BUG=321695
R=jamescook@chromium.org

Review URL: https://codereview.chromium.org/78763003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@238391 0039d316-1c4b-4281-b951-d872f2087c98
parent d89eec80
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "ash/wm/window_cycle_controller.h" #include "ash/wm/window_cycle_controller.h"
#include "ash/wm/window_state.h" #include "ash/wm/window_state.h"
#include "ash/wm/window_util.h" #include "ash/wm/window_util.h"
#include "content/public/browser/user_metrics.h"
namespace ash { namespace ash {
namespace accelerators { namespace accelerators {
...@@ -25,8 +26,8 @@ bool ToggleMinimized() { ...@@ -25,8 +26,8 @@ bool ToggleMinimized() {
wm::WindowState* window_state = wm::GetWindowState(window); wm::WindowState* window_state = wm::GetWindowState(window);
if (!window_state->CanMinimize()) if (!window_state->CanMinimize())
return false; return false;
ash::Shell::GetInstance()->delegate()->RecordUserMetricsAction( content::RecordAction(
ash::UMA_MINIMIZE_PER_KEY); content::UserMetricsAction("Accel_Toggle_Minimized"));
window_state->Minimize(); window_state->Minimize();
return true; return true;
} }
...@@ -35,6 +36,8 @@ void ToggleMaximized() { ...@@ -35,6 +36,8 @@ void ToggleMaximized() {
wm::WindowState* window_state = wm::GetActiveWindowState(); wm::WindowState* window_state = wm::GetActiveWindowState();
if (!window_state) if (!window_state)
return; return;
content::RecordAction(
content::UserMetricsAction("Accel_Toggle_Maximized"));
// Get out of fullscreen when in fullscreen mode. // Get out of fullscreen when in fullscreen mode.
if (window_state->IsFullscreen()) if (window_state->IsFullscreen())
ToggleFullscreen(); ToggleFullscreen();
......
...@@ -92,7 +92,40 @@ bool DebugShortcutsEnabled() { ...@@ -92,7 +92,40 @@ bool DebugShortcutsEnabled() {
#endif #endif
} }
void HandleCycleBackwardMRU(const ui::Accelerator& accelerator) { bool HandleAccessibleFocusCycle(bool reverse) {
if (reverse) {
content::RecordAction(
content::UserMetricsAction("Accel_Accessible_Focus_Previous"));
} else {
content::RecordAction(
content::UserMetricsAction("Accel_Accessible_Focus_Next"));
}
if (!Shell::GetInstance()->accessibility_delegate()->
IsSpokenFeedbackEnabled()) {
return false;
}
aura::Window* active_window = ash::wm::GetActiveWindow();
if (!active_window)
return false;
views::Widget* widget =
views::Widget::GetWidgetForNativeWindow(active_window);
if (!widget)
return false;
views::FocusManager* focus_manager = widget->GetFocusManager();
if (!focus_manager)
return false;
views::View* view = focus_manager->GetFocusedView();
if (!view)
return false;
if (!strcmp(view->GetClassName(), views::WebView::kViewClassName))
return false;
focus_manager->AdvanceFocus(reverse);
return true;
}
bool HandleCycleBackwardMRU(const ui::Accelerator& accelerator) {
Shell* shell = Shell::GetInstance(); Shell* shell = Shell::GetInstance();
if (accelerator.key_code() == ui::VKEY_TAB) if (accelerator.key_code() == ui::VKEY_TAB)
...@@ -101,13 +134,14 @@ void HandleCycleBackwardMRU(const ui::Accelerator& accelerator) { ...@@ -101,13 +134,14 @@ void HandleCycleBackwardMRU(const ui::Accelerator& accelerator) {
if (switches::UseOverviewMode()) { if (switches::UseOverviewMode()) {
shell->window_selector_controller()->HandleCycleWindow( shell->window_selector_controller()->HandleCycleWindow(
WindowSelector::BACKWARD); WindowSelector::BACKWARD);
return; return true;
} }
shell->window_cycle_controller()->HandleCycleWindow( shell->window_cycle_controller()->HandleCycleWindow(
WindowCycleController::BACKWARD, accelerator.IsAltDown()); WindowCycleController::BACKWARD, accelerator.IsAltDown());
return true;
} }
void HandleCycleForwardMRU(const ui::Accelerator& accelerator) { bool HandleCycleForwardMRU(const ui::Accelerator& accelerator) {
Shell* shell = Shell::GetInstance(); Shell* shell = Shell::GetInstance();
if (accelerator.key_code() == ui::VKEY_TAB) if (accelerator.key_code() == ui::VKEY_TAB)
...@@ -116,13 +150,14 @@ void HandleCycleForwardMRU(const ui::Accelerator& accelerator) { ...@@ -116,13 +150,14 @@ void HandleCycleForwardMRU(const ui::Accelerator& accelerator) {
if (switches::UseOverviewMode()) { if (switches::UseOverviewMode()) {
shell->window_selector_controller()->HandleCycleWindow( shell->window_selector_controller()->HandleCycleWindow(
WindowSelector::FORWARD); WindowSelector::FORWARD);
return; return true;
} }
shell->window_cycle_controller()->HandleCycleWindow( shell->window_cycle_controller()->HandleCycleWindow(
WindowCycleController::FORWARD, accelerator.IsAltDown()); WindowCycleController::FORWARD, accelerator.IsAltDown());
return true;
} }
void HandleCycleLinear(const ui::Accelerator& accelerator) { bool HandleCycleLinear(const ui::Accelerator& accelerator) {
Shell* shell = Shell::GetInstance(); Shell* shell = Shell::GetInstance();
// TODO(jamescook): When overview becomes the default the AcceleratorAction // TODO(jamescook): When overview becomes the default the AcceleratorAction
...@@ -130,98 +165,206 @@ void HandleCycleLinear(const ui::Accelerator& accelerator) { ...@@ -130,98 +165,206 @@ void HandleCycleLinear(const ui::Accelerator& accelerator) {
if (switches::UseOverviewMode()) { if (switches::UseOverviewMode()) {
content::RecordAction(content::UserMetricsAction("Accel_Overview_F5")); content::RecordAction(content::UserMetricsAction("Accel_Overview_F5"));
shell->window_selector_controller()->ToggleOverview(); shell->window_selector_controller()->ToggleOverview();
return; return true;
} }
if (accelerator.key_code() == ui::VKEY_MEDIA_LAUNCH_APP1) if (accelerator.key_code() == ui::VKEY_MEDIA_LAUNCH_APP1)
content::RecordAction(content::UserMetricsAction("Accel_NextWindow_F5")); content::RecordAction(content::UserMetricsAction("Accel_NextWindow_F5"));
shell->window_cycle_controller()->HandleLinearCycleWindow(); shell->window_cycle_controller()->HandleLinearCycleWindow();
return true;
} }
bool HandleAccessibleFocusCycle(bool reverse) { bool HandleDisableCapsLock(ui::KeyboardCode key_code,
if (!Shell::GetInstance()->accessibility_delegate()-> ui::EventType previous_event_type,
IsSpokenFeedbackEnabled()) { ui::KeyboardCode previous_key_code) {
Shell* shell = Shell::GetInstance();
if (previous_event_type == ui::ET_KEY_RELEASED ||
(previous_key_code != ui::VKEY_LSHIFT &&
previous_key_code != ui::VKEY_SHIFT &&
previous_key_code != ui::VKEY_RSHIFT)) {
// If something else was pressed between the Shift key being pressed
// and released, then ignore the release of the Shift key.
return false; return false;
} }
aura::Window* active_window = ash::wm::GetActiveWindow(); content::RecordAction(
if (!active_window) content::UserMetricsAction("Accel_Disable_Caps_Lock"));
return false; if (shell->caps_lock_delegate()->IsCapsLockEnabled()) {
views::Widget* widget = shell->caps_lock_delegate()->SetCapsLockEnabled(false);
views::Widget::GetWidgetForNativeWindow(active_window); return true;
if (!widget) }
return false; return false;
views::FocusManager* focus_manager = widget->GetFocusManager(); }
if (!focus_manager)
return false;
views::View* view = focus_manager->GetFocusedView();
if (!view)
return false;
if (!strcmp(view->GetClassName(), views::WebView::kViewClassName))
return false;
focus_manager->AdvanceFocus(reverse); bool HandleFocusLauncher() {
Shell* shell = Shell::GetInstance();
content::RecordAction(content::UserMetricsAction("Accel_Focus_Launcher"));
return shell->focus_cycler()->FocusWidget(
Launcher::ForPrimaryDisplay()->shelf_widget());
}
bool HandleLaunchAppN(int n) {
content::RecordAction(
content::UserMetricsAction("Accel_Launch_App"));
Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(n);
return true; return true;
} }
void HandleSilenceSpokenFeedback() { bool HandleLaunchLastApp() {
AccessibilityDelegate* delegate = content::RecordAction(
Shell::GetInstance()->accessibility_delegate(); content::UserMetricsAction("Accel_Launch_Last_App"));
if (!delegate->IsSpokenFeedbackEnabled()) Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(-1);
return; return true;
delegate->SilenceSpokenFeedback();
} }
#if defined(OS_CHROMEOS) // Magnify the screen
bool HandleLock() { bool HandleMagnifyScreen(int delta_index) {
Shell::GetInstance()->session_state_delegate()->LockScreen(); if (ash::Shell::GetInstance()->magnification_controller()->IsEnabled()) {
// TODO(yoshiki): Move the following logic to MagnificationController.
float scale =
ash::Shell::GetInstance()->magnification_controller()->GetScale();
// Calculate rounded logarithm (base kMagnificationScaleFactor) of scale.
int scale_index =
std::floor(std::log(scale) / std::log(kMagnificationScaleFactor) + 0.5);
int new_scale_index = std::max(0, std::min(8, scale_index + delta_index));
ash::Shell::GetInstance()->magnification_controller()->
SetScale(std::pow(kMagnificationScaleFactor, new_scale_index), true);
} else if (ash::Shell::GetInstance()->
partial_magnification_controller()->is_enabled()) {
float scale = delta_index > 0 ? kDefaultPartialMagnifiedScale : 1;
ash::Shell::GetInstance()->partial_magnification_controller()->
SetScale(scale);
}
return true; return true;
} }
bool HandleFileManager() { bool HandleMediaNextTrack() {
Shell::GetInstance()->new_window_delegate()->OpenFileManager(); Shell::GetInstance()->media_delegate()->HandleMediaNextTrack();
return true; return true;
} }
bool HandleCrosh() { bool HandleMediaPlayPause() {
Shell::GetInstance()->new_window_delegate()->OpenCrosh(); Shell::GetInstance()->media_delegate()->HandleMediaPlayPause();
return true; return true;
} }
bool HandleToggleSpokenFeedback() { bool HandleMediaPrevTrack() {
Shell::GetInstance()->accessibility_delegate()-> Shell::GetInstance()->media_delegate()->HandleMediaPrevTrack();
ToggleSpokenFeedback(A11Y_NOTIFICATION_SHOW);
return true; return true;
} }
bool SwitchToNextUser() { bool HandleNewIncognitoWindow() {
if (!Shell::GetInstance()->delegate()->IsMultiProfilesEnabled()) content::RecordAction(
return false; content::UserMetricsAction("Accel_New_Incognito_Window"));
ash::SessionStateDelegate* delegate = bool incognito_allowed =
ash::Shell::GetInstance()->session_state_delegate(); Shell::GetInstance()->delegate()->IsIncognitoAllowed();
if (delegate->NumberOfLoggedInUsers() <= 1) if (incognito_allowed)
Shell::GetInstance()->new_window_delegate()->NewWindow(
true /* is_incognito */);
return incognito_allowed;
}
bool HandleNewTab(ui::KeyboardCode key_code) {
if (key_code == ui::VKEY_T)
content::RecordAction(content::UserMetricsAction("Accel_NewTab_T"));
Shell::GetInstance()->new_window_delegate()->NewTab();
return true;
}
bool HandleNewWindow() {
content::RecordAction(content::UserMetricsAction("Accel_New_Window"));
Shell::GetInstance()->new_window_delegate()->NewWindow(
false /* is_incognito */);
return true;
}
bool HandleNextIme(ImeControlDelegate* ime_control_delegate,
ui::EventType previous_event_type,
ui::KeyboardCode previous_key_code) {
// This check is necessary e.g. not to process the Shift+Alt+
// ET_KEY_RELEASED accelerator for Chrome OS (see ash/accelerators/
// accelerator_controller.cc) when Shift+Alt+Tab is pressed and then Tab
// is released.
if (previous_event_type == ui::ET_KEY_RELEASED &&
// Workaround for crbug.com/139556. CJK IME users tend to press
// Enter (or Space) and Shift+Alt almost at the same time to commit
// an IME string and then switch from the IME to the English layout.
// This workaround allows the user to trigger NEXT_IME even if the
// user presses Shift+Alt before releasing Enter.
// TODO(nona|mazda): Fix crbug.com/139556 in a cleaner way.
previous_key_code != ui::VKEY_RETURN &&
previous_key_code != ui::VKEY_SPACE) {
// We totally ignore this accelerator.
// TODO(mazda): Fix crbug.com/158217
return false; return false;
MultiProfileUMA::RecordSwitchActiveUser( }
MultiProfileUMA::SWITCH_ACTIVE_USER_BY_ACCELERATOR); content::RecordAction(
delegate->SwitchActiveUserToNext(); content::UserMetricsAction("Accel_Next_Ime"));
if (ime_control_delegate)
return ime_control_delegate->HandleNextIme();
return false;
}
bool HandleOpenFeedbackPage() {
content::RecordAction(
content::UserMetricsAction("Accel_Open_Feedback_Page"));
ash::Shell::GetInstance()->new_window_delegate()->OpenFeedbackPage();
return true; return true;
} }
#endif // defined(OS_CHROMEOS) bool HandlePositionCenter() {
content::RecordAction(
content::UserMetricsAction("Accel_Window_Position_Center"));
aura::Window* window = wm::GetActiveWindow();
// Docked windows do not support centering and ignore accelerator.
if (window && !wm::GetWindowState(window)->IsDocked()) {
wm::CenterWindow(window);
return true;
}
return false;
}
bool HandlePreviousIme(ImeControlDelegate* ime_control_delegate,
const ui::Accelerator& accelerator) {
content::RecordAction(
content::UserMetricsAction("Accel_Previous_Ime"));
if (ime_control_delegate)
return ime_control_delegate->HandlePreviousIme(accelerator);
return false;
}
bool HandleRestoreTab() {
content::RecordAction(content::UserMetricsAction("Accel_Restore_Tab"));
Shell::GetInstance()->new_window_delegate()->RestoreTab();
return true;
}
bool HandleRotatePaneFocus(Shell::Direction direction) { bool HandleRotatePaneFocus(Shell::Direction direction) {
Shell* shell = Shell::GetInstance(); Shell* shell = Shell::GetInstance();
switch (direction) { switch (direction) {
case Shell::FORWARD: // TODO(stevet): Not sure if this is the same as IDC_FOCUS_NEXT_PANE.
case Shell::FORWARD: {
content::RecordAction(
content::UserMetricsAction("Accel_Focus_Next_Pane"));
shell->focus_cycler()->RotateFocus(internal::FocusCycler::FORWARD); shell->focus_cycler()->RotateFocus(internal::FocusCycler::FORWARD);
break; break;
case Shell::BACKWARD: }
case Shell::BACKWARD: {
content::RecordAction(
content::UserMetricsAction("Accel_Focus_Previous_Pane"));
shell->focus_cycler()->RotateFocus(internal::FocusCycler::BACKWARD); shell->focus_cycler()->RotateFocus(internal::FocusCycler::BACKWARD);
break; break;
}
} }
return true; return true;
} }
// Rotate the active window. // Rotate the active window.
bool HandleRotateActiveWindow() { bool HandleRotateActiveWindow() {
content::RecordAction(
content::UserMetricsAction("Accel_Rotate_Window"));
aura::Window* active_window = wm::GetActiveWindow(); aura::Window* active_window = wm::GetActiveWindow();
if (active_window) { if (active_window) {
// The rotation animation bases its target transform on the current // The rotation animation bases its target transform on the current
...@@ -252,12 +395,48 @@ gfx::Display::Rotation GetNextRotation(gfx::Display::Rotation current) { ...@@ -252,12 +395,48 @@ gfx::Display::Rotation GetNextRotation(gfx::Display::Rotation current) {
return gfx::Display::ROTATE_0; return gfx::Display::ROTATE_0;
} }
// Rotates the screen.
bool HandleRotateScreen() {
content::RecordAction(
content::UserMetricsAction("Accel_Rotate_Window"));
gfx::Point point = Shell::GetScreen()->GetCursorScreenPoint();
gfx::Display display = Shell::GetScreen()->GetDisplayNearestPoint(point);
const DisplayInfo& display_info =
Shell::GetInstance()->display_manager()->GetDisplayInfo(display.id());
Shell::GetInstance()->display_manager()->SetDisplayRotation(
display.id(), GetNextRotation(display_info.rotation()));
return true;
}
bool HandleScaleReset() {
internal::DisplayManager* display_manager =
Shell::GetInstance()->display_manager();
int64 display_id = display_manager->GetDisplayIdForUIScaling();
if (display_id == gfx::Display::kInvalidDisplayID)
return false;
content::RecordAction(
content::UserMetricsAction("Accel_Scale_Ui_Reset"));
display_manager->SetDisplayUIScale(display_id, 1.0f);
return true;
}
bool HandleScaleUI(bool up) { bool HandleScaleUI(bool up) {
internal::DisplayManager* display_manager = internal::DisplayManager* display_manager =
Shell::GetInstance()->display_manager(); Shell::GetInstance()->display_manager();
int64 display_id = display_manager->GetDisplayIdForUIScaling(); int64 display_id = display_manager->GetDisplayIdForUIScaling();
if (display_id == gfx::Display::kInvalidDisplayID) if (display_id == gfx::Display::kInvalidDisplayID)
return false; return false;
if (up) {
content::RecordAction(
content::UserMetricsAction("Accel_Scale_Ui_Up"));
} else {
content::RecordAction(
content::UserMetricsAction("Accel_Scale_Ui_Down"));
}
const DisplayInfo& display_info = display_manager->GetDisplayInfo(display_id); const DisplayInfo& display_info = display_manager->GetDisplayInfo(display_id);
float next_scale = float next_scale =
internal::DisplayManager::GetNextUIScale(display_info, up); internal::DisplayManager::GetNextUIScale(display_info, up);
...@@ -265,24 +444,156 @@ bool HandleScaleUI(bool up) { ...@@ -265,24 +444,156 @@ bool HandleScaleUI(bool up) {
return true; return true;
} }
bool HandleScaleReset() { bool HandleSwapPrimaryDisplay() {
internal::DisplayManager* display_manager = content::RecordAction(
Shell::GetInstance()->display_manager(); content::UserMetricsAction("Accel_Swap_Primary_Display"));
int64 display_id = display_manager->GetDisplayIdForUIScaling(); Shell::GetInstance()->display_controller()->SwapPrimaryDisplay();
if (display_id == gfx::Display::kInvalidDisplayID) return true;
}
bool HandleShowKeyboardOverlay() {
content::RecordAction(
content::UserMetricsAction("Accel_Show_Keyboard_Overlay"));
ash::Shell::GetInstance()->new_window_delegate()->ShowKeyboardOverlay();
return true;
}
void HandleShowMessageCenterBubble() {
content::RecordAction(
content::UserMetricsAction("Accel_Show_Message_Center_Bubble"));
internal::RootWindowController* controller =
internal::RootWindowController::ForTargetRootWindow();
internal::StatusAreaWidget* status_area_widget =
controller->shelf()->status_area_widget();
if (status_area_widget) {
WebNotificationTray* notification_tray =
status_area_widget->web_notification_tray();
if (notification_tray->visible())
notification_tray->ShowMessageCenterBubble();
}
}
bool HandleShowOak() {
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kAshEnableOak)) {
oak::ShowOakWindowWithContext(Shell::GetPrimaryRootWindow());
return true;
}
return false;
}
bool HandleShowSystemTrayBubble() {
content::RecordAction(
content::UserMetricsAction("Accel_Show_System_Tray_Bubble"));
internal::RootWindowController* controller =
internal::RootWindowController::ForTargetRootWindow();
if (!controller->GetSystemTray()->HasSystemBubble()) {
controller->GetSystemTray()->ShowDefaultView(BUBBLE_CREATE_NEW);
return true;
}
return false;
}
bool HandleShowTaskManager() {
content::RecordAction(
content::UserMetricsAction("Accel_Show_Task_Manager"));
Shell::GetInstance()->new_window_delegate()->ShowTaskManager();
return true;
}
void HandleSilenceSpokenFeedback() {
content::RecordAction(
content::UserMetricsAction("Accel_Silence_Spoken_Feedback"));
AccessibilityDelegate* delegate =
Shell::GetInstance()->accessibility_delegate();
if (!delegate->IsSpokenFeedbackEnabled())
return;
delegate->SilenceSpokenFeedback();
}
bool HandleSwitchIme(ImeControlDelegate* ime_control_delegate,
const ui::Accelerator& accelerator) {
content::RecordAction(
content::UserMetricsAction("Accel_Switch_Ime"));
if (ime_control_delegate)
return ime_control_delegate->HandleSwitchIme(accelerator);
return false;
}
bool HandleTakePartialScreenshot(ScreenshotDelegate* screenshot_delegate) {
content::RecordAction(
content::UserMetricsAction("Accel_Take_Partial_Screenshot"));
if (screenshot_delegate) {
ash::PartialScreenshotView::StartPartialScreenshot(
screenshot_delegate);
}
// Return true to prevent propagation of the key event because
// this key combination is reserved for partial screenshot.
return true;
}
bool HandleTakeScreenshot(ScreenshotDelegate* screenshot_delegate) {
content::RecordAction(
content::UserMetricsAction("Accel_Take_Screenshot"));
if (screenshot_delegate &&
screenshot_delegate->CanTakeScreenshot()) {
screenshot_delegate->HandleTakeScreenshotForAllRootWindows();
}
// Return true to prevent propagation of the key event.
return true;
}
bool HandleToggleAppList(ui::KeyboardCode key_code,
ui::EventType previous_event_type,
ui::KeyboardCode previous_key_code,
const ui::Accelerator& accelerator) {
// If something else was pressed between the Search key (LWIN)
// being pressed and released, then ignore the release of the
// Search key.
if (key_code == ui::VKEY_LWIN &&
(previous_event_type == ui::ET_KEY_RELEASED ||
previous_key_code != ui::VKEY_LWIN))
return false; return false;
display_manager->SetDisplayUIScale(display_id, 1.0f); if (key_code == ui::VKEY_LWIN)
content::RecordAction(content::UserMetricsAction("Accel_Search_LWin"));
// When spoken feedback is enabled, we should neither toggle the list nor
// consume the key since Search+Shift is one of the shortcuts the a11y
// feature uses. crbug.com/132296
DCHECK_EQ(ui::VKEY_LWIN, accelerator.key_code());
if (Shell::GetInstance()->accessibility_delegate()->
IsSpokenFeedbackEnabled())
return false;
ash::Shell::GetInstance()->ToggleAppList(NULL);
return true; return true;
} }
// Rotates the screen. bool HandleToggleCapsLock(ui::KeyboardCode key_code,
bool HandleRotateScreen() { ui::EventType previous_event_type,
gfx::Point point = Shell::GetScreen()->GetCursorScreenPoint(); ui::KeyboardCode previous_key_code) {
gfx::Display display = Shell::GetScreen()->GetDisplayNearestPoint(point); Shell* shell = Shell::GetInstance();
const DisplayInfo& display_info = if (key_code == ui::VKEY_LWIN) {
Shell::GetInstance()->display_manager()->GetDisplayInfo(display.id()); // If something else was pressed between the Search key (LWIN)
Shell::GetInstance()->display_manager()->SetDisplayRotation( // being pressed and released, then ignore the release of the
display.id(), GetNextRotation(display_info.rotation())); // Search key.
// TODO(danakj): Releasing Alt first breaks this: crbug.com/166495
if (previous_event_type == ui::ET_KEY_RELEASED ||
previous_key_code != ui::VKEY_LWIN)
return false;
}
content::RecordAction(
content::UserMetricsAction("Accel_Toggle_Caps_Lock"));
shell->caps_lock_delegate()->ToggleCapsLock();
return true;
}
bool HandleToggleFullscreen(ui::KeyboardCode key_code) {
if (key_code == ui::VKEY_MEDIA_LAUNCH_APP2) {
content::RecordAction(
content::UserMetricsAction("Accel_Fullscreen_F4"));
}
accelerators::ToggleFullscreen();
return true; return true;
} }
...@@ -291,45 +602,125 @@ bool HandleToggleRootWindowFullScreen() { ...@@ -291,45 +602,125 @@ bool HandleToggleRootWindowFullScreen() {
return true; return true;
} }
// Magnify the screen bool HandleWindowSnap(int action) {
bool HandleMagnifyScreen(int delta_index) { wm::WindowState* window_state = wm::GetActiveWindowState();
if (ash::Shell::GetInstance()->magnification_controller()->IsEnabled()) { // Disable window snapping shortcut key for full screen window due to
// TODO(yoshiki): Move the following logic to MagnificationController. // http://crbug.com/135487.
float scale = if (!window_state ||
ash::Shell::GetInstance()->magnification_controller()->GetScale(); window_state->window()->type() != aura::client::WINDOW_TYPE_NORMAL ||
// Calculate rounded logarithm (base kMagnificationScaleFactor) of scale. window_state->IsFullscreen()) {
int scale_index = return false;
std::floor(std::log(scale) / std::log(kMagnificationScaleFactor) + 0.5); }
int new_scale_index = std::max(0, std::min(8, scale_index + delta_index));
ash::Shell::GetInstance()->magnification_controller()-> if (action == WINDOW_SNAP_LEFT) {
SetScale(std::pow(kMagnificationScaleFactor, new_scale_index), true); content::RecordAction(
} else if (ash::Shell::GetInstance()-> content::UserMetricsAction("Accel_Window_Snap_Left"));
partial_magnification_controller()->is_enabled()) { } else {
float scale = delta_index > 0 ? kDefaultPartialMagnifiedScale : 1; content::RecordAction(
ash::Shell::GetInstance()->partial_magnification_controller()-> content::UserMetricsAction("Accel_Window_Snap_Right"));
SetScale(scale);
} }
internal::SnapSizer::SnapWindow(window_state,
action == WINDOW_SNAP_LEFT ? internal::SnapSizer::LEFT_EDGE :
internal::SnapSizer::RIGHT_EDGE);
return true; return true;
} }
bool HandleMediaNextTrack() { #if defined(OS_CHROMEOS)
Shell::GetInstance()->media_delegate()->HandleMediaNextTrack(); bool HandleAddRemoveDisplay() {
content::RecordAction(
content::UserMetricsAction("Accel_Add_Remove_Display"));
Shell::GetInstance()->display_manager()->AddRemoveDisplay();
return true; return true;
} }
bool HandleMediaPlayPause() { bool HandleCrosh() {
Shell::GetInstance()->media_delegate()->HandleMediaPlayPause(); content::RecordAction(
content::UserMetricsAction("Accel_Open_Crosh"));
Shell::GetInstance()->new_window_delegate()->OpenCrosh();
return true; return true;
} }
bool HandleMediaPrevTrack() { bool HandleFileManager() {
Shell::GetInstance()->media_delegate()->HandleMediaPrevTrack(); content::RecordAction(
content::UserMetricsAction("Accel_Open_File_Manager"));
Shell::GetInstance()->new_window_delegate()->OpenFileManager();
return true; return true;
} }
bool HandleLock(ui::KeyboardCode key_code) {
content::RecordAction(
content::UserMetricsAction("Accel_LockScreen_L"));
Shell::GetInstance()->session_state_delegate()->LockScreen();
return true;
}
bool HandleSwitchToNextUser() {
content::RecordAction(
content::UserMetricsAction("Accel_Switch_To_Next_User"));
if (!Shell::GetInstance()->delegate()->IsMultiProfilesEnabled())
return false;
ash::SessionStateDelegate* delegate =
ash::Shell::GetInstance()->session_state_delegate();
if (delegate->NumberOfLoggedInUsers() <= 1)
return false;
MultiProfileUMA::RecordSwitchActiveUser(
MultiProfileUMA::SWITCH_ACTIVE_USER_BY_ACCELERATOR);
delegate->SwitchActiveUserToNext();
return true;
}
bool HandleToggleMirrorMode() {
content::RecordAction(
content::UserMetricsAction("Accel_Toggle_Mirror_Mode"));
Shell::GetInstance()->display_controller()->ToggleMirrorMode();
return true;
}
bool HandleToggleSpokenFeedback() {
content::RecordAction(
content::UserMetricsAction("Accel_Toggle_Spoken_Feedback"));
Shell::GetInstance()->accessibility_delegate()->
ToggleSpokenFeedback(A11Y_NOTIFICATION_SHOW);
return true;
}
bool HandleTouchHudClear() {
internal::RootWindowController* controller =
internal::RootWindowController::ForTargetRootWindow();
if (controller->touch_hud_debug()) {
controller->touch_hud_debug()->Clear();
return true;
}
return false;
}
bool HandleTouchHudModeChange() {
internal::RootWindowController* controller =
internal::RootWindowController::ForTargetRootWindow();
if (controller->touch_hud_debug()) {
controller->touch_hud_debug()->ChangeToNextMode();
return true;
}
return false;
}
bool HandleTouchHudProjectToggle() {
content::RecordAction(
content::UserMetricsAction("Accel_Touch_Hud_Clear"));
bool enabled = Shell::GetInstance()->is_touch_hud_projection_enabled();
Shell::GetInstance()->SetTouchHudProjectionEnabled(!enabled);
return true;
}
#endif // defined(OS_CHROMEOS)
// Debug print methods.
bool HandlePrintLayerHierarchy() { bool HandlePrintLayerHierarchy() {
aura::Window::Windows root_windows = Shell::GetAllRootWindows(); aura::Window::Windows root_windows = Shell::GetAllRootWindows();
for (size_t i = 0; i < root_windows.size(); ++i) { for (size_t i = 0; i < root_windows.size(); ++i) {
...@@ -565,25 +956,18 @@ bool AcceleratorController::PerformAction(int action, ...@@ -565,25 +956,18 @@ bool AcceleratorController::PerformAction(int action,
case ACCESSIBLE_FOCUS_PREVIOUS: case ACCESSIBLE_FOCUS_PREVIOUS:
return HandleAccessibleFocusCycle(true); return HandleAccessibleFocusCycle(true);
case CYCLE_BACKWARD_MRU: case CYCLE_BACKWARD_MRU:
HandleCycleBackwardMRU(accelerator); return HandleCycleBackwardMRU(accelerator);
return true;
case CYCLE_FORWARD_MRU: case CYCLE_FORWARD_MRU:
HandleCycleForwardMRU(accelerator); return HandleCycleForwardMRU(accelerator);
return true;
case CYCLE_LINEAR: case CYCLE_LINEAR:
HandleCycleLinear(accelerator); return HandleCycleLinear(accelerator);
return true;
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
case ADD_REMOVE_DISPLAY: case ADD_REMOVE_DISPLAY:
Shell::GetInstance()->display_manager()->AddRemoveDisplay(); return HandleAddRemoveDisplay();
return true;
case TOGGLE_MIRROR_MODE: case TOGGLE_MIRROR_MODE:
Shell::GetInstance()->display_controller()->ToggleMirrorMode(); return HandleToggleMirrorMode();
return true;
case LOCK_SCREEN: case LOCK_SCREEN:
if (key_code == ui::VKEY_L) return HandleLock(key_code);
content::RecordAction(content::UserMetricsAction("Accel_LockScreen_L"));
return HandleLock();
case OPEN_FILE_MANAGER: case OPEN_FILE_MANAGER:
return HandleFileManager(); return HandleFileManager();
case OPEN_CROSH: case OPEN_CROSH:
...@@ -592,129 +976,51 @@ bool AcceleratorController::PerformAction(int action, ...@@ -592,129 +976,51 @@ bool AcceleratorController::PerformAction(int action,
HandleSilenceSpokenFeedback(); HandleSilenceSpokenFeedback();
break; break;
case SWAP_PRIMARY_DISPLAY: case SWAP_PRIMARY_DISPLAY:
Shell::GetInstance()->display_controller()->SwapPrimaryDisplay(); return HandleSwapPrimaryDisplay();
return true;
case SWITCH_TO_NEXT_USER: case SWITCH_TO_NEXT_USER:
return SwitchToNextUser(); return HandleSwitchToNextUser();
case TOGGLE_SPOKEN_FEEDBACK: case TOGGLE_SPOKEN_FEEDBACK:
return HandleToggleSpokenFeedback(); return HandleToggleSpokenFeedback();
case TOGGLE_WIFI: case TOGGLE_WIFI:
Shell::GetInstance()->system_tray_notifier()->NotifyRequestToggleWifi(); Shell::GetInstance()->system_tray_notifier()->NotifyRequestToggleWifi();
return true; return true;
case TOUCH_HUD_CLEAR: { case TOUCH_HUD_CLEAR:
internal::RootWindowController* controller = return HandleTouchHudClear();
internal::RootWindowController::ForTargetRootWindow(); case TOUCH_HUD_MODE_CHANGE:
if (controller->touch_hud_debug()) { return HandleTouchHudModeChange();
controller->touch_hud_debug()->Clear(); case TOUCH_HUD_PROJECTION_TOGGLE:
return true; return HandleTouchHudProjectToggle();
}
return false;
}
case TOUCH_HUD_MODE_CHANGE: {
internal::RootWindowController* controller =
internal::RootWindowController::ForTargetRootWindow();
if (controller->touch_hud_debug()) {
controller->touch_hud_debug()->ChangeToNextMode();
return true;
}
return false;
}
case TOUCH_HUD_PROJECTION_TOGGLE: {
bool enabled = Shell::GetInstance()->is_touch_hud_projection_enabled();
Shell::GetInstance()->SetTouchHudProjectionEnabled(!enabled);
return true;
}
case DISABLE_GPU_WATCHDOG: case DISABLE_GPU_WATCHDOG:
content::GpuDataManager::GetInstance()->DisableGpuWatchdog(); content::GpuDataManager::GetInstance()->DisableGpuWatchdog();
return true; return true;
#endif #endif // OS_CHROMEOS
case OPEN_FEEDBACK_PAGE: case OPEN_FEEDBACK_PAGE:
ash::Shell::GetInstance()->new_window_delegate()->OpenFeedbackPage(); return HandleOpenFeedbackPage();
return true;
case EXIT: case EXIT:
// UMA metrics are recorded in the handler. // UMA metrics are recorded in the handler.
exit_warning_handler_.HandleAccelerator(); exit_warning_handler_.HandleAccelerator();
return true; return true;
case NEW_INCOGNITO_WINDOW: { case NEW_INCOGNITO_WINDOW:
bool incognito_allowed = return HandleNewIncognitoWindow();
Shell::GetInstance()->delegate()->IsIncognitoAllowed();
if (incognito_allowed)
Shell::GetInstance()->new_window_delegate()->NewWindow(
true /* is_incognito */);
return incognito_allowed;
}
case NEW_TAB: case NEW_TAB:
if (key_code == ui::VKEY_T) return HandleNewTab(key_code);
content::RecordAction(content::UserMetricsAction("Accel_NewTab_T"));
Shell::GetInstance()->new_window_delegate()->NewTab();
return true;
case NEW_WINDOW: case NEW_WINDOW:
Shell::GetInstance()->new_window_delegate()->NewWindow( return HandleNewWindow();
false /* is_incognito */);
return true;
case RESTORE_TAB: case RESTORE_TAB:
Shell::GetInstance()->new_window_delegate()->RestoreTab(); return HandleRestoreTab();
return true;
case TAKE_SCREENSHOT: case TAKE_SCREENSHOT:
if (screenshot_delegate_.get() && return HandleTakeScreenshot(screenshot_delegate_.get());
screenshot_delegate_->CanTakeScreenshot()) {
screenshot_delegate_->HandleTakeScreenshotForAllRootWindows();
}
// Return true to prevent propagation of the key event.
return true;
case TAKE_PARTIAL_SCREENSHOT: case TAKE_PARTIAL_SCREENSHOT:
if (screenshot_delegate_) { return HandleTakePartialScreenshot(screenshot_delegate_.get());
ash::PartialScreenshotView::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: case TOGGLE_APP_LIST:
// If something else was pressed between the Search key (LWIN) return HandleToggleAppList(
// being pressed and released, then ignore the release of the key_code, previous_event_type, previous_key_code, accelerator);
// Search key.
if (key_code == ui::VKEY_LWIN &&
(previous_event_type == ui::ET_KEY_RELEASED ||
previous_key_code != ui::VKEY_LWIN))
return false;
if (key_code == ui::VKEY_LWIN)
content::RecordAction(content::UserMetricsAction("Accel_Search_LWin"));
// When spoken feedback is enabled, we should neither toggle the list nor
// consume the key since Search+Shift is one of the shortcuts the a11y
// feature uses. crbug.com/132296
DCHECK_EQ(ui::VKEY_LWIN, accelerator.key_code());
if (Shell::GetInstance()->accessibility_delegate()->
IsSpokenFeedbackEnabled())
return false;
ash::Shell::GetInstance()->ToggleAppList(NULL);
return true;
case DISABLE_CAPS_LOCK: case DISABLE_CAPS_LOCK:
if (previous_event_type == ui::ET_KEY_RELEASED || return HandleDisableCapsLock(
(previous_key_code != ui::VKEY_LSHIFT && key_code, previous_event_type, previous_key_code);
previous_key_code != ui::VKEY_SHIFT &&
previous_key_code != ui::VKEY_RSHIFT)) {
// If something else was pressed between the Shift key being pressed
// and released, then ignore the release of the Shift key.
return false;
}
if (shell->caps_lock_delegate()->IsCapsLockEnabled()) {
shell->caps_lock_delegate()->SetCapsLockEnabled(false);
return true;
}
return false;
case TOGGLE_CAPS_LOCK: case TOGGLE_CAPS_LOCK:
if (key_code == ui::VKEY_LWIN) { return HandleToggleCapsLock(
// If something else was pressed between the Search key (LWIN) key_code, previous_event_type, previous_key_code);
// being pressed and released, then ignore the release of the
// Search key.
// TODO(danakj): Releasing Alt first breaks this: crbug.com/166495
if (previous_event_type == ui::ET_KEY_RELEASED ||
previous_key_code != ui::VKEY_LWIN)
return false;
}
shell->caps_lock_delegate()->ToggleCapsLock();
return true;
case BRIGHTNESS_DOWN: case BRIGHTNESS_DOWN:
if (brightness_control_delegate_) if (brightness_control_delegate_)
return brightness_control_delegate_->HandleBrightnessDown(accelerator); return brightness_control_delegate_->HandleBrightnessDown(accelerator);
...@@ -749,145 +1055,61 @@ bool AcceleratorController::PerformAction(int action, ...@@ -749,145 +1055,61 @@ bool AcceleratorController::PerformAction(int action,
return volume_delegate && volume_delegate->HandleVolumeUp(accelerator); return volume_delegate && volume_delegate->HandleVolumeUp(accelerator);
} }
case FOCUS_LAUNCHER: case FOCUS_LAUNCHER:
return shell->focus_cycler()->FocusWidget( return HandleFocusLauncher();
Launcher::ForPrimaryDisplay()->shelf_widget());
case FOCUS_NEXT_PANE: case FOCUS_NEXT_PANE:
return HandleRotatePaneFocus(Shell::FORWARD); return HandleRotatePaneFocus(Shell::FORWARD);
case FOCUS_PREVIOUS_PANE: case FOCUS_PREVIOUS_PANE:
return HandleRotatePaneFocus(Shell::BACKWARD); return HandleRotatePaneFocus(Shell::BACKWARD);
case SHOW_KEYBOARD_OVERLAY: case SHOW_KEYBOARD_OVERLAY:
ash::Shell::GetInstance()->new_window_delegate()->ShowKeyboardOverlay(); return HandleShowKeyboardOverlay();
return true;
case SHOW_OAK: case SHOW_OAK:
if (CommandLine::ForCurrentProcess()->HasSwitch( return HandleShowOak();
switches::kAshEnableOak)) { case SHOW_SYSTEM_TRAY_BUBBLE:
oak::ShowOakWindowWithContext(Shell::GetPrimaryRootWindow()); return HandleShowSystemTrayBubble();
return true; case SHOW_MESSAGE_CENTER_BUBBLE:
} HandleShowMessageCenterBubble();
break; break;
case SHOW_SYSTEM_TRAY_BUBBLE: {
internal::RootWindowController* controller =
internal::RootWindowController::ForTargetRootWindow();
if (!controller->GetSystemTray()->HasSystemBubble()) {
controller->GetSystemTray()->ShowDefaultView(BUBBLE_CREATE_NEW);
return true;
}
break;
}
case SHOW_MESSAGE_CENTER_BUBBLE: {
internal::RootWindowController* controller =
internal::RootWindowController::ForTargetRootWindow();
internal::StatusAreaWidget* status_area_widget =
controller->shelf()->status_area_widget();
if (status_area_widget) {
WebNotificationTray* notification_tray =
status_area_widget->web_notification_tray();
if (notification_tray->visible())
notification_tray->ShowMessageCenterBubble();
}
break;
}
case SHOW_TASK_MANAGER: case SHOW_TASK_MANAGER:
Shell::GetInstance()->new_window_delegate()->ShowTaskManager(); return HandleShowTaskManager();
return true;
case NEXT_IME: case NEXT_IME:
// This check is necessary e.g. not to process the Shift+Alt+ return HandleNextIme(
// ET_KEY_RELEASED accelerator for Chrome OS (see ash/accelerators/ ime_control_delegate_.get(), previous_event_type, previous_key_code);
// accelerator_controller.cc) when Shift+Alt+Tab is pressed and then Tab
// is released.
if (previous_event_type == ui::ET_KEY_RELEASED &&
// Workaround for crbug.com/139556. CJK IME users tend to press
// Enter (or Space) and Shift+Alt almost at the same time to commit
// an IME string and then switch from the IME to the English layout.
// This workaround allows the user to trigger NEXT_IME even if the
// user presses Shift+Alt before releasing Enter.
// TODO(nona|mazda): Fix crbug.com/139556 in a cleaner way.
previous_key_code != ui::VKEY_RETURN &&
previous_key_code != ui::VKEY_SPACE) {
// We totally ignore this accelerator.
// TODO(mazda): Fix crbug.com/158217
return false;
}
if (ime_control_delegate_)
return ime_control_delegate_->HandleNextIme();
break;
case PREVIOUS_IME: case PREVIOUS_IME:
if (ime_control_delegate_) return HandlePreviousIme(ime_control_delegate_.get(), accelerator);
return ime_control_delegate_->HandlePreviousIme(accelerator);
break;
case PRINT_UI_HIERARCHIES: case PRINT_UI_HIERARCHIES:
return HandlePrintUIHierarchies(); return HandlePrintUIHierarchies();
case SWITCH_IME: case SWITCH_IME:
if (ime_control_delegate_) return HandleSwitchIme(ime_control_delegate_.get(), accelerator);
return ime_control_delegate_->HandleSwitchIme(accelerator);
break;
case LAUNCH_APP_0: case LAUNCH_APP_0:
Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(0); return HandleLaunchAppN(0);
return true;
case LAUNCH_APP_1: case LAUNCH_APP_1:
Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(1); return HandleLaunchAppN(1);
return true;
case LAUNCH_APP_2: case LAUNCH_APP_2:
Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(2); return HandleLaunchAppN(2);
return true;
case LAUNCH_APP_3: case LAUNCH_APP_3:
Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(3); return HandleLaunchAppN(3);
return true;
case LAUNCH_APP_4: case LAUNCH_APP_4:
Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(4); return HandleLaunchAppN(4);
return true;
case LAUNCH_APP_5: case LAUNCH_APP_5:
Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(5); return HandleLaunchAppN(5);
return true;
case LAUNCH_APP_6: case LAUNCH_APP_6:
Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(6); return HandleLaunchAppN(6);
return true;
case LAUNCH_APP_7: case LAUNCH_APP_7:
Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(7); return HandleLaunchAppN(7);
return true;
case LAUNCH_LAST_APP: case LAUNCH_LAST_APP:
Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(-1); return HandleLaunchLastApp();
return true;
case WINDOW_SNAP_LEFT: case WINDOW_SNAP_LEFT:
case WINDOW_SNAP_RIGHT: { case WINDOW_SNAP_RIGHT:
wm::WindowState* window_state = wm::GetActiveWindowState(); return HandleWindowSnap(action);
// Disable window snapping shortcut key for full screen window due to
// http://crbug.com/135487.
if (!window_state ||
window_state->window()->type() != aura::client::WINDOW_TYPE_NORMAL ||
window_state->IsFullscreen()) {
break;
}
internal::SnapSizer::SnapWindow(window_state,
action == WINDOW_SNAP_LEFT ? internal::SnapSizer::LEFT_EDGE :
internal::SnapSizer::RIGHT_EDGE);
return true;
}
case WINDOW_MINIMIZE: case WINDOW_MINIMIZE:
return accelerators::ToggleMinimized(); return accelerators::ToggleMinimized();
case TOGGLE_FULLSCREEN: { case TOGGLE_FULLSCREEN:
if (key_code == ui::VKEY_MEDIA_LAUNCH_APP2) { return HandleToggleFullscreen(key_code);
content::RecordAction( case TOGGLE_MAXIMIZED:
content::UserMetricsAction("Accel_Fullscreen_F4"));
}
accelerators::ToggleFullscreen();
return true;
}
case TOGGLE_MAXIMIZED: {
accelerators::ToggleMaximized(); accelerators::ToggleMaximized();
return true; return true;
} case WINDOW_POSITION_CENTER:
case WINDOW_POSITION_CENTER: { return HandlePositionCenter();
content::RecordAction(content::UserMetricsAction("Accel_Center"));
aura::Window* window = wm::GetActiveWindow();
// Docked windows do not support centering and ignore accelerator.
if (window && !wm::GetWindowState(window)->IsDocked()) {
wm::CenterWindow(window);
return true;
}
break;
}
case SCALE_UI_UP: case SCALE_UI_UP:
return HandleScaleUI(true /* up */); return HandleScaleUI(true /* up */);
case SCALE_UI_DOWN: case SCALE_UI_DOWN:
......
...@@ -68,7 +68,6 @@ enum UserMetricsAction { ...@@ -68,7 +68,6 @@ enum UserMetricsAction {
UMA_GESTURE_OVERVIEW, UMA_GESTURE_OVERVIEW,
UMA_LAUNCHER_CLICK_ON_APP, UMA_LAUNCHER_CLICK_ON_APP,
UMA_LAUNCHER_CLICK_ON_APPLIST_BUTTON, UMA_LAUNCHER_CLICK_ON_APPLIST_BUTTON,
UMA_MINIMIZE_PER_KEY,
UMA_MOUSE_DOWN, UMA_MOUSE_DOWN,
UMA_SHELF_ALIGNMENT_SET_BOTTOM, UMA_SHELF_ALIGNMENT_SET_BOTTOM,
UMA_SHELF_ALIGNMENT_SET_LEFT, UMA_SHELF_ALIGNMENT_SET_LEFT,
......
...@@ -180,9 +180,6 @@ void ChromeShellDelegate::RecordUserMetricsAction( ...@@ -180,9 +180,6 @@ void ChromeShellDelegate::RecordUserMetricsAction(
chromeos::default_pinned_apps_field_trial::APP_LAUNCHER); chromeos::default_pinned_apps_field_trial::APP_LAUNCHER);
#endif #endif
break; break;
case ash::UMA_MINIMIZE_PER_KEY:
content::RecordAction(content::UserMetricsAction("Minimize_UsingKey"));
break;
case ash::UMA_MOUSE_DOWN: case ash::UMA_MOUSE_DOWN:
content::RecordAction(content::UserMetricsAction("Mouse_Down")); content::RecordAction(content::UserMetricsAction("Mouse_Down"));
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment