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