1// Copyright (c) 2012 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/ash_focus_rules.h"
6
7#include "ash/shell.h"
8#include "ash/shell_window_ids.h"
9#include "ash/wm/window_state.h"
10#include "ui/aura/window.h"
11
12namespace ash {
13namespace wm {
14namespace {
15
16// These are the list of container ids of containers which may contain windows
17// that need to be activated in the order that they should be activated.
18const int kWindowContainerIds[] = {
19    kShellWindowId_OverlayContainer,
20    kShellWindowId_LockSystemModalContainer,
21    kShellWindowId_SettingBubbleContainer,
22    kShellWindowId_LockScreenContainer,
23    kShellWindowId_SystemModalContainer,
24    kShellWindowId_AlwaysOnTopContainer,
25    kShellWindowId_AppListContainer,
26    kShellWindowId_DefaultContainer,
27
28    // Docked, panel, launcher and status are intentionally checked after other
29    // containers even though these layers are higher. The user expects their
30    // windows to be focused before these elements.
31    kShellWindowId_DockedContainer,
32    kShellWindowId_PanelContainer,
33    kShellWindowId_ShelfContainer,
34    kShellWindowId_StatusContainer, };
35
36bool BelongsToContainerWithEqualOrGreaterId(const aura::Window* window,
37                                            int container_id) {
38  for (; window; window = window->parent()) {
39    if (window->id() >= container_id)
40      return true;
41  }
42  return false;
43}
44
45}  // namespace
46
47////////////////////////////////////////////////////////////////////////////////
48// AshFocusRules, public:
49
50AshFocusRules::AshFocusRules() {
51}
52
53AshFocusRules::~AshFocusRules() {
54}
55
56////////////////////////////////////////////////////////////////////////////////
57// AshFocusRules, ::wm::FocusRules:
58
59bool AshFocusRules::SupportsChildActivation(aura::Window* window) const {
60  if (window->id() == kShellWindowId_DefaultContainer)
61    return true;
62
63  for (size_t i = 0; i < arraysize(kWindowContainerIds); i++) {
64    if (window->id() == kWindowContainerIds[i])
65      return true;
66  }
67  return false;
68}
69
70bool AshFocusRules::IsWindowConsideredVisibleForActivation(
71    aura::Window* window) const {
72  if (BaseFocusRules::IsWindowConsideredVisibleForActivation(window))
73    return true;
74
75  // Minimized windows are hidden in their minimized state, but they can always
76  // be activated.
77  if (wm::GetWindowState(window)->IsMinimized())
78    return true;
79
80  return window->TargetVisibility() &&
81         (window->parent()->id() == kShellWindowId_DefaultContainer ||
82          window->parent()->id() == kShellWindowId_LockScreenContainer);
83}
84
85bool AshFocusRules::CanActivateWindow(aura::Window* window) const {
86  // Clearing activation is always permissible.
87  if (!window)
88    return true;
89
90  if (!BaseFocusRules::CanActivateWindow(window))
91    return false;
92
93  if (Shell::GetInstance()->IsSystemModalWindowOpen()) {
94    return BelongsToContainerWithEqualOrGreaterId(
95        window, kShellWindowId_SystemModalContainer);
96  }
97
98  return true;
99}
100
101aura::Window* AshFocusRules::GetNextActivatableWindow(
102    aura::Window* ignore) const {
103  DCHECK(ignore);
104
105  int starting_container_index = 0;
106  // If the container of the window losing focus is in the list, start from that
107  // container.
108  aura::Window* root = ignore->GetRootWindow();
109  if (!root)
110    root = Shell::GetTargetRootWindow();
111  int container_count = static_cast<int>(arraysize(kWindowContainerIds));
112  for (int i = 0; ignore && i < container_count; i++) {
113    aura::Window* container = Shell::GetContainer(root, kWindowContainerIds[i]);
114    if (container && container->Contains(ignore)) {
115      starting_container_index = i;
116      break;
117    }
118  }
119
120  // Look for windows to focus in |ignore|'s container. If none are found, we
121  // look in all the containers in front of |ignore|'s container, then all
122  // behind.
123  aura::Window* window = NULL;
124  for (int i = starting_container_index; !window && i < container_count; i++)
125    window = GetTopmostWindowToActivateForContainerIndex(i, ignore);
126  if (!window && starting_container_index > 0) {
127    for (int i = starting_container_index - 1; !window && i >= 0; i--)
128      window = GetTopmostWindowToActivateForContainerIndex(i, ignore);
129  }
130  return window;
131}
132
133////////////////////////////////////////////////////////////////////////////////
134// AshFocusRules, private:
135
136aura::Window* AshFocusRules::GetTopmostWindowToActivateForContainerIndex(
137    int index,
138    aura::Window* ignore) const {
139  aura::Window* window = NULL;
140  aura::Window* root = ignore ? ignore->GetRootWindow() : NULL;
141  aura::Window::Windows containers = Shell::GetContainersFromAllRootWindows(
142      kWindowContainerIds[index], root);
143  for (aura::Window::Windows::const_iterator iter = containers.begin();
144        iter != containers.end() && !window; ++iter) {
145    window = GetTopmostWindowToActivateInContainer((*iter), ignore);
146  }
147  return window;
148}
149
150aura::Window* AshFocusRules::GetTopmostWindowToActivateInContainer(
151    aura::Window* container,
152    aura::Window* ignore) const {
153  for (aura::Window::Windows::const_reverse_iterator i =
154           container->children().rbegin();
155       i != container->children().rend();
156       ++i) {
157    WindowState* window_state = GetWindowState(*i);
158    if (*i != ignore &&
159        window_state->CanActivate() &&
160        !window_state->IsMinimized())
161      return *i;
162  }
163  return NULL;
164}
165
166}  // namespace wm
167}  // namespace ash
168