root/chrome/browser/ui/panels/stacked_panel_collection.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. minimizing_all_
  2. OnDisplayChanged
  3. RefreshLayout
  4. RefreshLayoutWithTopPanelStartingAt
  5. GetTitle
  6. GetIcon
  7. PanelBoundsBatchUpdateCompleted
  8. GetEnclosingBounds
  9. MinimizePanelsForSpace
  10. AddPanel
  11. RemovePanel
  12. CloseAll
  13. OnPanelAttentionStateChanged
  14. OnPanelTitlebarClicked
  15. ResizePanelWindow
  16. ActivatePanel
  17. MinimizePanel
  18. RestorePanel
  19. MinimizeAll
  20. RestoreAll
  21. OnMinimizeButtonClicked
  22. OnRestoreButtonClicked
  23. CanShowMinimizeButton
  24. CanShowRestoreButton
  25. IsPanelMinimized
  26. UsesAlwaysOnTopPanels
  27. SavePanelPlacement
  28. RestorePanelToSavedPlacement
  29. DiscardSavedPanelPlacement
  30. GetPanelResizability
  31. OnPanelResizedByMouse
  32. HasPanel
  33. UpdatePanelOnCollectionChange
  34. OnPanelExpansionStateChanged
  35. OnPanelActiveStateChanged
  36. GetInitialPanelBounds
  37. GetPanelAbove
  38. GetPanelBelow
  39. MoveAllDraggingPanelsInstantly
  40. IsMinimized
  41. IsAnimatingPanelBounds
  42. UpdatePanelCornerStyle
  43. GetWorkArea
  44. GetCurrentAvailableTopSpace
  45. GetCurrentAvailableBottomSpace
  46. GetMaximiumAvailableBottomSpace
  47. GetStackWindowForPanel

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/panels/stacked_panel_collection.h"

#include <algorithm>
#include "base/auto_reset.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/panels/detached_panel_collection.h"
#include "chrome/browser/ui/panels/display_settings_provider.h"
#include "chrome/browser/ui/panels/native_panel_stack_window.h"
#include "chrome/browser/ui/panels/panel.h"
#include "chrome/browser/ui/panels/panel_constants.h"
#include "chrome/browser/ui/panels/panel_manager.h"
#include "extensions/common/extension.h"

StackedPanelCollection::StackedPanelCollection(PanelManager* panel_manager)
    : PanelCollection(PanelCollection::STACKED),
      panel_manager_(panel_manager),
      primary_stack_window_(NULL),
      secondary_stack_window_(NULL),
      minimizing_all_(false) {
}

StackedPanelCollection::~StackedPanelCollection() {
  DCHECK(panels_.empty());
  DCHECK(most_recently_active_panels_.empty());
}

void StackedPanelCollection::OnDisplayChanged() {
  if (panels_.empty())
    return;

  gfx::Rect enclosing_bounds = GetEnclosingBounds();
  gfx::Rect work_area = panel_manager_->display_settings_provider()->
      GetWorkAreaMatching(enclosing_bounds);

  // If the height of the whole stack is bigger than the height of the new work
  // area, try to reduce the stack height by collapsing panels. In rare case,
  // all panels are collapsed and there is still not enough space. We simply
  // let the stack go beyond the work area limit.
  if (enclosing_bounds.height() > work_area.height()) {
    int needed_space = enclosing_bounds.height() - work_area.height();
    MinimizePanelsForSpace(needed_space);
  }

  gfx::Rect top_bounds = top_panel()->GetBounds();
  int common_width = top_bounds.width();
  if (common_width > work_area.width())
    common_width = work_area.width();

  int common_x = top_bounds.x();
  if (common_x + common_width > work_area.right())
    common_x = work_area.right() - common_width;
  if (common_x < work_area.x())
    common_x = work_area.x();

  int total_height = bottom_panel()->GetBounds().bottom() - top_bounds.y();
  int start_y = top_bounds.y();
  if (start_y + total_height > work_area.bottom())
    start_y = work_area.bottom() - total_height;
  if (start_y < work_area.y())
    start_y = work_area.y();

  RefreshLayoutWithTopPanelStartingAt(
      gfx::Point(common_x, start_y), common_width);
}

void StackedPanelCollection::RefreshLayout() {
  if (panels_.empty())
    return;
  gfx::Rect top_bounds = top_panel()->GetBounds();
  RefreshLayoutWithTopPanelStartingAt(top_bounds.origin(), top_bounds.width());
}

void StackedPanelCollection::RefreshLayoutWithTopPanelStartingAt(
    const gfx::Point& start_position, int common_width) {
  if (panels_.empty())
    return;

  // If only one panel is left in the stack, we only need to check if it should
  // be moved to |start_y| position.
  if (panels_.size() == 1) {
    Panel* panel = panels_.front();
    gfx::Rect bounds = panel->GetBounds();
    if (bounds.origin() != start_position) {
      bounds.set_origin(start_position);
      panel->SetPanelBounds(bounds);
    }
    return;
  }

  // We do not update bounds for affected panels one by one. Instead, all
  // changes are bundled and performed synchronously.
  primary_stack_window_->BeginBatchUpdatePanelBounds(true);
  if (secondary_stack_window_)
    secondary_stack_window_->BeginBatchUpdatePanelBounds(true);

  int y = start_position.y();
  int common_x = start_position.x();
  for (Panels::const_iterator iter = panels_.begin();
       iter != panels_.end(); ++iter) {
    Panel* panel = *iter;

    // The visibility of minimize button might need to be updated due to that
    // top panel might change when a panel is being added or removed from
    // the stack.
    panel->UpdateMinimizeRestoreButtonVisibility();

    // Don't update the stacked panel that is in preview mode.
    gfx::Rect bounds = panel->GetBounds();
    if (panel->in_preview_mode()) {
      y += bounds.height();
      continue;
    }

    // Update the restored size.
    gfx::Size full_size = panel->full_size();
    full_size.set_width(common_width);
    panel->set_full_size(full_size);

    // Recompute the bounds.
    bounds.SetRect(
        common_x,
        y,
        common_width,
        panel->expansion_state() == Panel::EXPANDED ?
            panel->full_size().height() : panel->TitleOnlyHeight());

    GetStackWindowForPanel(panel)->AddPanelBoundsForBatchUpdate(panel, bounds);

    y += bounds.height();
  }

  primary_stack_window_->EndBatchUpdatePanelBounds();
  if (secondary_stack_window_)
    secondary_stack_window_->EndBatchUpdatePanelBounds();
}

base::string16 StackedPanelCollection::GetTitle() const {
  if (panels_.empty())
    return base::string16();

  Panel* panel = panels_.front();
  const extensions::Extension* extension = panel->GetExtension();
  return base::UTF8ToUTF16(extension && !extension->name().empty() ?
      extension->name() : panel->app_name());
}

gfx::Image StackedPanelCollection::GetIcon() const {
  if (panels_.empty())
    return gfx::Image();
  return panels_.front()->app_icon();
}

void StackedPanelCollection::PanelBoundsBatchUpdateCompleted() {
  if (!secondary_stack_window_ || panels_.empty())
    return;

  if (top_panel()->in_preview_mode() != bottom_panel()->in_preview_mode() ||
      primary_stack_window_->IsAnimatingPanelBounds() ||
      secondary_stack_window_->IsAnimatingPanelBounds())
    return;

  // Move all panels from secondary stack window to primary stack window.
  primary_stack_window_->MergeWith(secondary_stack_window_);
  secondary_stack_window_->Close();
  secondary_stack_window_ = NULL;
}

gfx::Rect StackedPanelCollection::GetEnclosingBounds() const {
  gfx::Rect enclosing_bounds = top_panel()->GetBounds();
  enclosing_bounds.set_height(
      bottom_panel()->GetBounds().bottom() - enclosing_bounds.y());
  return enclosing_bounds;
}

int StackedPanelCollection::MinimizePanelsForSpace(int needed_space) {
  int available_space = GetCurrentAvailableBottomSpace();

  // Only the most recently active panel might be active.
  Panel* active_panel = NULL;
  if (!most_recently_active_panels_.empty()) {
    Panel* most_recently_active_panel = most_recently_active_panels_.front();
    if (most_recently_active_panel->IsActive())
      active_panel = most_recently_active_panel;
  }

  for (Panels::const_reverse_iterator iter =
           most_recently_active_panels_.rbegin();
       iter != most_recently_active_panels_.rend() &&
           available_space < needed_space;
       ++iter) {
    Panel* current_panel = *iter;
    if (current_panel != active_panel && !IsPanelMinimized(current_panel)) {
      available_space +=
          current_panel->GetBounds().height() - panel::kTitlebarHeight;
      MinimizePanel(current_panel);
    }
  }
  return available_space;
}

void StackedPanelCollection::AddPanel(Panel* panel,
                                      PositioningMask positioning_mask) {
  DCHECK_NE(this, panel->collection());
  panel->set_collection(this);
  Panel* adjacent_panel = NULL;
  if (positioning_mask & PanelCollection::TOP_POSITION) {
    adjacent_panel = top_panel();
    panels_.push_front(panel);
  } else {
    // To fit the new panel within the working area, collapse unfocused panels
    // in the least recent active order until there is enough space.
    if (positioning_mask & PanelCollection::COLLAPSE_TO_FIT) {
      int needed_space = panel->GetBounds().height();
      int available_space = MinimizePanelsForSpace(needed_space);
      DCHECK(available_space >= needed_space);
    }

    adjacent_panel = bottom_panel();
    panels_.push_back(panel);
  }

  if (panel->IsActive())
    most_recently_active_panels_.push_front(panel);
  else
    most_recently_active_panels_.push_back(panel);

  if (adjacent_panel)
    UpdatePanelCornerStyle(adjacent_panel);

  // The secondary stack window should be used when one of the following occurs:
  // 1) Some panels but not all panels are being dragged. This is because
  //    those panels being dragged might not be fully aligned with other panels
  //    not being dragged.
  // 2) The newly added panel is not fully aligned with the existing panel, in
  //    terms of both x and width.
  NativePanelStackWindow* stack_window;
  if (top_panel()->in_preview_mode() == bottom_panel()->in_preview_mode() &&
      top_panel()->GetBounds().x() == bottom_panel()->GetBounds().x() &&
      top_panel()->GetBounds().width() == bottom_panel()->GetBounds().width()) {
    if (!primary_stack_window_)
      primary_stack_window_ = NativePanelStackWindow::Create(this);
    stack_window = primary_stack_window_;
  } else {
    if (!secondary_stack_window_)
      secondary_stack_window_ = NativePanelStackWindow::Create(this);
    stack_window = secondary_stack_window_;
  }
  stack_window->AddPanel(panel);

  if ((positioning_mask & NO_LAYOUT_REFRESH) == 0)
    RefreshLayout();
}

void StackedPanelCollection::RemovePanel(Panel* panel, RemovalReason reason) {
  bool is_top = panel == top_panel();
  bool is_bottom = panel == bottom_panel();

  // If the top panel is being closed, all panels below it should move up. To
  // do this, the top y position of top panel needs to be tracked first.
  bool top_panel_closed = false;
  gfx::Point top_origin;
  int top_width = 0;
  if (reason == PanelCollection::PANEL_CLOSED && is_top) {
    top_panel_closed = true;
    top_origin = panel->GetBounds().origin();
    top_width = panel->GetBounds().width();
  }

  panel->set_collection(NULL);
  panels_.remove(panel);
  most_recently_active_panels_.remove(panel);

  if (is_top) {
    Panel* new_top_panel = top_panel();
    if (new_top_panel)
      UpdatePanelCornerStyle(new_top_panel);
  } else if (is_bottom) {
    Panel* new_bottom_panel = bottom_panel();
    if (new_bottom_panel)
      UpdatePanelCornerStyle(new_bottom_panel);
  }

  // If an active panel is being closed, try to focus the next recently active
  // panel in the stack that is not minimized.
  if (reason == PanelCollection::PANEL_CLOSED &&
      panel->IsActive() &&
      !most_recently_active_panels_.empty()) {
    for (Panels::const_iterator iter = most_recently_active_panels_.begin();
       iter != most_recently_active_panels_.end(); ++iter) {
      Panel* other_panel = *iter;
      if (!IsPanelMinimized(other_panel)) {
        other_panel->Activate();
        break;
      }
    }
  }

  // If the top panel is closed, move up all other panels to stay at the same
  // y position as the top panel being closed.
  if (top_panel_closed)
    RefreshLayoutWithTopPanelStartingAt(top_origin, top_width);
  else if (reason == PanelCollection::PANEL_CLOSED)
    RefreshLayout();

  // Remove the panel from the corresponding stack window.
  GetStackWindowForPanel(panel)->RemovePanel(panel);

  // Close the secondary stack window if no panel is is shown inside it.
  // Note that we do not need to do this for primary stack window since the
  // whole stack will be gone when only one panel is left.
  if (secondary_stack_window_ && secondary_stack_window_->IsEmpty()) {
    secondary_stack_window_->Close();
    secondary_stack_window_ = NULL;
  }
}

void StackedPanelCollection::CloseAll() {
  // Make a copy as closing panels can modify the iterator.
  Panels panels_copy = panels_;

  for (Panels::const_iterator iter = panels_copy.begin();
       iter != panels_copy.end(); ++iter)
    (*iter)->Close();

  if (primary_stack_window_) {
    primary_stack_window_->Close();
    primary_stack_window_ = NULL;
  }
  if (secondary_stack_window_) {
    secondary_stack_window_->Close();
    secondary_stack_window_ = NULL;
  }
}

void StackedPanelCollection::OnPanelAttentionStateChanged(Panel* panel) {
  if ((panel->attention_mode() & Panel::USE_SYSTEM_ATTENTION) != 0)
    primary_stack_window_->DrawSystemAttention(panel->IsDrawingAttention());
}

void StackedPanelCollection::OnPanelTitlebarClicked(
    Panel* panel, panel::ClickModifier modifier) {
  bool expanded = panel->expansion_state() == Panel::EXPANDED;
  if (modifier == panel::APPLY_TO_ALL) {
    if (expanded)
      MinimizeAll();
    else
      RestoreAll(panel);
  } else {
    if (expanded)
      MinimizePanel(panel);
    else
      RestorePanel(panel);
  }
}

void StackedPanelCollection::ResizePanelWindow(
    Panel* panel,
    const gfx::Size& preferred_window_size) {
}

void StackedPanelCollection::ActivatePanel(Panel* panel) {
  // Make sure the panel is expanded when activated so the user input
  // does not go into a collapsed window.
  if (panel->IsMinimized())
    panel->SetExpansionState(Panel::EXPANDED);
}

void StackedPanelCollection::MinimizePanel(Panel* panel) {
  panel->SetExpansionState(Panel::TITLE_ONLY);
}

void StackedPanelCollection::RestorePanel(Panel* panel) {
  // Ensure that the panel could fit within the work area after it is expanded.
  // First, try to collapse the unfocused panel in the least recent active
  // order in order to get enough space.
  int needed_space = panel->full_size().height() - panel->TitleOnlyHeight();
  int available_space = MinimizePanelsForSpace(needed_space);

  // If there is still not enough space, try to move up the stack.
  int space_beyond_available = needed_space - available_space;
  if (space_beyond_available > 0) {
    int top_available_space = GetCurrentAvailableTopSpace();
    int move_delta = (space_beyond_available > top_available_space) ?
        top_available_space : space_beyond_available;
    for (Panels::const_iterator iter = panels_.begin();
         iter != panels_.end(); iter++) {
      Panel* current_panel = *iter;
      gfx::Rect bounds = current_panel->GetBounds();
      bounds.set_y(bounds.y() - move_delta);
      current_panel->SetPanelBounds(bounds);
    }
    available_space += move_delta;
  }

  // If there is still not enough space, shrink the restored height to make it
  // fit at the last resort. Note that the restored height cannot be shrunk less
  // than the minimum panel height. If this is the case, we will just let it
  // expand beyond the screen boundary.
  space_beyond_available = needed_space - available_space;
  if (space_beyond_available > 0) {
    gfx::Size full_size = panel->full_size();
    int reduced_height = full_size.height() - space_beyond_available;
    if (reduced_height < panel::kPanelMinHeight)
      reduced_height = panel::kPanelMinHeight;
    full_size.set_height(reduced_height);
    panel->set_full_size(full_size);
  }

  panel->SetExpansionState(Panel::EXPANDED);
}

void StackedPanelCollection::MinimizeAll() {
  // Set minimizing_all_ to prevent deactivation of each panel when it
  // is minimized. See comments in OnPanelExpansionStateChanged.
  base::AutoReset<bool> pin(&minimizing_all_, true);
  Panel* minimized_active_panel = NULL;
  for (Panels::const_iterator iter = panels_.begin();
       iter != panels_.end(); ++iter) {
    if ((*iter)->IsActive())
      minimized_active_panel = *iter;
    MinimizePanel(*iter);
  }

  // When a single panel is minimized, it is deactivated to ensure that
  // a minimized panel does not have focus. However, when minimizing all,
  // the deactivation is only done once after all panels are minimized,
  // rather than per minimized panel, both for efficiency and to avoid
  // temporary activations of random not-yet-minimized panels.
  if (minimized_active_panel) {
    minimized_active_panel->Deactivate();
    // Layout will be refreshed in response to (de)activation notification.
  }
}

void StackedPanelCollection::RestoreAll(Panel* panel_clicked) {
  // Expand the panel being clicked first. This is to make sure at least one
  // panel that is clicked by the user will be expanded.
  RestorePanel(panel_clicked);

  // Try to expand all other panels starting from the most recently active
  // panel.
  for (Panels::const_iterator iter = most_recently_active_panels_.begin();
       iter != most_recently_active_panels_.end(); ++iter) {
    // If the stack already extends to both top and bottom of the work area,
    // stop now since we cannot fit any more expanded panels.
    if (GetCurrentAvailableTopSpace() == 0 &&
        GetCurrentAvailableBottomSpace() == 0) {
      break;
    }

    Panel* panel = *iter;
    if (panel != panel_clicked)
      RestorePanel(panel);
  }
}

void StackedPanelCollection::OnMinimizeButtonClicked(
    Panel* panel, panel::ClickModifier modifier) {
  // The minimize button is only present in the top panel.
  DCHECK_EQ(top_panel(), panel);

  primary_stack_window_->Minimize();
}

void StackedPanelCollection::OnRestoreButtonClicked(
    Panel* panel, panel::ClickModifier modifier) {
  NOTREACHED();
}

bool StackedPanelCollection::CanShowMinimizeButton(const Panel* panel) const {
  // Only the top panel in the stack shows the minimize button.
  return PanelManager::CanUseSystemMinimize() && panel == top_panel();
}

bool StackedPanelCollection::CanShowRestoreButton(const Panel* panel) const {
  return false;
}

bool StackedPanelCollection::IsPanelMinimized(const Panel* panel) const {
  return panel->expansion_state() != Panel::EXPANDED;
}

bool StackedPanelCollection::UsesAlwaysOnTopPanels() const {
  return false;
}

void StackedPanelCollection::SavePanelPlacement(Panel* panel) {
  DCHECK(!saved_panel_placement_.panel);
  saved_panel_placement_.panel = panel;

  if (top_panel() != panel)
    saved_panel_placement_.top_panel = top_panel();
  else
    saved_panel_placement_.position = panel->GetBounds().origin();

  saved_panel_placement_.top_panel = top_panel() != panel ? top_panel() : NULL;
}

void StackedPanelCollection::RestorePanelToSavedPlacement() {
  DCHECK(saved_panel_placement_.panel);

  if (saved_panel_placement_.top_panel) {
    // Restore the top panel if it has been moved out of the stack. This could
    // happen when there're 2 panels in the stack and the bottom panel is being
    // dragged out of the stack and thus cause both panels become detached.
    if (saved_panel_placement_.top_panel->stack() != this) {
      DCHECK_EQ(PanelCollection::DETACHED,
                saved_panel_placement_.top_panel->collection()->type());
      panel_manager_->MovePanelToCollection(
          saved_panel_placement_.top_panel,
          this,
          static_cast<PanelCollection::PositioningMask>(
              PanelCollection::TOP_POSITION |
              PanelCollection::NO_LAYOUT_REFRESH));
    }
    RefreshLayout();
  } else {
    // Restore the position when the top panel is being dragged.
    DCHECK_EQ(top_panel(), saved_panel_placement_.panel);
    RefreshLayoutWithTopPanelStartingAt(saved_panel_placement_.position,
                                        top_panel()->GetBounds().width());
  }

  DiscardSavedPanelPlacement();
}

void StackedPanelCollection::DiscardSavedPanelPlacement() {
  DCHECK(saved_panel_placement_.panel);
  saved_panel_placement_.panel = NULL;
  saved_panel_placement_.top_panel = NULL;
}

panel::Resizability StackedPanelCollection::GetPanelResizability(
    const Panel* panel) const {
  // The panel in the stack can be resized by the following rules:
  // * If collapsed, it can only be resized by its left or right edge.
  // * Otherwise, it can be resized by its left or right edge plus:
  //   % top edge and corners, if it is at the top;
  //   % bottom edge, if it is not at the bottom.
  //   % bottom edge and corners, if it is at the bottom.
  panel::Resizability resizability = static_cast<panel::Resizability>(
      panel::RESIZABLE_LEFT | panel::RESIZABLE_RIGHT);
  if (panel->IsMinimized())
    return resizability;
  if (panel == top_panel()) {
    resizability = static_cast<panel::Resizability>(resizability |
        panel::RESIZABLE_TOP | panel::RESIZABLE_TOP_LEFT |
        panel::RESIZABLE_TOP_RIGHT);
  }
  if (panel == bottom_panel()) {
    resizability = static_cast<panel::Resizability>(resizability |
        panel::RESIZABLE_BOTTOM | panel::RESIZABLE_BOTTOM_LEFT |
        panel::RESIZABLE_BOTTOM_RIGHT);
  } else {
    resizability = static_cast<panel::Resizability>(resizability |
        panel::RESIZABLE_BOTTOM);
  }
  return resizability;
}

void StackedPanelCollection::OnPanelResizedByMouse(
    Panel* resized_panel, const gfx::Rect& new_bounds) {
  resized_panel->set_full_size(new_bounds.size());

  DCHECK(!secondary_stack_window_);
  primary_stack_window_->BeginBatchUpdatePanelBounds(false);

  // The delta x and width can be computed from the difference between
  // the panel being resized and any other panel.
  Panel* other_panel = resized_panel == top_panel() ? bottom_panel()
                                                    : top_panel();
  gfx::Rect other_bounds = other_panel->GetBounds();
  int delta_x = new_bounds.x() - other_bounds.x();
  int delta_width = new_bounds.width() - other_bounds.width();

  gfx::Rect previous_bounds;
  bool resized_panel_found = false;
  bool panel_below_resized_panel_updated = false;
  for (Panels::const_iterator iter = panels_.begin();
       iter != panels_.end(); iter++) {
    Panel* panel = *iter;
    if (panel == resized_panel) {
      // |new_bounds| should be used since the panel bounds have not been
      // updated yet.
      previous_bounds = new_bounds;
      resized_panel_found = true;
      primary_stack_window_->AddPanelBoundsForBatchUpdate(panel, new_bounds);
      continue;
    }

    gfx::Rect bounds = panel->GetBounds();
    bounds.set_x(bounds.x() + delta_x);
    bounds.set_width(bounds.width() + delta_width);

    // If the panel below the panel being resized is expanded, update its
    // height to offset the height change of the panel being resized.
    // For example, the stack has P1 and P2 (from top to bottom). P1's height
    // is 100 and P2's height is 120. If P1's bottom increases by 10, P2's
    // height needs to shrink by 10.
    if (resized_panel_found) {
      if (!panel_below_resized_panel_updated && !panel->IsMinimized()) {
        int old_bottom = bounds.bottom();
        bounds.set_y(previous_bounds.bottom());
        bounds.set_height(old_bottom - bounds.y());
      } else {
        bounds.set_y(previous_bounds.bottom());
      }
      panel_below_resized_panel_updated = true;
    }

    if (!panel->IsMinimized())
      panel->set_full_size(bounds.size());

    primary_stack_window_->AddPanelBoundsForBatchUpdate(panel, bounds);
    previous_bounds = bounds;
  }

  primary_stack_window_->EndBatchUpdatePanelBounds();
}

bool StackedPanelCollection::HasPanel(Panel* panel) const {
  return std::find(panels_.begin(), panels_.end(), panel) != panels_.end();
}

void StackedPanelCollection::UpdatePanelOnCollectionChange(Panel* panel) {
  panel->set_attention_mode(
      static_cast<Panel::AttentionMode>(Panel::USE_PANEL_ATTENTION |
                                        Panel::USE_SYSTEM_ATTENTION));
  panel->ShowShadow(false);
  panel->UpdateMinimizeRestoreButtonVisibility();
  UpdatePanelCornerStyle(panel);
}

void StackedPanelCollection::OnPanelExpansionStateChanged(Panel* panel) {
  DCHECK_NE(Panel::MINIMIZED, panel->expansion_state());

  // Ensure minimized panel does not get the focus. If minimizing all,
  // the active panel will be deactivated once when all panels are minimized
  // rather than per minimized panel.
  if (panel->expansion_state() != Panel::EXPANDED && !minimizing_all_ &&
      panel->IsActive()) {
    panel->Deactivate();
  }

  // The bounds change per expansion state will be done in RefreshLayout.
  RefreshLayout();
}

void StackedPanelCollection::OnPanelActiveStateChanged(Panel* panel) {
  if (!panel->IsActive())
    return;

  // Move the panel to the front if not yet.
  Panels::iterator iter = std::find(most_recently_active_panels_.begin(),
      most_recently_active_panels_.end(), panel);
  DCHECK(iter != most_recently_active_panels_.end());
  if (iter != most_recently_active_panels_.begin()) {
    most_recently_active_panels_.erase(iter);
    most_recently_active_panels_.push_front(panel);
  }

  GetStackWindowForPanel(panel)->OnPanelActivated(panel);
}

gfx::Rect StackedPanelCollection::GetInitialPanelBounds(
      const gfx::Rect& requested_bounds) const {
  DCHECK(!panels_.empty());
  gfx::Rect bottom_panel_bounds = bottom_panel()->GetBounds();
  return gfx::Rect(bottom_panel_bounds.x(),
                   bottom_panel_bounds.bottom(),
                   bottom_panel_bounds.width(),
                   requested_bounds.height());
}

Panel* StackedPanelCollection::GetPanelAbove(Panel* panel) const {
  DCHECK(panel);

  if (panels_.size() < 2)
    return NULL;
  Panels::const_iterator iter = panels_.begin();
  Panel* above_panel = *iter;
  for (; iter != panels_.end(); ++iter) {
    if (*iter == panel)
      return above_panel;
    above_panel = *iter;
  }
  return NULL;
}

Panel* StackedPanelCollection::GetPanelBelow(Panel* panel) const {
  DCHECK(panel);

  if (panels_.size() < 2)
    return NULL;
  Panels::const_iterator iter =
      std::find(panels_.begin(), panels_.end(), panel);
  if (iter == panels_.end())
    return NULL;
  ++iter;
  return iter == panels_.end() ? NULL : *iter;
}

void StackedPanelCollection::MoveAllDraggingPanelsInstantly(
    const gfx::Vector2d& delta_origin) {
  for (Panels::const_iterator iter = panels_.begin();
       iter != panels_.end(); iter++) {
    Panel* panel = *iter;
    if (panel->in_preview_mode()) {
      GetStackWindowForPanel(panel)->MovePanelsBy(delta_origin);
      return;
    }
  }
}

bool StackedPanelCollection::IsMinimized() const {
  return primary_stack_window_->IsMinimized();
}

bool StackedPanelCollection::IsAnimatingPanelBounds(Panel* panel) const {
  return GetStackWindowForPanel(panel)->IsAnimatingPanelBounds();
}

void StackedPanelCollection::UpdatePanelCornerStyle(Panel* panel) {
  panel::CornerStyle corner_style;
  bool at_top = panel == top_panel();
  bool at_bottom = panel == bottom_panel();
  if (at_top && at_bottom)
    corner_style = panel::ALL_ROUNDED;
  else if (at_top)
    corner_style = panel::TOP_ROUNDED;
  else if (at_bottom)
    corner_style = panel::BOTTOM_ROUNDED;
  else
    corner_style = panel::NOT_ROUNDED;
  panel->SetWindowCornerStyle(corner_style);
}

gfx::Rect StackedPanelCollection::GetWorkArea() const {
  if (panels_.empty())
    return panel_manager_->display_settings_provider()->GetPrimaryWorkArea();
  return panel_manager_->display_settings_provider()->GetWorkAreaMatching(
      GetEnclosingBounds());
}

int StackedPanelCollection::GetCurrentAvailableTopSpace() const {
  gfx::Rect work_area = GetWorkArea();
  if (panels_.empty())
    return work_area.height();

  int available_space = top_panel()->GetBounds().y() - work_area.y();
  if (available_space < 0)
    available_space = 0;
  return available_space;
}

int StackedPanelCollection::GetCurrentAvailableBottomSpace() const {
  gfx::Rect work_area = GetWorkArea();
  if (panels_.empty())
    return work_area.height();

  int available_space = work_area.bottom() -
      bottom_panel()->GetBounds().bottom();
  if (available_space < 0)
    available_space = 0;
  return available_space;
}

int StackedPanelCollection::GetMaximiumAvailableBottomSpace() const {
  gfx::Rect work_area = GetWorkArea();
  if (panels_.empty())
    return work_area.height();

  int bottom = top_panel()->GetBounds().y();
  for (Panels::const_iterator iter = panels_.begin();
       iter != panels_.end(); iter++) {
    Panel* panel = *iter;
    // Only the most recently active panel might be active.
    if (iter == panels_.begin() && panel->IsActive())
      bottom += panel->GetBounds().height();
    else
      bottom += panel::kTitlebarHeight;
  }
  int available_space = work_area.bottom() - bottom;
  if (available_space < 0)
    available_space = 0;
  return available_space;
}

NativePanelStackWindow* StackedPanelCollection::GetStackWindowForPanel(
    Panel* panel) const {
  return secondary_stack_window_ && secondary_stack_window_->HasPanel(panel) ?
      secondary_stack_window_ : primary_stack_window_;
}

/* [<][>][^][v][top][bottom][index][help] */