Home | History | Annotate | Download | only in launcher
      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