Home | History | Annotate | Download | only in shelf
      1 // Copyright 2013 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 "ash/shelf/shelf_window_watcher.h"
      6 
      7 #include "ash/display/display_controller.h"
      8 #include "ash/shelf/shelf_constants.h"
      9 #include "ash/shelf/shelf_item_delegate_manager.h"
     10 #include "ash/shelf/shelf_model.h"
     11 #include "ash/shelf/shelf_util.h"
     12 #include "ash/shelf/shelf_window_watcher_item_delegate.h"
     13 #include "ash/shell.h"
     14 #include "ash/shell_window_ids.h"
     15 #include "ash/wm/window_state.h"
     16 #include "ash/wm/window_util.h"
     17 #include "base/memory/scoped_ptr.h"
     18 #include "ui/aura/window.h"
     19 #include "ui/base/resource/resource_bundle.h"
     20 #include "ui/gfx/image/image_skia.h"
     21 #include "ui/gfx/screen.h"
     22 #include "ui/wm/public/activation_client.h"
     23 
     24 namespace {
     25 
     26 // Sets ShelfItem property by using the value of |details|.
     27 void SetShelfItemDetailsForShelfItem(ash::ShelfItem* item,
     28                                      const ash::ShelfItemDetails& details) {
     29   item->type = details.type;
     30   if (details.image_resource_id != ash::kInvalidImageResourceID) {
     31     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
     32     item->image = *rb.GetImageSkiaNamed(details.image_resource_id);
     33   }
     34 }
     35 
     36 // Returns true if |window| has a ShelfItem added by ShelfWindowWatcher.
     37 bool HasShelfItemForWindow(aura::Window* window) {
     38   if (ash::GetShelfItemDetailsForWindow(window) != NULL &&
     39       ash::GetShelfIDForWindow(window) != ash::kInvalidShelfID)
     40     return true;
     41   return false;
     42 }
     43 
     44 // Returns true if |window| is in the process of being dragged.
     45 bool IsDragging(aura::Window* window) {
     46   return ash::wm::GetWindowState(window)->is_dragged();
     47 }
     48 
     49 }  // namespace
     50 
     51 namespace ash {
     52 
     53 ShelfWindowWatcher::RootWindowObserver::RootWindowObserver(
     54     ShelfWindowWatcher* window_watcher)
     55     : window_watcher_(window_watcher) {
     56 }
     57 
     58 ShelfWindowWatcher::RootWindowObserver::~RootWindowObserver() {
     59 }
     60 
     61 void ShelfWindowWatcher::RootWindowObserver::OnWindowDestroying(
     62     aura::Window* window) {
     63   window_watcher_->OnRootWindowRemoved(window);
     64 }
     65 
     66 ShelfWindowWatcher::RemovedWindowObserver::RemovedWindowObserver(
     67     ShelfWindowWatcher* window_watcher)
     68     : window_watcher_(window_watcher) {
     69 }
     70 
     71 ShelfWindowWatcher::RemovedWindowObserver::~RemovedWindowObserver() {
     72 }
     73 
     74 void ShelfWindowWatcher::RemovedWindowObserver::OnWindowParentChanged(
     75     aura::Window* window,
     76     aura::Window* parent) {
     77   // When |parent| is NULL, this |window| will be destroyed. In that case, its
     78   // item will be removed at OnWindowDestroyed().
     79   if (!parent)
     80     return;
     81 
     82   // When |parent| is changed from default container to docked container
     83   // during the dragging, |window|'s item should not be removed because it will
     84   // be re-parented to default container again after finishing the dragging.
     85   // We don't need to check |parent| is default container because this observer
     86   // is already removed from |window| when |window| is re-parented to default
     87   // container.
     88   if (IsDragging(window) && parent->id() == kShellWindowId_DockedContainer)
     89     return;
     90 
     91   // When |window| is re-parented to other containers or |window| is re-parented
     92   // not to |docked_container| during the dragging, its item should be removed
     93   // and stop observing this |window|.
     94   window_watcher_->FinishObservingRemovedWindow(window);
     95 }
     96 
     97 void ShelfWindowWatcher::RemovedWindowObserver::OnWindowDestroyed(
     98     aura::Window* window) {
     99   DCHECK(HasShelfItemForWindow(window));
    100   window_watcher_->FinishObservingRemovedWindow(window);
    101 }
    102 
    103 ShelfWindowWatcher::ShelfWindowWatcher(
    104     ShelfModel* model,
    105     ShelfItemDelegateManager* item_delegate_manager)
    106     : model_(model),
    107       item_delegate_manager_(item_delegate_manager),
    108       root_window_observer_(this),
    109       removed_window_observer_(this),
    110       observed_windows_(this),
    111       observed_root_windows_(&root_window_observer_),
    112       observed_removed_windows_(&removed_window_observer_),
    113       observed_activation_clients_(this) {
    114   // We can't assume all RootWindows have the same ActivationClient.
    115   // Add a RootWindow and its ActivationClient to the observed list.
    116   aura::Window::Windows root_windows = Shell::GetAllRootWindows();
    117   for (aura::Window::Windows::const_iterator it = root_windows.begin();
    118        it != root_windows.end(); ++it)
    119     OnRootWindowAdded(*it);
    120 
    121   Shell::GetScreen()->AddObserver(this);
    122 }
    123 
    124 ShelfWindowWatcher::~ShelfWindowWatcher() {
    125   Shell::GetScreen()->RemoveObserver(this);
    126 }
    127 
    128 void ShelfWindowWatcher::AddShelfItem(aura::Window* window) {
    129   const ShelfItemDetails* item_details =
    130       GetShelfItemDetailsForWindow(window);
    131   ShelfItem item;
    132   ShelfID id = model_->next_id();
    133   item.status = wm::IsActiveWindow(window) ? STATUS_ACTIVE: STATUS_RUNNING;
    134   SetShelfItemDetailsForShelfItem(&item, *item_details);
    135   SetShelfIDForWindow(id, window);
    136   scoped_ptr<ShelfItemDelegate> item_delegate(
    137       new ShelfWindowWatcherItemDelegate(window, model_));
    138   // |item_delegate| is owned by |item_delegate_manager_|.
    139   item_delegate_manager_->SetShelfItemDelegate(id, item_delegate.Pass());
    140   model_->Add(item);
    141 }
    142 
    143 void ShelfWindowWatcher::RemoveShelfItem(aura::Window* window) {
    144   model_->RemoveItemAt(model_->ItemIndexByID(GetShelfIDForWindow(window)));
    145   SetShelfIDForWindow(kInvalidShelfID, window);
    146 }
    147 
    148 void ShelfWindowWatcher::OnRootWindowAdded(aura::Window* root_window) {
    149   // |observed_activation_clients_| can have the same ActivationClient multiple
    150   // times - which would be handled by the |observed_activation_clients_|.
    151   observed_activation_clients_.Add(
    152       aura::client::GetActivationClient(root_window));
    153   observed_root_windows_.Add(root_window);
    154 
    155   aura::Window* default_container = Shell::GetContainer(
    156       root_window,
    157       kShellWindowId_DefaultContainer);
    158   observed_windows_.Add(default_container);
    159   for (size_t i = 0; i < default_container->children().size(); ++i)
    160     observed_windows_.Add(default_container->children()[i]);
    161 }
    162 
    163 void ShelfWindowWatcher::OnRootWindowRemoved(aura::Window* root_window) {
    164   observed_root_windows_.Remove(root_window);
    165   observed_activation_clients_.Remove(
    166       aura::client::GetActivationClient(root_window));
    167 }
    168 
    169 void ShelfWindowWatcher::UpdateShelfItemStatus(aura::Window* window,
    170                                                bool is_active) {
    171   int index = GetShelfItemIndexForWindow(window);
    172   DCHECK_GE(index, 0);
    173 
    174   ShelfItem item = model_->items()[index];
    175   item.status = is_active ? STATUS_ACTIVE : STATUS_RUNNING;
    176   model_->Set(index, item);
    177 }
    178 
    179 int ShelfWindowWatcher::GetShelfItemIndexForWindow(
    180     aura::Window* window) const {
    181   return model_->ItemIndexByID(GetShelfIDForWindow(window));
    182 }
    183 
    184 void ShelfWindowWatcher::StartObservingRemovedWindow(aura::Window* window) {
    185   observed_removed_windows_.Add(window);
    186 }
    187 
    188 void ShelfWindowWatcher::FinishObservingRemovedWindow(aura::Window* window) {
    189   observed_removed_windows_.Remove(window);
    190   RemoveShelfItem(window);
    191 }
    192 
    193 void ShelfWindowWatcher::OnWindowActivated(aura::Window* gained_active,
    194                                            aura::Window* lost_active) {
    195   if (gained_active && HasShelfItemForWindow(gained_active))
    196     UpdateShelfItemStatus(gained_active, true);
    197   if (lost_active && HasShelfItemForWindow(lost_active))
    198     UpdateShelfItemStatus(lost_active, false);
    199 }
    200 
    201 void ShelfWindowWatcher::OnWindowAdded(aura::Window* window) {
    202   observed_windows_.Add(window);
    203 
    204   if (observed_removed_windows_.IsObserving(window)) {
    205     // When |window| is added and it is already observed by
    206     // |dragged_window_observer_|, |window| already has its item.
    207     DCHECK(HasShelfItemForWindow(window));
    208     observed_removed_windows_.Remove(window);
    209     return;
    210   }
    211 
    212   // Add ShelfItem if |window| already has a ShelfItemDetails when it is
    213   // created. Don't make a new ShelfItem for the re-parented |window| that
    214   // already has a ShelfItem.
    215   if (GetShelfIDForWindow(window) == kInvalidShelfID &&
    216       GetShelfItemDetailsForWindow(window))
    217     AddShelfItem(window);
    218 }
    219 
    220 void ShelfWindowWatcher::OnWillRemoveWindow(aura::Window* window) {
    221   // Remove a child window of default container.
    222   if (observed_windows_.IsObserving(window))
    223     observed_windows_.Remove(window);
    224 
    225   // Don't remove |window| item immediately. Instead, defer handling of removing
    226   // |window|'s item to RemovedWindowObserver because |window| could be added
    227   // again to default container.
    228   if (HasShelfItemForWindow(window))
    229     StartObservingRemovedWindow(window);
    230 }
    231 
    232 void ShelfWindowWatcher::OnWindowDestroying(aura::Window* window) {
    233   // Remove the default container.
    234   if (observed_windows_.IsObserving(window))
    235     observed_windows_.Remove(window);
    236 }
    237 
    238 void ShelfWindowWatcher::OnWindowPropertyChanged(aura::Window* window,
    239                                                  const void* key,
    240                                                  intptr_t old) {
    241   if (key != kShelfItemDetailsKey)
    242     return;
    243 
    244   if (GetShelfItemDetailsForWindow(window) == NULL) {
    245     // Removes ShelfItem for |window| when it has a ShelfItem.
    246     if (reinterpret_cast<ShelfItemDetails*>(old) != NULL)
    247       RemoveShelfItem(window);
    248     return;
    249   }
    250 
    251   // When ShelfItemDetails is changed, update ShelfItem.
    252   if (HasShelfItemForWindow(window)) {
    253     int index = GetShelfItemIndexForWindow(window);
    254     DCHECK_GE(index, 0);
    255     ShelfItem item = model_->items()[index];
    256     const ShelfItemDetails* details =
    257         GetShelfItemDetailsForWindow(window);
    258     SetShelfItemDetailsForShelfItem(&item, *details);
    259     model_->Set(index, item);
    260     return;
    261   }
    262 
    263   // Creates a new ShelfItem for |window|.
    264   AddShelfItem(window);
    265 }
    266 
    267 void ShelfWindowWatcher::OnDisplayAdded(const gfx::Display& new_display) {
    268   // Add a new RootWindow and its ActivationClient to observed list.
    269   aura::Window* root_window = Shell::GetInstance()->display_controller()->
    270       GetRootWindowForDisplayId(new_display.id());
    271 
    272   // When the primary root window's display get removed, the existing root
    273   // window is taken over by the new display and the observer is already set.
    274   if (!observed_root_windows_.IsObserving(root_window))
    275     OnRootWindowAdded(root_window);
    276 }
    277 
    278 void ShelfWindowWatcher::OnDisplayRemoved(const gfx::Display& old_display) {
    279   // When this is called, RootWindow of |old_display| is already removed.
    280   // Instead, we remove an observer from RootWindow and ActivationClient in the
    281   // OnRootWindowDestroyed().
    282   // Do nothing here.
    283 }
    284 
    285 void ShelfWindowWatcher::OnDisplayMetricsChanged(const gfx::Display&,
    286                                                  uint32_t) {
    287 }
    288 
    289 }  // namespace ash
    290