Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ash / wm / overview / window_selector.cc
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/wm/overview/window_selector.h"
6
7 #include <algorithm>
8
9 #include "ash/ash_switches.h"
10 #include "ash/root_window_controller.h"
11 #include "ash/shell.h"
12 #include "ash/switchable_windows.h"
13 #include "ash/wm/overview/window_overview.h"
14 #include "ash/wm/overview/window_selector_delegate.h"
15 #include "ash/wm/overview/window_selector_panels.h"
16 #include "ash/wm/overview/window_selector_window.h"
17 #include "ash/wm/window_state.h"
18 #include "base/auto_reset.h"
19 #include "base/command_line.h"
20 #include "base/metrics/histogram.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "ui/aura/client/focus_client.h"
23 #include "ui/aura/window.h"
24 #include "ui/aura/window_event_dispatcher.h"
25 #include "ui/aura/window_observer.h"
26 #include "ui/events/event.h"
27 #include "ui/events/event_handler.h"
28 #include "ui/wm/core/window_util.h"
29 #include "ui/wm/public/activation_client.h"
30
31 namespace ash {
32
33 namespace {
34
35 // A comparator for locating a given selectable window.
36 struct WindowSelectorItemComparator
37     : public std::unary_function<WindowSelectorItem*, bool> {
38   explicit WindowSelectorItemComparator(const aura::Window* window)
39       : window_(window) {
40   }
41
42   bool operator()(WindowSelectorItem* window) const {
43     return window->HasSelectableWindow(window_);
44   }
45
46   const aura::Window* window_;
47 };
48
49 // A comparator for locating a selectable window given a targeted window.
50 struct WindowSelectorItemTargetComparator
51     : public std::unary_function<WindowSelectorItem*, bool> {
52   explicit WindowSelectorItemTargetComparator(const aura::Window* target_window)
53       : target(target_window) {
54   }
55
56   bool operator()(WindowSelectorItem* window) const {
57     return window->TargetedWindow(target) != NULL;
58   }
59
60   const aura::Window* target;
61 };
62
63 // A comparator for locating a selector item for a given root.
64 struct WindowSelectorItemForRoot
65     : public std::unary_function<WindowSelectorItem*, bool> {
66   explicit WindowSelectorItemForRoot(const aura::Window* root)
67       : root_window(root) {
68   }
69
70   bool operator()(WindowSelectorItem* item) const {
71     return item->GetRootWindow() == root_window;
72   }
73
74   const aura::Window* root_window;
75 };
76
77 // Filter to watch for the termination of a keyboard gesture to cycle through
78 // multiple windows.
79 class WindowSelectorEventFilter : public ui::EventHandler {
80  public:
81   WindowSelectorEventFilter(WindowSelector* selector);
82   virtual ~WindowSelectorEventFilter();
83
84   // Overridden from ui::EventHandler:
85   virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
86
87  private:
88   // A weak pointer to the WindowSelector which owns this instance.
89   WindowSelector* selector_;
90
91   DISALLOW_COPY_AND_ASSIGN(WindowSelectorEventFilter);
92 };
93
94 // Watch for all keyboard events by filtering the root window.
95 WindowSelectorEventFilter::WindowSelectorEventFilter(WindowSelector* selector)
96     : selector_(selector) {
97   Shell::GetInstance()->AddPreTargetHandler(this);
98 }
99
100 WindowSelectorEventFilter::~WindowSelectorEventFilter() {
101   Shell::GetInstance()->RemovePreTargetHandler(this);
102 }
103
104 void WindowSelectorEventFilter::OnKeyEvent(ui::KeyEvent* event) {
105   // Views uses VKEY_MENU for both left and right Alt keys.
106   if (event->key_code() == ui::VKEY_MENU &&
107       event->type() == ui::ET_KEY_RELEASED) {
108     selector_->SelectWindow();
109     // Warning: |this| will be deleted from here on.
110   }
111 }
112
113 // Triggers a shelf visibility update on all root window controllers.
114 void UpdateShelfVisibility() {
115   Shell::RootWindowControllerList root_window_controllers =
116       Shell::GetInstance()->GetAllRootWindowControllers();
117   for (Shell::RootWindowControllerList::iterator iter =
118           root_window_controllers.begin();
119        iter != root_window_controllers.end(); ++iter) {
120     (*iter)->UpdateShelfVisibility();
121   }
122 }
123
124 // Returns the window immediately below |window| in the current container.
125 aura::Window* GetWindowBelow(aura::Window* window) {
126   aura::Window* parent = window->parent();
127   if (!parent)
128     return NULL;
129   aura::Window* below = NULL;
130   for (aura::Window::Windows::const_iterator iter = parent->children().begin();
131        iter != parent->children().end(); ++iter) {
132     if (*iter == window)
133       return below;
134     below = *iter;
135   }
136   NOTREACHED();
137   return NULL;
138 }
139
140 }  // namespace
141
142 // This class restores and moves a window to the front of the stacking order for
143 // the duration of the class's scope.
144 class ScopedShowWindow : public aura::WindowObserver {
145  public:
146   ScopedShowWindow();
147   virtual ~ScopedShowWindow();
148
149   // Show |window| at the top of the stacking order.
150   void Show(aura::Window* window);
151
152   // Cancel restoring the window on going out of scope.
153   void CancelRestore();
154
155   aura::Window* window() { return window_; }
156
157   // aura::WindowObserver:
158   virtual void OnWillRemoveWindow(aura::Window* window) OVERRIDE;
159
160  private:
161   // The window being shown.
162   aura::Window* window_;
163
164   // The window immediately below where window_ belongs.
165   aura::Window* stack_window_above_;
166
167   // If true, minimize window_ on going out of scope.
168   bool minimized_;
169
170   DISALLOW_COPY_AND_ASSIGN(ScopedShowWindow);
171 };
172
173 ScopedShowWindow::ScopedShowWindow()
174     : window_(NULL),
175       stack_window_above_(NULL),
176       minimized_(false) {
177 }
178
179 void ScopedShowWindow::Show(aura::Window* window) {
180   DCHECK(!window_);
181   window_ = window;
182   stack_window_above_ = GetWindowBelow(window);
183   minimized_ = wm::GetWindowState(window)->IsMinimized();
184   window_->parent()->AddObserver(this);
185   window_->Show();
186   wm::GetWindowState(window_)->Activate();
187 }
188
189 ScopedShowWindow::~ScopedShowWindow() {
190   if (window_) {
191     window_->parent()->RemoveObserver(this);
192
193     // Restore window's stacking position.
194     if (stack_window_above_)
195       window_->parent()->StackChildAbove(window_, stack_window_above_);
196     else
197       window_->parent()->StackChildAtBottom(window_);
198
199     // Restore minimized state.
200     if (minimized_)
201       wm::GetWindowState(window_)->Minimize();
202   }
203 }
204
205 void ScopedShowWindow::CancelRestore() {
206   if (!window_)
207     return;
208   window_->parent()->RemoveObserver(this);
209   window_ = stack_window_above_ = NULL;
210 }
211
212 void ScopedShowWindow::OnWillRemoveWindow(aura::Window* window) {
213   if (window == window_) {
214     CancelRestore();
215   } else if (window == stack_window_above_) {
216     // If the window this window was above is removed, use the next window down
217     // as the restore marker.
218     stack_window_above_ = GetWindowBelow(stack_window_above_);
219   }
220 }
221
222 WindowSelector::WindowSelector(const WindowList& windows,
223                                WindowSelector::Mode mode,
224                                WindowSelectorDelegate* delegate)
225     : mode_(mode),
226       delegate_(delegate),
227       selected_window_(0),
228       restore_focus_window_(aura::client::GetFocusClient(
229           Shell::GetPrimaryRootWindow())->GetFocusedWindow()),
230       ignore_activations_(false) {
231   DCHECK(delegate_);
232
233   if (restore_focus_window_)
234     restore_focus_window_->AddObserver(this);
235
236   std::vector<WindowSelectorPanels*> panels_items;
237   for (size_t i = 0; i < windows.size(); ++i) {
238     WindowSelectorItem* item = NULL;
239     if (windows[i] != restore_focus_window_)
240       windows[i]->AddObserver(this);
241     observed_windows_.insert(windows[i]);
242
243     if (windows[i]->type() == ui::wm::WINDOW_TYPE_PANEL &&
244         wm::GetWindowState(windows[i])->panel_attached()) {
245       // Attached panel windows are grouped into a single overview item per
246       // root window (display).
247       std::vector<WindowSelectorPanels*>::iterator iter =
248           std::find_if(panels_items.begin(), panels_items.end(),
249                        WindowSelectorItemForRoot(windows[i]->GetRootWindow()));
250       WindowSelectorPanels* panels_item = NULL;
251       if (iter == panels_items.end()) {
252         panels_item = new WindowSelectorPanels();
253         panels_items.push_back(panels_item);
254         windows_.push_back(panels_item);
255       } else {
256         panels_item = *iter;
257       }
258       panels_item->AddWindow(windows[i]);
259       item = panels_item;
260     } else {
261       item = new WindowSelectorWindow(windows[i]);
262       windows_.push_back(item);
263     }
264     // Verify that the window has been added to an item in overview.
265     CHECK(item->TargetedWindow(windows[i]));
266   }
267   UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.Items", windows_.size());
268
269   // Observe window activations and switchable containers on all root windows
270   // for newly created windows during overview.
271   Shell::GetInstance()->activation_client()->AddObserver(this);
272   aura::Window::Windows root_windows = Shell::GetAllRootWindows();
273   for (aura::Window::Windows::const_iterator iter = root_windows.begin();
274        iter != root_windows.end(); ++iter) {
275     for (size_t i = 0; i < kSwitchableWindowContainerIdsLength; ++i) {
276       aura::Window* container = Shell::GetContainer(*iter,
277           kSwitchableWindowContainerIds[i]);
278       container->AddObserver(this);
279       observed_windows_.insert(container);
280     }
281   }
282
283   if (mode == WindowSelector::CYCLE) {
284     cycle_start_time_ = base::Time::Now();
285     event_handler_.reset(new WindowSelectorEventFilter(this));
286   } else {
287     StartOverview();
288   }
289 }
290
291 WindowSelector::~WindowSelector() {
292   ResetFocusRestoreWindow(true);
293   for (std::set<aura::Window*>::iterator iter = observed_windows_.begin();
294        iter != observed_windows_.end(); ++iter) {
295     (*iter)->RemoveObserver(this);
296   }
297   Shell::GetInstance()->activation_client()->RemoveObserver(this);
298   aura::Window::Windows root_windows = Shell::GetAllRootWindows();
299   window_overview_.reset();
300   // Clearing the window list resets the ignored_by_shelf flag on the windows.
301   windows_.clear();
302   UpdateShelfVisibility();
303
304   if (!cycle_start_time_.is_null()) {
305     UMA_HISTOGRAM_MEDIUM_TIMES("Ash.WindowSelector.CycleTime",
306         base::Time::Now() - cycle_start_time_);
307   }
308 }
309
310 void WindowSelector::Step(WindowSelector::Direction direction) {
311   DCHECK(!windows_.empty());
312   // Upgrade to CYCLE mode if currently in OVERVIEW mode.
313   if (mode_ != CYCLE) {
314     event_handler_.reset(new WindowSelectorEventFilter(this));
315     DCHECK(window_overview_);
316     // Set the initial selection window to animate to the new selection.
317     window_overview_->SetSelection(selected_window_);
318     window_overview_->MoveToSingleRootWindow(
319         windows_[selected_window_]->GetRootWindow());
320     mode_ = CYCLE;
321   }
322
323   selected_window_ = (selected_window_ + windows_.size() +
324       (direction == WindowSelector::FORWARD ? 1 : -1)) % windows_.size();
325   if (window_overview_) {
326     window_overview_->SetSelection(selected_window_);
327   } else {
328     base::AutoReset<bool> restoring_focus(&ignore_activations_, true);
329     showing_window_.reset(new ScopedShowWindow);
330     showing_window_->Show(windows_[selected_window_]->SelectionWindow());
331   }
332 }
333
334 void WindowSelector::SelectWindow() {
335   SelectWindow(windows_[selected_window_]->SelectionWindow());
336 }
337
338 void WindowSelector::SelectWindow(aura::Window* window) {
339   ResetFocusRestoreWindow(false);
340   if (showing_window_ && showing_window_->window() == window)
341     showing_window_->CancelRestore();
342   ScopedVector<WindowSelectorItem>::iterator iter =
343       std::find_if(windows_.begin(), windows_.end(),
344                    WindowSelectorItemTargetComparator(window));
345   DCHECK(iter != windows_.end());
346   // The selected window should not be minimized when window selection is
347   // ended.
348   (*iter)->RestoreWindowOnExit(window);
349   delegate_->OnWindowSelected(window);
350 }
351
352 void WindowSelector::CancelSelection() {
353   delegate_->OnSelectionCanceled();
354 }
355
356 void WindowSelector::OnWindowAdded(aura::Window* new_window) {
357   if (new_window->type() != ui::wm::WINDOW_TYPE_NORMAL &&
358       new_window->type() != ui::wm::WINDOW_TYPE_PANEL) {
359     return;
360   }
361
362   for (size_t i = 0; i < kSwitchableWindowContainerIdsLength; ++i) {
363     if (new_window->parent()->id() == kSwitchableWindowContainerIds[i] &&
364         !::wm::GetTransientParent(new_window)) {
365       // The new window is in one of the switchable containers, abort overview.
366       CancelSelection();
367       return;
368     }
369   }
370 }
371
372 void WindowSelector::OnWindowDestroying(aura::Window* window) {
373   // window is one of a container, the restore_focus_window and/or
374   // one of the selectable windows in overview.
375   ScopedVector<WindowSelectorItem>::iterator iter =
376       std::find_if(windows_.begin(), windows_.end(),
377                    WindowSelectorItemComparator(window));
378   window->RemoveObserver(this);
379   observed_windows_.erase(window);
380   if (window == restore_focus_window_)
381     restore_focus_window_ = NULL;
382   if (iter == windows_.end())
383     return;
384
385   (*iter)->RemoveWindow(window);
386   // If there are still windows in this selector entry then the overview is
387   // still active and the active selection remains the same.
388   if (!(*iter)->empty())
389     return;
390
391   size_t deleted_index = iter - windows_.begin();
392   windows_.erase(iter);
393   if (windows_.empty()) {
394     CancelSelection();
395     return;
396   }
397   if (window_overview_)
398     window_overview_->OnWindowsChanged();
399   if (mode_ == CYCLE && selected_window_ >= deleted_index) {
400     if (selected_window_ > deleted_index)
401       selected_window_--;
402     selected_window_ = selected_window_ % windows_.size();
403     if (window_overview_)
404       window_overview_->SetSelection(selected_window_);
405   }
406 }
407
408 void WindowSelector::OnWindowBoundsChanged(aura::Window* window,
409                                            const gfx::Rect& old_bounds,
410                                            const gfx::Rect& new_bounds) {
411   if (!window_overview_)
412     return;
413
414   ScopedVector<WindowSelectorItem>::iterator iter =
415       std::find_if(windows_.begin(), windows_.end(),
416                    WindowSelectorItemTargetComparator(window));
417   if (iter == windows_.end())
418     return;
419
420   // Immediately finish any active bounds animation.
421   window->layer()->GetAnimator()->StopAnimatingProperty(
422       ui::LayerAnimationElement::BOUNDS);
423
424   // Recompute the transform for the window.
425   (*iter)->RecomputeWindowTransforms();
426 }
427
428 void WindowSelector::OnWindowActivated(aura::Window* gained_active,
429                                        aura::Window* lost_active) {
430   if (ignore_activations_ || !gained_active)
431     return;
432   // Don't restore focus on exit if a window was just activated.
433   ResetFocusRestoreWindow(false);
434   CancelSelection();
435 }
436
437 void WindowSelector::OnAttemptToReactivateWindow(aura::Window* request_active,
438                                                  aura::Window* actual_active) {
439   if (ignore_activations_)
440     return;
441   // Don't restore focus on exit if a window was just activated.
442   ResetFocusRestoreWindow(false);
443   CancelSelection();
444 }
445
446 void WindowSelector::StartOverview() {
447   DCHECK(!window_overview_);
448   // Remove focus from active window before entering overview.
449   aura::client::GetFocusClient(
450       Shell::GetPrimaryRootWindow())->FocusWindow(NULL);
451
452   aura::Window* overview_root = NULL;
453   if (mode_ == CYCLE)
454     overview_root = windows_[selected_window_]->GetRootWindow();
455   window_overview_.reset(new WindowOverview(this, &windows_, overview_root));
456   if (mode_ == CYCLE)
457     window_overview_->SetSelection(selected_window_);
458   UpdateShelfVisibility();
459 }
460
461 void WindowSelector::ResetFocusRestoreWindow(bool focus) {
462   if (!restore_focus_window_)
463     return;
464   if (focus) {
465     base::AutoReset<bool> restoring_focus(&ignore_activations_, true);
466     restore_focus_window_->Focus();
467   }
468   // If the window is in the observed_windows_ list it needs to continue to be
469   // observed.
470   if (observed_windows_.find(restore_focus_window_) ==
471           observed_windows_.end()) {
472     restore_focus_window_->RemoveObserver(this);
473   }
474   restore_focus_window_ = NULL;
475 }
476
477 }  // namespace ash