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/mru_window_tracker.h" 6 7 #include <algorithm> 8 9 #include "ash/session_state_delegate.h" 10 #include "ash/shell.h" 11 #include "ash/shell_window_ids.h" 12 #include "ash/wm/activation_controller.h" 13 #include "ash/wm/window_cycle_list.h" 14 #include "ash/wm/window_util.h" 15 #include "ash/wm/workspace_controller.h" 16 #include "ui/aura/root_window.h" 17 #include "ui/base/events/event.h" 18 #include "ui/base/events/event_handler.h" 19 20 namespace ash { 21 22 namespace { 23 24 // List of containers whose children we will allow switching to. 25 const int kContainerIds[] = { 26 internal::kShellWindowId_DefaultContainer, 27 internal::kShellWindowId_AlwaysOnTopContainer 28 }; 29 30 // Adds the windows that can be cycled through for the specified window id to 31 // |windows|. 32 void AddTrackedWindows(aura::RootWindow* root, 33 int container_id, 34 MruWindowTracker::WindowList* windows) { 35 aura::Window* container = Shell::GetContainer(root, container_id); 36 const MruWindowTracker::WindowList& children(container->children()); 37 windows->insert(windows->end(), children.begin(), children.end()); 38 } 39 40 // Returns a list of windows ordered by their stacking order. 41 // If |mru_windows| is passed, these windows are moved to the front of the list. 42 // If |top_most_at_end|, the list is returned in descending (bottom-most / least 43 // recently used) order. 44 MruWindowTracker::WindowList BuildWindowListInternal( 45 const std::list<aura::Window*>* mru_windows, 46 bool top_most_at_end) { 47 MruWindowTracker::WindowList windows; 48 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 49 50 aura::RootWindow* active_root = Shell::GetActiveRootWindow(); 51 for (Shell::RootWindowList::const_iterator iter = root_windows.begin(); 52 iter != root_windows.end(); ++iter) { 53 if (*iter == active_root) 54 continue; 55 for (size_t i = 0; i < arraysize(kContainerIds); ++i) 56 AddTrackedWindows(*iter, kContainerIds[i], &windows); 57 } 58 59 // Add windows in the active root windows last so that the topmost window 60 // in the active root window becomes the front of the list. 61 for (size_t i = 0; i < arraysize(kContainerIds); ++i) 62 AddTrackedWindows(active_root, kContainerIds[i], &windows); 63 64 // Removes unfocusable windows. 65 MruWindowTracker::WindowList::iterator last = 66 std::remove_if( 67 windows.begin(), 68 windows.end(), 69 std::not1(std::ptr_fun(ash::wm::CanActivateWindow))); 70 windows.erase(last, windows.end()); 71 72 // Put the windows in the mru_windows list at the head, if it's available. 73 if (mru_windows) { 74 // Iterate through the list backwards, so that we can move each window to 75 // the front of the windows list as we find them. 76 for (std::list<aura::Window*>::const_reverse_iterator ix = 77 mru_windows->rbegin(); 78 ix != mru_windows->rend(); ++ix) { 79 MruWindowTracker::WindowList::iterator window = 80 std::find(windows.begin(), windows.end(), *ix); 81 if (window != windows.end()) { 82 windows.erase(window); 83 windows.push_back(*ix); 84 } 85 } 86 } 87 88 // Window cycling expects the topmost window at the front of the list. 89 if (!top_most_at_end) 90 std::reverse(windows.begin(), windows.end()); 91 92 return windows; 93 } 94 95 } // namespace 96 97 ////////////////////////////////////////////////////////////////////////////// 98 // MruWindowTracker, public: 99 100 MruWindowTracker::MruWindowTracker( 101 aura::client::ActivationClient* activation_client) 102 : activation_client_(activation_client), 103 ignore_window_activations_(false) { 104 activation_client_->AddObserver(this); 105 } 106 107 MruWindowTracker::~MruWindowTracker() { 108 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 109 for (Shell::RootWindowList::const_iterator iter = root_windows.begin(); 110 iter != root_windows.end(); ++iter) { 111 for (size_t i = 0; i < arraysize(kContainerIds); ++i) { 112 aura::Window* container = Shell::GetContainer(*iter, kContainerIds[i]); 113 if (container) 114 container->RemoveObserver(this); 115 } 116 } 117 118 activation_client_->RemoveObserver(this); 119 } 120 121 // static 122 MruWindowTracker::WindowList MruWindowTracker::BuildWindowList( 123 bool top_most_at_end) { 124 return BuildWindowListInternal(NULL, top_most_at_end); 125 } 126 127 MruWindowTracker::WindowList MruWindowTracker::BuildMruWindowList() { 128 return BuildWindowListInternal(&mru_windows_, false); 129 } 130 131 void MruWindowTracker::OnRootWindowAdded(aura::RootWindow* root_window) { 132 for (size_t i = 0; i < arraysize(kContainerIds); ++i) { 133 aura::Window* container = 134 Shell::GetContainer(root_window, kContainerIds[i]); 135 container->AddObserver(this); 136 } 137 } 138 139 void MruWindowTracker::SetIgnoreActivations(bool ignore) { 140 ignore_window_activations_ = ignore; 141 142 // If no longer ignoring window activations, move currently active window 143 // to front. 144 if (!ignore) { 145 aura::Window* active_window = wm::GetActiveWindow(); 146 mru_windows_.remove(active_window); 147 mru_windows_.push_front(active_window); 148 } 149 } 150 151 ////////////////////////////////////////////////////////////////////////////// 152 // MruWindowTracker, private: 153 154 // static 155 bool MruWindowTracker::IsTrackedContainer(aura::Window* window) { 156 if (!window) 157 return false; 158 for (size_t i = 0; i < arraysize(kContainerIds); ++i) { 159 if (window->id() == kContainerIds[i]) 160 return true; 161 } 162 return false; 163 } 164 165 void MruWindowTracker::OnWindowActivated(aura::Window* gained_active, 166 aura::Window* lost_active) { 167 if (gained_active && !ignore_window_activations_ && 168 IsTrackedContainer(gained_active->parent())) { 169 mru_windows_.remove(gained_active); 170 mru_windows_.push_front(gained_active); 171 } 172 } 173 174 void MruWindowTracker::OnWillRemoveWindow(aura::Window* window) { 175 mru_windows_.remove(window); 176 } 177 178 void MruWindowTracker::OnWindowDestroying(aura::Window* window) { 179 window->RemoveObserver(this); 180 } 181 182 } // namespace ash 183