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.
5 #include "ash/shelf/shelf_window_watcher.h"
7 #include "ash/ash_switches.h"
8 #include "ash/display/display_controller.h"
9 #include "ash/shelf/shelf_constants.h"
10 #include "ash/shelf/shelf_item_delegate_manager.h"
11 #include "ash/shelf/shelf_model.h"
12 #include "ash/shelf/shelf_util.h"
13 #include "ash/shelf/shelf_window_watcher_item_delegate.h"
14 #include "ash/shell.h"
15 #include "ash/shell_window_ids.h"
16 #include "ash/wm/window_state.h"
17 #include "ash/wm/window_util.h"
18 #include "base/memory/scoped_ptr.h"
19 #include "ui/aura/window.h"
20 #include "ui/base/resource/resource_bundle.h"
21 #include "ui/gfx/image/image_skia.h"
22 #include "ui/gfx/screen.h"
23 #include "ui/wm/public/activation_client.h"
27 // Sets ShelfItem property by using the value of |details|.
28 void SetShelfItemDetailsForShelfItem(ash::ShelfItem
* item
,
29 const ash::ShelfItemDetails
& details
) {
30 item
->type
= details
.type
;
31 if (details
.image_resource_id
!= ash::kInvalidImageResourceID
) {
32 ResourceBundle
& rb
= ResourceBundle::GetSharedInstance();
33 item
->image
= *rb
.GetImageSkiaNamed(details
.image_resource_id
);
37 // Returns true if |window| has a ShelfItem added by ShelfWindowWatcher.
38 bool HasShelfItemForWindow(aura::Window
* window
) {
39 if (ash::GetShelfItemDetailsForWindow(window
) != NULL
&&
40 ash::GetShelfIDForWindow(window
) != ash::kInvalidShelfID
)
45 // Returns true if |window| is in the process of being dragged.
46 bool IsDragging(aura::Window
* window
) {
47 return ash::wm::GetWindowState(window
)->is_dragged();
54 ShelfWindowWatcher::RootWindowObserver::RootWindowObserver(
55 ShelfWindowWatcher
* window_watcher
)
56 : window_watcher_(window_watcher
) {
59 ShelfWindowWatcher::RootWindowObserver::~RootWindowObserver() {
62 void ShelfWindowWatcher::RootWindowObserver::OnWindowDestroying(
63 aura::Window
* window
) {
64 window_watcher_
->OnRootWindowRemoved(window
);
67 ShelfWindowWatcher::RemovedWindowObserver::RemovedWindowObserver(
68 ShelfWindowWatcher
* window_watcher
)
69 : window_watcher_(window_watcher
) {
72 ShelfWindowWatcher::RemovedWindowObserver::~RemovedWindowObserver() {
75 void ShelfWindowWatcher::RemovedWindowObserver::OnWindowParentChanged(
77 aura::Window
* parent
) {
78 // When |parent| is NULL, this |window| will be destroyed. In that case, its
79 // item will be removed at OnWindowDestroyed().
83 // When |parent| is changed from default container to docked container
84 // during the dragging, |window|'s item should not be removed because it will
85 // be re-parented to default container again after finishing the dragging.
86 // We don't need to check |parent| is default container because this observer
87 // is already removed from |window| when |window| is re-parented to default
89 if (switches::UseDockedWindows() &&
91 parent
->id() == kShellWindowId_DockedContainer
)
94 // When |window| is re-parented to other containers or |window| is re-parented
95 // not to |docked_container| during the dragging, its item should be removed
96 // and stop observing this |window|.
97 window_watcher_
->FinishObservingRemovedWindow(window
);
100 void ShelfWindowWatcher::RemovedWindowObserver::OnWindowDestroyed(
101 aura::Window
* window
) {
102 DCHECK(HasShelfItemForWindow(window
));
103 window_watcher_
->FinishObservingRemovedWindow(window
);
106 ShelfWindowWatcher::ShelfWindowWatcher(
108 ShelfItemDelegateManager
* item_delegate_manager
)
110 item_delegate_manager_(item_delegate_manager
),
111 root_window_observer_(this),
112 removed_window_observer_(this),
113 observed_windows_(this),
114 observed_root_windows_(&root_window_observer_
),
115 observed_removed_windows_(&removed_window_observer_
),
116 observed_activation_clients_(this) {
117 // We can't assume all RootWindows have the same ActivationClient.
118 // Add a RootWindow and its ActivationClient to the observed list.
119 aura::Window::Windows root_windows
= Shell::GetAllRootWindows();
120 for (aura::Window::Windows::const_iterator it
= root_windows
.begin();
121 it
!= root_windows
.end(); ++it
)
122 OnRootWindowAdded(*it
);
124 Shell::GetScreen()->AddObserver(this);
127 ShelfWindowWatcher::~ShelfWindowWatcher() {
128 Shell::GetScreen()->RemoveObserver(this);
131 void ShelfWindowWatcher::AddShelfItem(aura::Window
* window
) {
132 const ShelfItemDetails
* item_details
=
133 GetShelfItemDetailsForWindow(window
);
135 ShelfID id
= model_
->next_id();
136 item
.status
= wm::IsActiveWindow(window
) ? STATUS_ACTIVE
: STATUS_RUNNING
;
137 SetShelfItemDetailsForShelfItem(&item
, *item_details
);
138 SetShelfIDForWindow(id
, window
);
139 scoped_ptr
<ShelfItemDelegate
> item_delegate(
140 new ShelfWindowWatcherItemDelegate(window
, model_
));
141 // |item_delegate| is owned by |item_delegate_manager_|.
142 item_delegate_manager_
->SetShelfItemDelegate(id
, item_delegate
.Pass());
146 void ShelfWindowWatcher::RemoveShelfItem(aura::Window
* window
) {
147 model_
->RemoveItemAt(model_
->ItemIndexByID(GetShelfIDForWindow(window
)));
148 SetShelfIDForWindow(kInvalidShelfID
, window
);
151 void ShelfWindowWatcher::OnRootWindowAdded(aura::Window
* root_window
) {
152 // |observed_activation_clients_| can have the same ActivationClient multiple
153 // times - which would be handled by the |observed_activation_clients_|.
154 observed_activation_clients_
.Add(
155 aura::client::GetActivationClient(root_window
));
156 observed_root_windows_
.Add(root_window
);
158 aura::Window
* default_container
= Shell::GetContainer(
160 kShellWindowId_DefaultContainer
);
161 observed_windows_
.Add(default_container
);
162 for (size_t i
= 0; i
< default_container
->children().size(); ++i
)
163 observed_windows_
.Add(default_container
->children()[i
]);
166 void ShelfWindowWatcher::OnRootWindowRemoved(aura::Window
* root_window
) {
167 observed_root_windows_
.Remove(root_window
);
168 observed_activation_clients_
.Remove(
169 aura::client::GetActivationClient(root_window
));
172 void ShelfWindowWatcher::UpdateShelfItemStatus(aura::Window
* window
,
174 int index
= GetShelfItemIndexForWindow(window
);
177 ShelfItem item
= model_
->items()[index
];
178 item
.status
= is_active
? STATUS_ACTIVE
: STATUS_RUNNING
;
179 model_
->Set(index
, item
);
182 int ShelfWindowWatcher::GetShelfItemIndexForWindow(
183 aura::Window
* window
) const {
184 return model_
->ItemIndexByID(GetShelfIDForWindow(window
));
187 void ShelfWindowWatcher::StartObservingRemovedWindow(aura::Window
* window
) {
188 observed_removed_windows_
.Add(window
);
191 void ShelfWindowWatcher::FinishObservingRemovedWindow(aura::Window
* window
) {
192 observed_removed_windows_
.Remove(window
);
193 RemoveShelfItem(window
);
196 void ShelfWindowWatcher::OnWindowActivated(aura::Window
* gained_active
,
197 aura::Window
* lost_active
) {
198 if (gained_active
&& HasShelfItemForWindow(gained_active
))
199 UpdateShelfItemStatus(gained_active
, true);
200 if (lost_active
&& HasShelfItemForWindow(lost_active
))
201 UpdateShelfItemStatus(lost_active
, false);
204 void ShelfWindowWatcher::OnWindowAdded(aura::Window
* window
) {
205 observed_windows_
.Add(window
);
207 if (observed_removed_windows_
.IsObserving(window
)) {
208 // When |window| is added and it is already observed by
209 // |dragged_window_observer_|, |window| already has its item.
210 DCHECK(HasShelfItemForWindow(window
));
211 observed_removed_windows_
.Remove(window
);
215 // Add ShelfItem if |window| already has a ShelfItemDetails when it is
216 // created. Don't make a new ShelfItem for the re-parented |window| that
217 // already has a ShelfItem.
218 if (GetShelfIDForWindow(window
) == kInvalidShelfID
&&
219 GetShelfItemDetailsForWindow(window
))
220 AddShelfItem(window
);
223 void ShelfWindowWatcher::OnWillRemoveWindow(aura::Window
* window
) {
224 // Remove a child window of default container.
225 if (observed_windows_
.IsObserving(window
))
226 observed_windows_
.Remove(window
);
228 // Don't remove |window| item immediately. Instead, defer handling of removing
229 // |window|'s item to RemovedWindowObserver because |window| could be added
230 // again to default container.
231 if (HasShelfItemForWindow(window
))
232 StartObservingRemovedWindow(window
);
235 void ShelfWindowWatcher::OnWindowDestroying(aura::Window
* window
) {
236 // Remove the default container.
237 if (observed_windows_
.IsObserving(window
))
238 observed_windows_
.Remove(window
);
241 void ShelfWindowWatcher::OnWindowPropertyChanged(aura::Window
* window
,
244 if (key
!= kShelfItemDetailsKey
)
247 if (GetShelfItemDetailsForWindow(window
) == NULL
) {
248 // Removes ShelfItem for |window| when it has a ShelfItem.
249 if (reinterpret_cast<ShelfItemDetails
*>(old
) != NULL
)
250 RemoveShelfItem(window
);
254 // When ShelfItemDetails is changed, update ShelfItem.
255 if (HasShelfItemForWindow(window
)) {
256 int index
= GetShelfItemIndexForWindow(window
);
258 ShelfItem item
= model_
->items()[index
];
259 const ShelfItemDetails
* details
=
260 GetShelfItemDetailsForWindow(window
);
261 SetShelfItemDetailsForShelfItem(&item
, *details
);
262 model_
->Set(index
, item
);
266 // Creates a new ShelfItem for |window|.
267 AddShelfItem(window
);
270 void ShelfWindowWatcher::OnDisplayBoundsChanged(const gfx::Display
& display
) {
273 void ShelfWindowWatcher::OnDisplayAdded(const gfx::Display
& new_display
) {
274 // Add a new RootWindow and its ActivationClient to observed list.
275 aura::Window
* root_window
= Shell::GetInstance()->display_controller()->
276 GetRootWindowForDisplayId(new_display
.id());
278 // When the primary root window's display get removed, the existing root
279 // window is taken over by the new display and the observer is already set.
280 if (!observed_root_windows_
.IsObserving(root_window
))
281 OnRootWindowAdded(root_window
);
284 void ShelfWindowWatcher::OnDisplayRemoved(const gfx::Display
& old_display
) {
285 // When this is called, RootWindow of |old_display| is already removed.
286 // Instead, we remove an observer from RootWindow and ActivationClient in the
287 // OnRootWindowDestroyed().