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/ui/ash/launcher/shell_window_launcher_item_controller.h" 6 7 #include "apps/shell_window.h" 8 #include "apps/ui/native_app_window.h" 9 #include "ash/shelf/shelf_model.h" 10 #include "ash/wm/window_state.h" 11 #include "ash/wm/window_util.h" 12 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h" 13 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_v2app.h" 14 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h" 15 #include "chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.h" 16 #include "chrome/browser/ui/ash/launcher/launcher_context_menu.h" 17 #include "chrome/browser/ui/ash/launcher/launcher_item_controller.h" 18 #include "content/public/browser/web_contents.h" 19 #include "skia/ext/image_operations.h" 20 #include "ui/aura/client/aura_constants.h" 21 #include "ui/aura/window.h" 22 #include "ui/events/event.h" 23 #include "ui/gfx/image/image_skia.h" 24 #include "ui/views/corewm/window_animations.h" 25 26 using apps::ShellWindow; 27 28 namespace { 29 30 // Size of the icon in the shelf launcher in display-independent pixels. 31 const int kAppListIconSize = 24; 32 33 // This will return a slightly smaller icon than the app icon to be used in 34 // the application list menu. 35 scoped_ptr<gfx::Image> GetAppListIcon(ShellWindow* shell_window) { 36 // TODO(skuhne): We instead might want to use LoadImages in 37 // ShellWindow::UpdateExtensionAppIcon() to let the extension give us 38 // pre-defined icons in the launcher and the launcher list sizes. Since there 39 // is no mock yet, doing this now seems a bit premature and we scale for the 40 // time being. 41 if (shell_window->app_icon().IsEmpty()) 42 return make_scoped_ptr(new gfx::Image()); 43 44 SkBitmap bmp = 45 skia::ImageOperations::Resize(*shell_window->app_icon().ToSkBitmap(), 46 skia::ImageOperations::RESIZE_BEST, 47 kAppListIconSize, 48 kAppListIconSize); 49 return make_scoped_ptr( 50 new gfx::Image(gfx::ImageSkia::CreateFrom1xBitmap(bmp))); 51 } 52 53 // Functor for std::find_if used in AppLauncherItemController. 54 class ShellWindowHasWindow { 55 public: 56 explicit ShellWindowHasWindow(aura::Window* window) : window_(window) { } 57 58 bool operator()(ShellWindow* shell_window) const { 59 return shell_window->GetNativeWindow() == window_; 60 } 61 62 private: 63 const aura::Window* window_; 64 }; 65 66 } // namespace 67 68 ShellWindowLauncherItemController::ShellWindowLauncherItemController( 69 Type type, 70 const std::string& app_launcher_id, 71 const std::string& app_id, 72 ChromeLauncherController* controller) 73 : LauncherItemController(type, app_id, controller), 74 last_active_shell_window_(NULL), 75 app_launcher_id_(app_launcher_id), 76 observed_windows_(this) { 77 } 78 79 ShellWindowLauncherItemController::~ShellWindowLauncherItemController() { 80 } 81 82 void ShellWindowLauncherItemController::AddShellWindow( 83 ShellWindow* shell_window, 84 ash::LauncherItemStatus status) { 85 if (shell_window->window_type_is_panel() && type() != TYPE_APP_PANEL) 86 LOG(ERROR) << "ShellWindow of type Panel added to non-panel launcher item"; 87 shell_windows_.push_front(shell_window); 88 observed_windows_.Add(shell_window->GetNativeWindow()); 89 } 90 91 void ShellWindowLauncherItemController::RemoveShellWindowForWindow( 92 aura::Window* window) { 93 ShellWindowList::iterator iter = 94 std::find_if(shell_windows_.begin(), shell_windows_.end(), 95 ShellWindowHasWindow(window)); 96 if (iter != shell_windows_.end()) { 97 if (*iter == last_active_shell_window_) 98 last_active_shell_window_ = NULL; 99 shell_windows_.erase(iter); 100 } 101 observed_windows_.Remove(window); 102 } 103 104 void ShellWindowLauncherItemController::SetActiveWindow(aura::Window* window) { 105 ShellWindowList::iterator iter = 106 std::find_if(shell_windows_.begin(), shell_windows_.end(), 107 ShellWindowHasWindow(window)); 108 if (iter != shell_windows_.end()) 109 last_active_shell_window_ = *iter; 110 } 111 112 bool ShellWindowLauncherItemController::IsOpen() const { 113 return !shell_windows_.empty(); 114 } 115 116 bool ShellWindowLauncherItemController::IsVisible() const { 117 // Return true if any windows are visible. 118 for (ShellWindowList::const_iterator iter = shell_windows_.begin(); 119 iter != shell_windows_.end(); ++iter) { 120 if ((*iter)->GetNativeWindow()->IsVisible()) 121 return true; 122 } 123 return false; 124 } 125 126 void ShellWindowLauncherItemController::Launch(ash::LaunchSource source, 127 int event_flags) { 128 launcher_controller()->LaunchApp(app_id(), 129 source, 130 ui::EF_NONE); 131 } 132 133 bool ShellWindowLauncherItemController::Activate(ash::LaunchSource source) { 134 DCHECK(!shell_windows_.empty()); 135 ShellWindow* window_to_activate = last_active_shell_window_ ? 136 last_active_shell_window_ : shell_windows_.back(); 137 window_to_activate->GetBaseWindow()->Activate(); 138 return false; 139 } 140 141 void ShellWindowLauncherItemController::Close() { 142 // Note: Closing windows may affect the contents of shell_windows_. 143 ShellWindowList windows_to_close = shell_windows_; 144 for (ShellWindowList::iterator iter = windows_to_close.begin(); 145 iter != windows_to_close.end(); ++iter) { 146 (*iter)->GetBaseWindow()->Close(); 147 } 148 } 149 150 void ShellWindowLauncherItemController::ActivateIndexedApp(size_t index) { 151 if (index >= shell_windows_.size()) 152 return; 153 ShellWindowList::iterator it = shell_windows_.begin(); 154 std::advance(it, index); 155 ShowAndActivateOrMinimize(*it); 156 } 157 158 ChromeLauncherAppMenuItems 159 ShellWindowLauncherItemController::GetApplicationList(int event_flags) { 160 ChromeLauncherAppMenuItems items; 161 items.push_back(new ChromeLauncherAppMenuItem(GetTitle(), NULL, false)); 162 int index = 0; 163 for (ShellWindowList::iterator iter = shell_windows_.begin(); 164 iter != shell_windows_.end(); ++iter) { 165 ShellWindow* shell_window = *iter; 166 scoped_ptr<gfx::Image> image(GetAppListIcon(shell_window)); 167 items.push_back(new ChromeLauncherAppMenuItemV2App( 168 shell_window->GetTitle(), 169 image.get(), // Will be copied 170 app_id(), 171 launcher_controller(), 172 index, 173 index == 0 /* has_leading_separator */)); 174 ++index; 175 } 176 return items.Pass(); 177 } 178 179 bool ShellWindowLauncherItemController::ItemSelected(const ui::Event& event) { 180 if (shell_windows_.empty()) 181 return false; 182 if (type() == TYPE_APP_PANEL) { 183 DCHECK(shell_windows_.size() == 1); 184 ShellWindow* panel = shell_windows_.front(); 185 aura::Window* panel_window = panel->GetNativeWindow(); 186 // If the panel is attached on another display, move it to the current 187 // display and activate it. 188 if (ash::wm::GetWindowState(panel_window)->panel_attached() && 189 ash::wm::MoveWindowToEventRoot(panel_window, event)) { 190 if (!panel->GetBaseWindow()->IsActive()) 191 ShowAndActivateOrMinimize(panel); 192 } else { 193 ShowAndActivateOrMinimize(panel); 194 } 195 } else { 196 ShellWindow* window_to_show = last_active_shell_window_ ? 197 last_active_shell_window_ : shell_windows_.front(); 198 // If the event was triggered by a keystroke, we try to advance to the next 199 // item if the window we are trying to activate is already active. 200 if (shell_windows_.size() >= 1 && 201 window_to_show->GetBaseWindow()->IsActive() && 202 event.type() == ui::ET_KEY_RELEASED) { 203 ActivateOrAdvanceToNextShellWindow(window_to_show); 204 } else { 205 ShowAndActivateOrMinimize(window_to_show); 206 } 207 } 208 return false; 209 } 210 211 base::string16 ShellWindowLauncherItemController::GetTitle() { 212 // For panels return the title of the contents if set. 213 // Otherwise return the title of the app. 214 if (type() == TYPE_APP_PANEL && !shell_windows_.empty()) { 215 ShellWindow* shell_window = shell_windows_.front(); 216 if (shell_window->web_contents()) { 217 base::string16 title = shell_window->web_contents()->GetTitle(); 218 if (!title.empty()) 219 return title; 220 } 221 } 222 return GetAppTitle(); 223 } 224 225 ui::MenuModel* ShellWindowLauncherItemController::CreateContextMenu( 226 aura::Window* root_window) { 227 ash::LauncherItem item = 228 *(launcher_controller()->model()->ItemByID(launcher_id())); 229 return new LauncherContextMenu(launcher_controller(), &item, root_window); 230 } 231 232 ash::ShelfMenuModel* ShellWindowLauncherItemController::CreateApplicationMenu( 233 int event_flags) { 234 return new LauncherApplicationMenuItemModel(GetApplicationList(event_flags)); 235 } 236 237 bool ShellWindowLauncherItemController::IsDraggable() { 238 if (type() == TYPE_APP_PANEL) 239 return true; 240 return launcher_controller()->CanPin() ? true : false; 241 } 242 243 bool ShellWindowLauncherItemController::ShouldShowTooltip() { 244 if (type() == TYPE_APP_PANEL && IsVisible()) 245 return false; 246 return true; 247 } 248 249 void ShellWindowLauncherItemController::OnWindowPropertyChanged( 250 aura::Window* window, 251 const void* key, 252 intptr_t old) { 253 if (key == aura::client::kDrawAttentionKey) { 254 ash::LauncherItemStatus status; 255 if (ash::wm::IsActiveWindow(window)) { 256 status = ash::STATUS_ACTIVE; 257 } else if (window->GetProperty(aura::client::kDrawAttentionKey)) { 258 status = ash::STATUS_ATTENTION; 259 } else { 260 status = ash::STATUS_RUNNING; 261 } 262 launcher_controller()->SetItemStatus(launcher_id(), status); 263 } 264 } 265 266 void ShellWindowLauncherItemController::ShowAndActivateOrMinimize( 267 ShellWindow* shell_window) { 268 // Either show or minimize windows when shown from the launcher. 269 launcher_controller()->ActivateWindowOrMinimizeIfActive( 270 shell_window->GetBaseWindow(), 271 GetApplicationList(0).size() == 2); 272 } 273 274 void ShellWindowLauncherItemController::ActivateOrAdvanceToNextShellWindow( 275 ShellWindow* window_to_show) { 276 ShellWindowList::iterator i( 277 std::find(shell_windows_.begin(), 278 shell_windows_.end(), 279 window_to_show)); 280 if (i != shell_windows_.end()) { 281 if (++i != shell_windows_.end()) 282 window_to_show = *i; 283 else 284 window_to_show = shell_windows_.front(); 285 } 286 if (window_to_show->GetBaseWindow()->IsActive()) { 287 // Coming here, only a single window is active. For keyboard activations 288 // the window gets animated. 289 AnimateWindow(window_to_show->GetNativeWindow(), 290 views::corewm::WINDOW_ANIMATION_TYPE_BOUNCE); 291 } else { 292 ShowAndActivateOrMinimize(window_to_show); 293 } 294 } 295