1// Copyright (c) 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 "ui/wm/core/transient_window_stacking_client.h"
6
7#include <algorithm>
8
9#include "ui/wm/core/transient_window_manager.h"
10#include "ui/wm/core/window_util.h"
11
12using aura::Window;
13
14namespace wm {
15
16namespace {
17
18// Populates |ancestors| with all transient ancestors of |window| that are
19// siblings of |window|. Returns true if any ancestors were found, false if not.
20bool GetAllTransientAncestors(Window* window, Window::Windows* ancestors) {
21  Window* parent = window->parent();
22  for (; window; window = GetTransientParent(window)) {
23    if (window->parent() == parent)
24      ancestors->push_back(window);
25  }
26  return (!ancestors->empty());
27}
28
29// Replaces |window1| and |window2| with their possible transient ancestors that
30// are still siblings (have a common transient parent).  |window1| and |window2|
31// are not modified if such ancestors cannot be found.
32void FindCommonTransientAncestor(Window** window1, Window** window2) {
33  DCHECK(window1);
34  DCHECK(window2);
35  DCHECK(*window1);
36  DCHECK(*window2);
37  // Assemble chains of ancestors of both windows.
38  Window::Windows ancestors1;
39  Window::Windows ancestors2;
40  if (!GetAllTransientAncestors(*window1, &ancestors1) ||
41      !GetAllTransientAncestors(*window2, &ancestors2)) {
42    return;
43  }
44  // Walk the two chains backwards and look for the first difference.
45  Window::Windows::reverse_iterator it1 = ancestors1.rbegin();
46  Window::Windows::reverse_iterator it2 = ancestors2.rbegin();
47  for (; it1  != ancestors1.rend() && it2  != ancestors2.rend(); ++it1, ++it2) {
48    if (*it1 != *it2) {
49      *window1 = *it1;
50      *window2 = *it2;
51      break;
52    }
53  }
54}
55
56// Adjusts |target| so that we don't attempt to stack on top of a window with a
57// NULL delegate.
58void SkipNullDelegates(Window::StackDirection direction, Window** target) {
59  const Window::Windows& children((*target)->parent()->children());
60  size_t target_i =
61      std::find(children.begin(), children.end(), *target) -
62      children.begin();
63
64  // By convention we don't stack on top of windows with layers with NULL
65  // delegates.  Walk backward to find a valid target window.  See tests
66  // TransientWindowManagerTest.StackingMadrigal and StackOverClosingTransient
67  // for an explanation of this.
68  while (target_i > 0) {
69    const size_t index = direction == Window::STACK_ABOVE ?
70        target_i : target_i - 1;
71    if (!children[index]->layer() ||
72        children[index]->layer()->delegate() != NULL)
73      break;
74    --target_i;
75  }
76  *target = children[target_i];
77}
78
79}  // namespace
80
81// static
82TransientWindowStackingClient* TransientWindowStackingClient::instance_ = NULL;
83
84TransientWindowStackingClient::TransientWindowStackingClient() {
85  instance_ = this;
86}
87
88TransientWindowStackingClient::~TransientWindowStackingClient() {
89  if (instance_ == this)
90    instance_ = NULL;
91}
92
93bool TransientWindowStackingClient::AdjustStacking(
94    Window** child,
95    Window** target,
96    Window::StackDirection* direction) {
97  const TransientWindowManager* transient_manager =
98      TransientWindowManager::Get(static_cast<const Window*>(*child));
99  if (transient_manager && transient_manager->IsStackingTransient(*target))
100    return true;
101
102  // For windows that have transient children stack the transient ancestors that
103  // are siblings. This prevents one transient group from being inserted in the
104  // middle of another.
105  FindCommonTransientAncestor(child, target);
106
107  // When stacking above skip to the topmost transient descendant of the target.
108  if (*direction == Window::STACK_ABOVE &&
109      !HasTransientAncestor(*child, *target)) {
110    const Window::Windows& siblings((*child)->parent()->children());
111    size_t target_i =
112        std::find(siblings.begin(), siblings.end(), *target) - siblings.begin();
113    while (target_i + 1 < siblings.size() &&
114           HasTransientAncestor(siblings[target_i + 1], *target)) {
115      ++target_i;
116    }
117    *target = siblings[target_i];
118  }
119
120  SkipNullDelegates(*direction, target);
121
122  // If we couldn't find a valid target position, don't move anything.
123  if (*direction == Window::STACK_ABOVE &&
124      ((*target)->layer() && (*target)->layer()->delegate() == NULL)) {
125    return false;
126  }
127
128  return *child != *target;
129}
130
131}  // namespace wm
132