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/display/window_tree_host_manager.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"
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
);
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
)
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();
53 ShelfWindowWatcher::RootWindowObserver::RootWindowObserver(
54 ShelfWindowWatcher
* window_watcher
)
55 : window_watcher_(window_watcher
) {
58 ShelfWindowWatcher::RootWindowObserver::~RootWindowObserver() {
61 void ShelfWindowWatcher::RootWindowObserver::OnWindowDestroying(
62 aura::Window
* window
) {
63 window_watcher_
->OnRootWindowRemoved(window
);
66 ShelfWindowWatcher::RemovedWindowObserver::RemovedWindowObserver(
67 ShelfWindowWatcher
* window_watcher
)
68 : window_watcher_(window_watcher
) {
71 ShelfWindowWatcher::RemovedWindowObserver::~RemovedWindowObserver() {
74 void ShelfWindowWatcher::RemovedWindowObserver::OnWindowParentChanged(
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().
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
88 if (IsDragging(window
) && parent
->id() == kShellWindowId_DockedContainer
)
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
);
97 void ShelfWindowWatcher::RemovedWindowObserver::OnWindowDestroyed(
98 aura::Window
* window
) {
99 DCHECK(HasShelfItemForWindow(window
));
100 window_watcher_
->FinishObservingRemovedWindow(window
);
103 ShelfWindowWatcher::ShelfWindowWatcher(
105 ShelfItemDelegateManager
* item_delegate_manager
)
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
);
121 Shell::GetScreen()->AddObserver(this);
124 ShelfWindowWatcher::~ShelfWindowWatcher() {
125 Shell::GetScreen()->RemoveObserver(this);
128 void ShelfWindowWatcher::AddShelfItem(aura::Window
* window
) {
129 const ShelfItemDetails
* item_details
=
130 GetShelfItemDetailsForWindow(window
);
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());
143 void ShelfWindowWatcher::RemoveShelfItem(aura::Window
* window
) {
144 model_
->RemoveItemAt(model_
->ItemIndexByID(GetShelfIDForWindow(window
)));
145 SetShelfIDForWindow(kInvalidShelfID
, window
);
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
);
155 aura::Window
* default_container
= Shell::GetContainer(
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
]);
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
));
169 void ShelfWindowWatcher::UpdateShelfItemStatus(aura::Window
* window
,
171 int index
= GetShelfItemIndexForWindow(window
);
174 ShelfItem item
= model_
->items()[index
];
175 item
.status
= is_active
? STATUS_ACTIVE
: STATUS_RUNNING
;
176 model_
->Set(index
, item
);
179 int ShelfWindowWatcher::GetShelfItemIndexForWindow(
180 aura::Window
* window
) const {
181 return model_
->ItemIndexByID(GetShelfIDForWindow(window
));
184 void ShelfWindowWatcher::StartObservingRemovedWindow(aura::Window
* window
) {
185 observed_removed_windows_
.Add(window
);
188 void ShelfWindowWatcher::FinishObservingRemovedWindow(aura::Window
* window
) {
189 observed_removed_windows_
.Remove(window
);
190 RemoveShelfItem(window
);
193 void ShelfWindowWatcher::OnWindowActivated(
194 aura::client::ActivationChangeObserver::ActivationReason reason
,
195 aura::Window
* gained_active
,
196 aura::Window
* lost_active
) {
197 if (gained_active
&& HasShelfItemForWindow(gained_active
))
198 UpdateShelfItemStatus(gained_active
, true);
199 if (lost_active
&& HasShelfItemForWindow(lost_active
))
200 UpdateShelfItemStatus(lost_active
, false);
203 void ShelfWindowWatcher::OnWindowAdded(aura::Window
* window
) {
204 observed_windows_
.Add(window
);
206 if (observed_removed_windows_
.IsObserving(window
)) {
207 // When |window| is added and it is already observed by
208 // |dragged_window_observer_|, |window| already has its item.
209 DCHECK(HasShelfItemForWindow(window
));
210 observed_removed_windows_
.Remove(window
);
214 // Add ShelfItem if |window| already has a ShelfItemDetails when it is
215 // created. Don't make a new ShelfItem for the re-parented |window| that
216 // already has a ShelfItem.
217 if (GetShelfIDForWindow(window
) == kInvalidShelfID
&&
218 GetShelfItemDetailsForWindow(window
))
219 AddShelfItem(window
);
222 void ShelfWindowWatcher::OnWillRemoveWindow(aura::Window
* window
) {
223 // Remove a child window of default container.
224 if (observed_windows_
.IsObserving(window
))
225 observed_windows_
.Remove(window
);
227 // Don't remove |window| item immediately. Instead, defer handling of removing
228 // |window|'s item to RemovedWindowObserver because |window| could be added
229 // again to default container.
230 if (HasShelfItemForWindow(window
))
231 StartObservingRemovedWindow(window
);
234 void ShelfWindowWatcher::OnWindowDestroying(aura::Window
* window
) {
235 // Remove the default container.
236 if (observed_windows_
.IsObserving(window
))
237 observed_windows_
.Remove(window
);
240 void ShelfWindowWatcher::OnWindowPropertyChanged(aura::Window
* window
,
243 if (key
!= kShelfItemDetailsKey
)
246 if (GetShelfItemDetailsForWindow(window
) == NULL
) {
247 // Removes ShelfItem for |window| when it has a ShelfItem.
248 if (reinterpret_cast<ShelfItemDetails
*>(old
) != NULL
)
249 RemoveShelfItem(window
);
253 // When ShelfItemDetails is changed, update ShelfItem.
254 if (HasShelfItemForWindow(window
)) {
255 int index
= GetShelfItemIndexForWindow(window
);
257 ShelfItem item
= model_
->items()[index
];
258 const ShelfItemDetails
* details
=
259 GetShelfItemDetailsForWindow(window
);
260 SetShelfItemDetailsForShelfItem(&item
, *details
);
261 model_
->Set(index
, item
);
265 // Creates a new ShelfItem for |window|.
266 AddShelfItem(window
);
269 void ShelfWindowWatcher::OnDisplayAdded(const gfx::Display
& new_display
) {
270 // Add a new RootWindow and its ActivationClient to observed list.
271 aura::Window
* root_window
= Shell::GetInstance()
272 ->window_tree_host_manager()
273 ->GetRootWindowForDisplayId(new_display
.id());
275 // When the primary root window's display get removed, the existing root
276 // window is taken over by the new display and the observer is already set.
277 if (!observed_root_windows_
.IsObserving(root_window
))
278 OnRootWindowAdded(root_window
);
281 void ShelfWindowWatcher::OnDisplayRemoved(const gfx::Display
& old_display
) {
282 // When this is called, RootWindow of |old_display| is already removed.
283 // Instead, we remove an observer from RootWindow and ActivationClient in the
284 // OnRootWindowDestroyed().
288 void ShelfWindowWatcher::OnDisplayMetricsChanged(const gfx::Display
&,