| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/shelf/shelf_window_watcher.h" |
| |
| #include "ash/display/display_controller.h" |
| #include "ash/shelf/shelf_constants.h" |
| #include "ash/shelf/shelf_item_delegate_manager.h" |
| #include "ash/shelf/shelf_model.h" |
| #include "ash/shelf/shelf_util.h" |
| #include "ash/shelf/shelf_window_watcher_item_delegate.h" |
| #include "ash/shell.h" |
| #include "ash/shell_window_ids.h" |
| #include "ash/wm/window_state.h" |
| #include "ash/wm/window_util.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "ui/aura/window.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/gfx/image/image_skia.h" |
| #include "ui/gfx/screen.h" |
| #include "ui/wm/public/activation_client.h" |
| |
| namespace { |
| |
| // Sets ShelfItem property by using the value of |details|. |
| void SetShelfItemDetailsForShelfItem(ash::ShelfItem* item, |
| const ash::ShelfItemDetails& details) { |
| item->type = details.type; |
| if (details.image_resource_id != ash::kInvalidImageResourceID) { |
| ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
| item->image = *rb.GetImageSkiaNamed(details.image_resource_id); |
| } |
| } |
| |
| // Returns true if |window| has a ShelfItem added by ShelfWindowWatcher. |
| bool HasShelfItemForWindow(aura::Window* window) { |
| if (ash::GetShelfItemDetailsForWindow(window) != NULL && |
| ash::GetShelfIDForWindow(window) != ash::kInvalidShelfID) |
| return true; |
| return false; |
| } |
| |
| // Returns true if |window| is in the process of being dragged. |
| bool IsDragging(aura::Window* window) { |
| return ash::wm::GetWindowState(window)->is_dragged(); |
| } |
| |
| } // namespace |
| |
| namespace ash { |
| |
| ShelfWindowWatcher::RootWindowObserver::RootWindowObserver( |
| ShelfWindowWatcher* window_watcher) |
| : window_watcher_(window_watcher) { |
| } |
| |
| ShelfWindowWatcher::RootWindowObserver::~RootWindowObserver() { |
| } |
| |
| void ShelfWindowWatcher::RootWindowObserver::OnWindowDestroying( |
| aura::Window* window) { |
| window_watcher_->OnRootWindowRemoved(window); |
| } |
| |
| ShelfWindowWatcher::RemovedWindowObserver::RemovedWindowObserver( |
| ShelfWindowWatcher* window_watcher) |
| : window_watcher_(window_watcher) { |
| } |
| |
| ShelfWindowWatcher::RemovedWindowObserver::~RemovedWindowObserver() { |
| } |
| |
| void ShelfWindowWatcher::RemovedWindowObserver::OnWindowParentChanged( |
| aura::Window* window, |
| aura::Window* parent) { |
| // When |parent| is NULL, this |window| will be destroyed. In that case, its |
| // item will be removed at OnWindowDestroyed(). |
| if (!parent) |
| return; |
| |
| // When |parent| is changed from default container to docked container |
| // during the dragging, |window|'s item should not be removed because it will |
| // be re-parented to default container again after finishing the dragging. |
| // We don't need to check |parent| is default container because this observer |
| // is already removed from |window| when |window| is re-parented to default |
| // container. |
| if (IsDragging(window) && parent->id() == kShellWindowId_DockedContainer) |
| return; |
| |
| // When |window| is re-parented to other containers or |window| is re-parented |
| // not to |docked_container| during the dragging, its item should be removed |
| // and stop observing this |window|. |
| window_watcher_->FinishObservingRemovedWindow(window); |
| } |
| |
| void ShelfWindowWatcher::RemovedWindowObserver::OnWindowDestroyed( |
| aura::Window* window) { |
| DCHECK(HasShelfItemForWindow(window)); |
| window_watcher_->FinishObservingRemovedWindow(window); |
| } |
| |
| ShelfWindowWatcher::ShelfWindowWatcher( |
| ShelfModel* model, |
| ShelfItemDelegateManager* item_delegate_manager) |
| : model_(model), |
| item_delegate_manager_(item_delegate_manager), |
| root_window_observer_(this), |
| removed_window_observer_(this), |
| observed_windows_(this), |
| observed_root_windows_(&root_window_observer_), |
| observed_removed_windows_(&removed_window_observer_), |
| observed_activation_clients_(this) { |
| // We can't assume all RootWindows have the same ActivationClient. |
| // Add a RootWindow and its ActivationClient to the observed list. |
| aura::Window::Windows root_windows = Shell::GetAllRootWindows(); |
| for (aura::Window::Windows::const_iterator it = root_windows.begin(); |
| it != root_windows.end(); ++it) |
| OnRootWindowAdded(*it); |
| |
| Shell::GetScreen()->AddObserver(this); |
| } |
| |
| ShelfWindowWatcher::~ShelfWindowWatcher() { |
| Shell::GetScreen()->RemoveObserver(this); |
| } |
| |
| void ShelfWindowWatcher::AddShelfItem(aura::Window* window) { |
| const ShelfItemDetails* item_details = |
| GetShelfItemDetailsForWindow(window); |
| ShelfItem item; |
| ShelfID id = model_->next_id(); |
| item.status = wm::IsActiveWindow(window) ? STATUS_ACTIVE: STATUS_RUNNING; |
| SetShelfItemDetailsForShelfItem(&item, *item_details); |
| SetShelfIDForWindow(id, window); |
| scoped_ptr<ShelfItemDelegate> item_delegate( |
| new ShelfWindowWatcherItemDelegate(window, model_)); |
| // |item_delegate| is owned by |item_delegate_manager_|. |
| item_delegate_manager_->SetShelfItemDelegate(id, item_delegate.Pass()); |
| model_->Add(item); |
| } |
| |
| void ShelfWindowWatcher::RemoveShelfItem(aura::Window* window) { |
| model_->RemoveItemAt(model_->ItemIndexByID(GetShelfIDForWindow(window))); |
| SetShelfIDForWindow(kInvalidShelfID, window); |
| } |
| |
| void ShelfWindowWatcher::OnRootWindowAdded(aura::Window* root_window) { |
| // |observed_activation_clients_| can have the same ActivationClient multiple |
| // times - which would be handled by the |observed_activation_clients_|. |
| observed_activation_clients_.Add( |
| aura::client::GetActivationClient(root_window)); |
| observed_root_windows_.Add(root_window); |
| |
| aura::Window* default_container = Shell::GetContainer( |
| root_window, |
| kShellWindowId_DefaultContainer); |
| observed_windows_.Add(default_container); |
| for (size_t i = 0; i < default_container->children().size(); ++i) |
| observed_windows_.Add(default_container->children()[i]); |
| } |
| |
| void ShelfWindowWatcher::OnRootWindowRemoved(aura::Window* root_window) { |
| observed_root_windows_.Remove(root_window); |
| observed_activation_clients_.Remove( |
| aura::client::GetActivationClient(root_window)); |
| } |
| |
| void ShelfWindowWatcher::UpdateShelfItemStatus(aura::Window* window, |
| bool is_active) { |
| int index = GetShelfItemIndexForWindow(window); |
| DCHECK_GE(index, 0); |
| |
| ShelfItem item = model_->items()[index]; |
| item.status = is_active ? STATUS_ACTIVE : STATUS_RUNNING; |
| model_->Set(index, item); |
| } |
| |
| int ShelfWindowWatcher::GetShelfItemIndexForWindow( |
| aura::Window* window) const { |
| return model_->ItemIndexByID(GetShelfIDForWindow(window)); |
| } |
| |
| void ShelfWindowWatcher::StartObservingRemovedWindow(aura::Window* window) { |
| observed_removed_windows_.Add(window); |
| } |
| |
| void ShelfWindowWatcher::FinishObservingRemovedWindow(aura::Window* window) { |
| observed_removed_windows_.Remove(window); |
| RemoveShelfItem(window); |
| } |
| |
| void ShelfWindowWatcher::OnWindowActivated(aura::Window* gained_active, |
| aura::Window* lost_active) { |
| if (gained_active && HasShelfItemForWindow(gained_active)) |
| UpdateShelfItemStatus(gained_active, true); |
| if (lost_active && HasShelfItemForWindow(lost_active)) |
| UpdateShelfItemStatus(lost_active, false); |
| } |
| |
| void ShelfWindowWatcher::OnWindowAdded(aura::Window* window) { |
| observed_windows_.Add(window); |
| |
| if (observed_removed_windows_.IsObserving(window)) { |
| // When |window| is added and it is already observed by |
| // |dragged_window_observer_|, |window| already has its item. |
| DCHECK(HasShelfItemForWindow(window)); |
| observed_removed_windows_.Remove(window); |
| return; |
| } |
| |
| // Add ShelfItem if |window| already has a ShelfItemDetails when it is |
| // created. Don't make a new ShelfItem for the re-parented |window| that |
| // already has a ShelfItem. |
| if (GetShelfIDForWindow(window) == kInvalidShelfID && |
| GetShelfItemDetailsForWindow(window)) |
| AddShelfItem(window); |
| } |
| |
| void ShelfWindowWatcher::OnWillRemoveWindow(aura::Window* window) { |
| // Remove a child window of default container. |
| if (observed_windows_.IsObserving(window)) |
| observed_windows_.Remove(window); |
| |
| // Don't remove |window| item immediately. Instead, defer handling of removing |
| // |window|'s item to RemovedWindowObserver because |window| could be added |
| // again to default container. |
| if (HasShelfItemForWindow(window)) |
| StartObservingRemovedWindow(window); |
| } |
| |
| void ShelfWindowWatcher::OnWindowDestroying(aura::Window* window) { |
| // Remove the default container. |
| if (observed_windows_.IsObserving(window)) |
| observed_windows_.Remove(window); |
| } |
| |
| void ShelfWindowWatcher::OnWindowPropertyChanged(aura::Window* window, |
| const void* key, |
| intptr_t old) { |
| if (key != kShelfItemDetailsKey) |
| return; |
| |
| if (GetShelfItemDetailsForWindow(window) == NULL) { |
| // Removes ShelfItem for |window| when it has a ShelfItem. |
| if (reinterpret_cast<ShelfItemDetails*>(old) != NULL) |
| RemoveShelfItem(window); |
| return; |
| } |
| |
| // When ShelfItemDetails is changed, update ShelfItem. |
| if (HasShelfItemForWindow(window)) { |
| int index = GetShelfItemIndexForWindow(window); |
| DCHECK_GE(index, 0); |
| ShelfItem item = model_->items()[index]; |
| const ShelfItemDetails* details = |
| GetShelfItemDetailsForWindow(window); |
| SetShelfItemDetailsForShelfItem(&item, *details); |
| model_->Set(index, item); |
| return; |
| } |
| |
| // Creates a new ShelfItem for |window|. |
| AddShelfItem(window); |
| } |
| |
| void ShelfWindowWatcher::OnDisplayAdded(const gfx::Display& new_display) { |
| // Add a new RootWindow and its ActivationClient to observed list. |
| aura::Window* root_window = Shell::GetInstance()->display_controller()-> |
| GetRootWindowForDisplayId(new_display.id()); |
| |
| // When the primary root window's display get removed, the existing root |
| // window is taken over by the new display and the observer is already set. |
| if (!observed_root_windows_.IsObserving(root_window)) |
| OnRootWindowAdded(root_window); |
| } |
| |
| void ShelfWindowWatcher::OnDisplayRemoved(const gfx::Display& old_display) { |
| // When this is called, RootWindow of |old_display| is already removed. |
| // Instead, we remove an observer from RootWindow and ActivationClient in the |
| // OnRootWindowDestroyed(). |
| // Do nothing here. |
| } |
| |
| void ShelfWindowWatcher::OnDisplayMetricsChanged(const gfx::Display&, |
| uint32_t) { |
| } |
| |
| } // namespace ash |