1 // Copyright 2014 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.
5 #include "chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h"
7 #include "apps/app_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/wm/core/window_animations.h"
26 using apps::AppWindow
;
30 // Size of the icon in the shelf launcher in display-independent pixels.
31 const int kAppListIconSize
= 24;
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(AppWindow
* app_window
) {
36 // TODO(skuhne): We instead might want to use LoadImages in
37 // AppWindow::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
41 if (app_window
->app_icon().IsEmpty())
42 return make_scoped_ptr(new gfx::Image());
45 skia::ImageOperations::Resize(*app_window
->app_icon().ToSkBitmap(),
46 skia::ImageOperations::RESIZE_BEST
,
49 return make_scoped_ptr(
50 new gfx::Image(gfx::ImageSkia::CreateFrom1xBitmap(bmp
)));
53 // Functor for std::find_if used in AppLauncherItemController.
54 class AppWindowHasWindow
{
56 explicit AppWindowHasWindow(aura::Window
* window
) : window_(window
) {}
58 bool operator()(AppWindow
* app_window
) const {
59 return app_window
->GetNativeWindow() == window_
;
63 const aura::Window
* window_
;
68 AppWindowLauncherItemController::AppWindowLauncherItemController(
70 const std::string
& app_shelf_id
,
71 const std::string
& app_id
,
72 ChromeLauncherController
* controller
)
73 : LauncherItemController(type
, app_id
, controller
),
74 last_active_app_window_(NULL
),
75 app_shelf_id_(app_shelf_id
),
76 observed_windows_(this) {}
78 AppWindowLauncherItemController::~AppWindowLauncherItemController() {}
80 void AppWindowLauncherItemController::AddAppWindow(
81 AppWindow
* app_window
,
82 ash::ShelfItemStatus status
) {
83 if (app_window
->window_type_is_panel() && type() != TYPE_APP_PANEL
)
84 LOG(ERROR
) << "AppWindow of type Panel added to non-panel launcher item";
85 app_windows_
.push_front(app_window
);
86 observed_windows_
.Add(app_window
->GetNativeWindow());
89 void AppWindowLauncherItemController::RemoveAppWindowForWindow(
90 aura::Window
* window
) {
91 AppWindowList::iterator iter
= std::find_if(
92 app_windows_
.begin(), app_windows_
.end(), AppWindowHasWindow(window
));
93 if (iter
!= app_windows_
.end()) {
94 if (*iter
== last_active_app_window_
)
95 last_active_app_window_
= NULL
;
96 app_windows_
.erase(iter
);
98 observed_windows_
.Remove(window
);
101 void AppWindowLauncherItemController::SetActiveWindow(aura::Window
* window
) {
102 AppWindowList::iterator iter
= std::find_if(
103 app_windows_
.begin(), app_windows_
.end(), AppWindowHasWindow(window
));
104 if (iter
!= app_windows_
.end())
105 last_active_app_window_
= *iter
;
108 bool AppWindowLauncherItemController::IsOpen() const {
109 return !app_windows_
.empty();
112 bool AppWindowLauncherItemController::IsVisible() const {
113 // Return true if any windows are visible.
114 for (AppWindowList::const_iterator iter
= app_windows_
.begin();
115 iter
!= app_windows_
.end();
117 if ((*iter
)->GetNativeWindow()->IsVisible())
123 void AppWindowLauncherItemController::Launch(ash::LaunchSource source
,
125 launcher_controller()->LaunchApp(app_id(), source
, ui::EF_NONE
);
128 bool AppWindowLauncherItemController::Activate(ash::LaunchSource source
) {
129 DCHECK(!app_windows_
.empty());
130 AppWindow
* window_to_activate
=
131 last_active_app_window_
? last_active_app_window_
: app_windows_
.back();
132 window_to_activate
->GetBaseWindow()->Activate();
136 void AppWindowLauncherItemController::Close() {
137 // Note: Closing windows may affect the contents of app_windows_.
138 AppWindowList windows_to_close
= app_windows_
;
139 for (AppWindowList::iterator iter
= windows_to_close
.begin();
140 iter
!= windows_to_close
.end();
142 (*iter
)->GetBaseWindow()->Close();
146 void AppWindowLauncherItemController::ActivateIndexedApp(size_t index
) {
147 if (index
>= app_windows_
.size())
149 AppWindowList::iterator it
= app_windows_
.begin();
150 std::advance(it
, index
);
151 ShowAndActivateOrMinimize(*it
);
154 ChromeLauncherAppMenuItems
AppWindowLauncherItemController::GetApplicationList(
156 ChromeLauncherAppMenuItems items
;
157 items
.push_back(new ChromeLauncherAppMenuItem(GetTitle(), NULL
, false));
159 for (AppWindowList::iterator iter
= app_windows_
.begin();
160 iter
!= app_windows_
.end();
162 AppWindow
* app_window
= *iter
;
163 scoped_ptr
<gfx::Image
> image(GetAppListIcon(app_window
));
164 items
.push_back(new ChromeLauncherAppMenuItemV2App(
165 app_window
->GetTitle(),
166 image
.get(), // Will be copied
168 launcher_controller(),
170 index
== 0 /* has_leading_separator */));
176 bool AppWindowLauncherItemController::ItemSelected(const ui::Event
& event
) {
177 if (app_windows_
.empty())
179 if (type() == TYPE_APP_PANEL
) {
180 DCHECK(app_windows_
.size() == 1);
181 AppWindow
* panel
= app_windows_
.front();
182 aura::Window
* panel_window
= panel
->GetNativeWindow();
183 // If the panel is attached on another display, move it to the current
184 // display and activate it.
185 if (ash::wm::GetWindowState(panel_window
)->panel_attached() &&
186 ash::wm::MoveWindowToEventRoot(panel_window
, event
)) {
187 if (!panel
->GetBaseWindow()->IsActive())
188 ShowAndActivateOrMinimize(panel
);
190 ShowAndActivateOrMinimize(panel
);
193 AppWindow
* window_to_show
= last_active_app_window_
194 ? last_active_app_window_
195 : app_windows_
.front();
196 // If the event was triggered by a keystroke, we try to advance to the next
197 // item if the window we are trying to activate is already active.
198 if (app_windows_
.size() >= 1 &&
199 window_to_show
->GetBaseWindow()->IsActive() &&
200 event
.type() == ui::ET_KEY_RELEASED
) {
201 ActivateOrAdvanceToNextAppWindow(window_to_show
);
203 ShowAndActivateOrMinimize(window_to_show
);
209 base::string16
AppWindowLauncherItemController::GetTitle() {
210 // For panels return the title of the contents if set.
211 // Otherwise return the title of the app.
212 if (type() == TYPE_APP_PANEL
&& !app_windows_
.empty()) {
213 AppWindow
* app_window
= app_windows_
.front();
214 if (app_window
->web_contents()) {
215 base::string16 title
= app_window
->web_contents()->GetTitle();
220 return GetAppTitle();
223 ui::MenuModel
* AppWindowLauncherItemController::CreateContextMenu(
224 aura::Window
* root_window
) {
225 ash::ShelfItem item
= *(launcher_controller()->model()->ItemByID(shelf_id()));
226 return new LauncherContextMenu(launcher_controller(), &item
, root_window
);
229 ash::ShelfMenuModel
* AppWindowLauncherItemController::CreateApplicationMenu(
231 return new LauncherApplicationMenuItemModel(GetApplicationList(event_flags
));
234 bool AppWindowLauncherItemController::IsDraggable() {
235 if (type() == TYPE_APP_PANEL
)
237 return launcher_controller()->CanPin() ? true : false;
240 bool AppWindowLauncherItemController::ShouldShowTooltip() {
241 if (type() == TYPE_APP_PANEL
&& IsVisible())
246 void AppWindowLauncherItemController::OnWindowPropertyChanged(
247 aura::Window
* window
,
250 if (key
== aura::client::kDrawAttentionKey
) {
251 ash::ShelfItemStatus status
;
252 if (ash::wm::IsActiveWindow(window
)) {
253 status
= ash::STATUS_ACTIVE
;
254 } else if (window
->GetProperty(aura::client::kDrawAttentionKey
)) {
255 status
= ash::STATUS_ATTENTION
;
257 status
= ash::STATUS_RUNNING
;
259 launcher_controller()->SetItemStatus(shelf_id(), status
);
263 void AppWindowLauncherItemController::ShowAndActivateOrMinimize(
264 AppWindow
* app_window
) {
265 // Either show or minimize windows when shown from the launcher.
266 launcher_controller()->ActivateWindowOrMinimizeIfActive(
267 app_window
->GetBaseWindow(), GetApplicationList(0).size() == 2);
270 void AppWindowLauncherItemController::ActivateOrAdvanceToNextAppWindow(
271 AppWindow
* window_to_show
) {
272 AppWindowList::iterator
i(
273 std::find(app_windows_
.begin(), app_windows_
.end(), window_to_show
));
274 if (i
!= app_windows_
.end()) {
275 if (++i
!= app_windows_
.end())
278 window_to_show
= app_windows_
.front();
280 if (window_to_show
->GetBaseWindow()->IsActive()) {
281 // Coming here, only a single window is active. For keyboard activations
282 // the window gets animated.
283 AnimateWindow(window_to_show
->GetNativeWindow(),
284 wm::WINDOW_ANIMATION_TYPE_BOUNCE
);
286 ShowAndActivateOrMinimize(window_to_show
);