| OLD | NEW |
| (Empty) |
| 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 "chrome/browser/chromeos/status/caps_lock_menu_button.h" | |
| 6 | |
| 7 #include <string> | |
| 8 | |
| 9 #include "base/chromeos/chromeos_version.h" | |
| 10 #include "base/utf_string_conversions.h" | |
| 11 #include "chrome/browser/chromeos/input_method/input_method_manager.h" | |
| 12 #include "chrome/browser/chromeos/input_method/xkeyboard.h" | |
| 13 #include "chrome/browser/chromeos/login/screen_locker.h" | |
| 14 #include "chrome/browser/chromeos/status/status_area_bubble.h" | |
| 15 #include "chrome/browser/chromeos/status/status_area_view_chromeos.h" | |
| 16 #include "chrome/browser/chromeos/view_ids.h" | |
| 17 #include "chrome/browser/prefs/pref_service.h" | |
| 18 #include "chrome/browser/profiles/profile_manager.h" | |
| 19 #include "chrome/common/chrome_notification_types.h" | |
| 20 #include "chrome/common/pref_names.h" | |
| 21 #include "content/public/browser/notification_service.h" | |
| 22 #include "grit/generated_resources.h" | |
| 23 #include "grit/theme_resources.h" | |
| 24 #include "ui/base/l10n/l10n_util.h" | |
| 25 #include "ui/base/resource/resource_bundle.h" | |
| 26 #include "ui/gfx/image/image.h" | |
| 27 #include "ui/views/controls/image_view.h" | |
| 28 #include "ui/views/controls/menu/menu_item_view.h" | |
| 29 #include "ui/views/controls/menu/menu_runner.h" | |
| 30 #include "ui/views/controls/menu/submenu_view.h" | |
| 31 #include "ui/views/widget/widget.h" | |
| 32 | |
| 33 namespace { | |
| 34 | |
| 35 const size_t kMaxBubbleCount = 3; | |
| 36 | |
| 37 PrefService* GetPrefService() { | |
| 38 Profile* profile = ProfileManager::GetDefaultProfile(); | |
| 39 if (profile) | |
| 40 return profile->GetPrefs(); | |
| 41 return NULL; | |
| 42 } | |
| 43 | |
| 44 views::ImageView* CreateImageViewWithCapsLockIcon() { | |
| 45 const gfx::Image& image = | |
| 46 ResourceBundle::GetSharedInstance().GetImageNamed(IDR_CAPS_LOCK_ICON); | |
| 47 views::ImageView* image_view = new views::ImageView; | |
| 48 image_view->SetImage(image.ToSkBitmap()); | |
| 49 return image_view; | |
| 50 } | |
| 51 | |
| 52 } // namespace | |
| 53 | |
| 54 namespace chromeos { | |
| 55 | |
| 56 //////////////////////////////////////////////////////////////////////////////// | |
| 57 // CapsLockMenuButton | |
| 58 | |
| 59 CapsLockMenuButton::CapsLockMenuButton(StatusAreaButton::Delegate* delegate) | |
| 60 : StatusAreaButton(delegate, this), | |
| 61 initialized_prefs_(false), | |
| 62 status_(NULL), | |
| 63 should_show_bubble_(true), | |
| 64 bubble_count_(0) { | |
| 65 set_id(VIEW_ID_STATUS_BUTTON_CAPS_LOCK); | |
| 66 | |
| 67 if (StatusAreaViewChromeos::IsBrowserMode()) | |
| 68 InitializePrefMember(); | |
| 69 | |
| 70 SetIcon(*ResourceBundle::GetSharedInstance().GetBitmapNamed( | |
| 71 IDR_STATUSBAR_CAPS_LOCK)); | |
| 72 UpdateAccessibleName(); | |
| 73 | |
| 74 input_method::InputMethodManager* ime_manager = | |
| 75 input_method::InputMethodManager::GetInstance(); | |
| 76 UpdateUIFromCurrentCapsLock(ime_manager->GetXKeyboard()->CapsLockIsEnabled()); | |
| 77 | |
| 78 // Status bar should be initialized after SystemKeyEventListener on the | |
| 79 // device. SystemKeyEventListener is never initialized on chrome for cros | |
| 80 // running on linux. | |
| 81 DCHECK(SystemKeyEventListener::GetInstance() || | |
| 82 !base::chromeos::IsRunningOnChromeOS()); | |
| 83 if (SystemKeyEventListener::GetInstance()) | |
| 84 SystemKeyEventListener::GetInstance()->AddCapsLockObserver(this); | |
| 85 | |
| 86 #if defined(USE_ASH) | |
| 87 if (StatusAreaViewChromeos::IsLoginMode()) { | |
| 88 // See comments in InputMethodMenu::InputMethodMenu(). | |
| 89 registrar_.Add(this, | |
| 90 chrome::NOTIFICATION_SESSION_STARTED, | |
| 91 content::NotificationService::AllSources()); | |
| 92 } | |
| 93 #endif | |
| 94 } | |
| 95 | |
| 96 CapsLockMenuButton::~CapsLockMenuButton() { | |
| 97 if (SystemKeyEventListener::GetInstance()) | |
| 98 SystemKeyEventListener::GetInstance()->RemoveCapsLockObserver(this); | |
| 99 } | |
| 100 | |
| 101 //////////////////////////////////////////////////////////////////////////////// | |
| 102 // views::View implementation: | |
| 103 | |
| 104 void CapsLockMenuButton::OnLocaleChanged() { | |
| 105 input_method::InputMethodManager* ime_manager = | |
| 106 input_method::InputMethodManager::GetInstance(); | |
| 107 UpdateUIFromCurrentCapsLock(ime_manager->GetXKeyboard()->CapsLockIsEnabled()); | |
| 108 } | |
| 109 | |
| 110 //////////////////////////////////////////////////////////////////////////////// | |
| 111 // views::MenuDelegate implementation: | |
| 112 | |
| 113 string16 CapsLockMenuButton::GetLabel(int id) const { | |
| 114 return string16(); | |
| 115 } | |
| 116 | |
| 117 //////////////////////////////////////////////////////////////////////////////// | |
| 118 // views::MenuButtonListener implementation: | |
| 119 | |
| 120 void CapsLockMenuButton::OnMenuButtonClicked(views::View* source, | |
| 121 const gfx::Point& point) { | |
| 122 static const int kDummyCommandId = 1000; | |
| 123 | |
| 124 if (IsBubbleShown()) | |
| 125 HideBubble(); | |
| 126 | |
| 127 views::MenuItemView* menu = new views::MenuItemView(this); | |
| 128 // MenuRunner takes ownership of |menu|. | |
| 129 menu_runner_.reset(new views::MenuRunner(menu)); | |
| 130 views::MenuItemView* submenu = menu->AppendMenuItem( | |
| 131 kDummyCommandId, | |
| 132 string16(), | |
| 133 views::MenuItemView::NORMAL); | |
| 134 status_ = new StatusAreaBubbleContentView(CreateImageViewWithCapsLockIcon(), | |
| 135 GetText()); | |
| 136 submenu->AddChildView(status_); | |
| 137 menu->CreateSubmenu()->set_resize_open_menu(true); | |
| 138 menu->SetMargins(0, 0); | |
| 139 submenu->SetMargins(0, 0); | |
| 140 menu->ChildrenChanged(); | |
| 141 | |
| 142 gfx::Point screen_location; | |
| 143 views::View::ConvertPointToScreen(source, &screen_location); | |
| 144 gfx::Rect bounds(screen_location, source->size()); | |
| 145 if (menu_runner_->RunMenuAt( | |
| 146 source->GetWidget()->GetTopLevelWidget(), this, bounds, | |
| 147 views::MenuItemView::TOPRIGHT, views::MenuRunner::HAS_MNEMONICS) == | |
| 148 views::MenuRunner::MENU_DELETED) | |
| 149 return; | |
| 150 status_ = NULL; | |
| 151 menu_runner_.reset(NULL); | |
| 152 } | |
| 153 | |
| 154 //////////////////////////////////////////////////////////////////////////////// | |
| 155 // SystemKeyEventListener::CapsLockObserver implementation | |
| 156 | |
| 157 void CapsLockMenuButton::OnCapsLockChange(bool enabled) { | |
| 158 if (!enabled && !HasCapsLock() && bubble_count_ > 0) { | |
| 159 // Both shift keys are pressed. We can assume that the user now recognizes | |
| 160 // how to turn off Caps Lock. | |
| 161 should_show_bubble_ = false; | |
| 162 } | |
| 163 | |
| 164 // Update the indicator. | |
| 165 UpdateUIFromCurrentCapsLock(enabled); | |
| 166 | |
| 167 // Update the drop-down menu and bubble. Since the constructor also calls | |
| 168 // UpdateUIFromCurrentCapsLock, we shouldn't do this in the function. | |
| 169 if (enabled && IsMenuShown()) { | |
| 170 // Update the drop-down menu if it's already shown. | |
| 171 status_->SetMessage(GetText()); | |
| 172 } else if (!enabled && IsMenuShown()) { | |
| 173 HideMenu(); | |
| 174 } | |
| 175 if (enabled) | |
| 176 MaybeShowBubble(); | |
| 177 else if (!enabled && IsBubbleShown()) | |
| 178 HideBubble(); | |
| 179 } | |
| 180 | |
| 181 //////////////////////////////////////////////////////////////////////////////// | |
| 182 // content::NotificationObserver implementation | |
| 183 | |
| 184 void CapsLockMenuButton::Observe(int type, | |
| 185 const content::NotificationSource& source, | |
| 186 const content::NotificationDetails& details) { | |
| 187 switch (type) { | |
| 188 case chrome::NOTIFICATION_PREF_CHANGED: | |
| 189 UpdateAccessibleName(); | |
| 190 break; | |
| 191 case chrome::NOTIFICATION_SESSION_STARTED: | |
| 192 InitializePrefMember(); | |
| 193 break; | |
| 194 } | |
| 195 } | |
| 196 | |
| 197 void CapsLockMenuButton::UpdateAccessibleName() { | |
| 198 int id = IDS_STATUSBAR_CAPS_LOCK_ENABLED_PRESS_SHIFT_AND_SEARCH_KEYS; | |
| 199 if (HasCapsLock()) | |
| 200 id = IDS_STATUSBAR_CAPS_LOCK_ENABLED_PRESS_SEARCH; | |
| 201 SetAccessibleName(l10n_util::GetStringUTF16(id)); | |
| 202 } | |
| 203 | |
| 204 string16 CapsLockMenuButton::GetText() const { | |
| 205 int id = IDS_STATUSBAR_CAPS_LOCK_ENABLED_PRESS_SHIFT_AND_SEARCH_KEYS; | |
| 206 if (HasCapsLock()) | |
| 207 id = IDS_STATUSBAR_CAPS_LOCK_ENABLED_PRESS_SEARCH; | |
| 208 return l10n_util::GetStringUTF16(id); | |
| 209 } | |
| 210 | |
| 211 void CapsLockMenuButton::UpdateUIFromCurrentCapsLock(bool enabled) { | |
| 212 SetVisible(enabled); | |
| 213 SchedulePaint(); | |
| 214 } | |
| 215 | |
| 216 bool CapsLockMenuButton::IsMenuShown() const { | |
| 217 return menu_runner_.get() && status_; | |
| 218 } | |
| 219 | |
| 220 void CapsLockMenuButton::HideMenu() { | |
| 221 if (!IsMenuShown()) | |
| 222 return; | |
| 223 menu_runner_->Cancel(); | |
| 224 } | |
| 225 | |
| 226 bool CapsLockMenuButton::IsBubbleShown() const { | |
| 227 return bubble_controller_.get() && bubble_controller_->IsBubbleShown(); | |
| 228 } | |
| 229 | |
| 230 void CapsLockMenuButton::MaybeShowBubble() { | |
| 231 if (IsBubbleShown() || | |
| 232 // We've already shown the bubble |kMaxBubbleCount| times. | |
| 233 !should_show_bubble_ || | |
| 234 // Don't show the bubble when Caps Lock key is available. | |
| 235 HasCapsLock() || | |
| 236 // Don't show it when the status area is hidden. | |
| 237 (parent() && !parent()->visible()) || | |
| 238 // Don't show the bubble when screen is locked as this results in two | |
| 239 // visible caps lock bubbles (crbug.com/105280). The greater problem of | |
| 240 // displaying bubbles from all caps lock menu buttons regardless of | |
| 241 // visibility is described in crbug.com/106776. | |
| 242 ScreenLocker::default_screen_locker()) | |
| 243 return; | |
| 244 | |
| 245 ++bubble_count_; | |
| 246 if (bubble_count_ > kMaxBubbleCount) { | |
| 247 should_show_bubble_ = false; | |
| 248 } else { | |
| 249 CreateAndShowBubble(); | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 void CapsLockMenuButton::CreateAndShowBubble() { | |
| 254 if (IsBubbleShown()) { | |
| 255 NOTREACHED(); | |
| 256 return; | |
| 257 } | |
| 258 bubble_controller_.reset( | |
| 259 StatusAreaBubbleController::ShowBubbleUnderViewForAWhile( | |
| 260 this, | |
| 261 new StatusAreaBubbleContentView(CreateImageViewWithCapsLockIcon(), | |
| 262 GetText()))); | |
| 263 } | |
| 264 | |
| 265 void CapsLockMenuButton::HideBubble() { | |
| 266 bubble_controller_.reset(); | |
| 267 } | |
| 268 | |
| 269 bool CapsLockMenuButton::HasCapsLock() const { | |
| 270 // A keyboard for Linux usually has Caps Lock. | |
| 271 if (!base::chromeos::IsRunningOnChromeOS()) | |
| 272 return true; | |
| 273 // On the login screen, Caps Lock is not available. | |
| 274 if (!initialized_prefs_) | |
| 275 return false; | |
| 276 | |
| 277 return remap_search_key_to_.GetValue() == input_method::kCapsLockKey; | |
| 278 } | |
| 279 | |
| 280 void CapsLockMenuButton::InitializePrefMember() { | |
| 281 if (!initialized_prefs_) { | |
| 282 PrefService* prefs = GetPrefService(); | |
| 283 if (prefs) { | |
| 284 initialized_prefs_ = true; | |
| 285 remap_search_key_to_.Init( | |
| 286 prefs::kLanguageXkbRemapSearchKeyTo, prefs, this); | |
| 287 } | |
| 288 } | |
| 289 } | |
| 290 | |
| 291 } // namespace chromeos | |
| OLD | NEW |