1c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten// Use of this source code is governed by a BSD-style license that can be
3c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten// found in the LICENSE file.
4c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten
5c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#include "chrome/browser/ui/panels/panel_manager.h"
6c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten
7c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#include "base/bind.h"
8c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#include "base/command_line.h"
9c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#include "base/logging.h"
10c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#include "base/memory/scoped_ptr.h"
11c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#include "base/message_loop/message_loop.h"
12c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#include "chrome/browser/chrome_notification_types.h"
13c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#include "chrome/browser/ui/panels/detached_panel_collection.h"
14c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#include "chrome/browser/ui/panels/docked_panel_collection.h"
15c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#include "chrome/browser/ui/panels/panel_drag_controller.h"
16c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#include "chrome/browser/ui/panels/panel_mouse_watcher.h"
17c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#include "chrome/browser/ui/panels/panel_resize_controller.h"
18c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#include "chrome/browser/ui/panels/stacked_panel_collection.h"
19c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#include "chrome/common/chrome_switches.h"
207126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten#include "chrome/common/chrome_version_info.h"
21c6853892c94800e72c0bd676d5d2136d48cea76eGlenn Kasten#include "content/public/browser/notification_service.h"
22be2218961ac506ba9c2be0dddb2f74debd283bb8Glenn Kasten#include "content/public/browser/notification_source.h"
23c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#include "ui/base/hit_test.h"
24c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten
25c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#if defined(USE_X11) && !defined(OS_CHROMEOS)
26c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#include "base/environment.h"
27c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#include "base/nix/xdg_util.h"
28c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#include "ui/base/x/x11_util.h"
29c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#endif
30c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten
31c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kastennamespace {
32c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten// Maxmium width of a panel is based on a factor of the working area.
33c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#if defined(OS_CHROMEOS)
34c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten// ChromeOS device screens are relatively small and limiting the width
35c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten// interferes with some apps (e.g. http://crbug.com/111121).
36c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kastenconst double kPanelMaxWidthFactor = 0.80;
37c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#else
387126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenconst double kPanelMaxWidthFactor = 0.35;
39c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten#endif
40c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten
417126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten// Maxmium height of a panel is based on a factor of the working area.
427126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenconst double kPanelMaxHeightFactor = 0.5;
437126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
447126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten// Width to height ratio is used to compute the default width or height
457126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten// when only one value is provided.
467126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenconst double kPanelDefaultWidthToHeightRatio = 1.62;  // golden ratio
477126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
4858432eb9cea995c69b4f905e68b38c1b8216edebGlenn Kasten// The test code could call PanelManager::SetDisplaySettingsProviderForTesting
49c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten// to set this for testing purpose.
50c2b9d79c4b59caff965076f445f5a735a360b084Glenn KastenDisplaySettingsProvider* display_settings_provider_for_testing;
51c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten
52c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten// The following comparers are used by std::list<>::sort to determine which
537126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten// stack or panel we want to seacrh first for adding new panel.
547126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenbool ComparePanelsByPosition(Panel* panel1, Panel* panel2) {
55c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten  gfx::Rect bounds1 = panel1->GetBounds();
56c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten  gfx::Rect bounds2 = panel2->GetBounds();
57c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten
587126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  // When there're ties, the right-most stack will appear first.
59c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten  if (bounds1.x() > bounds2.x())
60c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten    return true;
617126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  if (bounds1.x() < bounds2.x())
627126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    return false;
637126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
647126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  // In the event of another draw, the top-most stack will appear first.
65c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten  return bounds1.y() < bounds2.y();
66c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten}
67c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten
6858432eb9cea995c69b4f905e68b38c1b8216edebGlenn Kastenbool ComparerNumberOfPanelsInStack(StackedPanelCollection* stack1,
69c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten                                   StackedPanelCollection* stack2) {
707126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  // The stack with more panels will appear first.
717126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  int num_panels_in_stack1 = stack1->num_panels();
72c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten  int num_panels_in_stack2 = stack2->num_panels();
73c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten  if (num_panels_in_stack1 > num_panels_in_stack2)
74c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten    return true;
75c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten  if (num_panels_in_stack1 < num_panels_in_stack2)
7658432eb9cea995c69b4f905e68b38c1b8216edebGlenn Kasten    return false;
77c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten
78c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten  DCHECK(num_panels_in_stack1);
79c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten
80c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten  return ComparePanelsByPosition(stack1->top_panel(), stack2->top_panel());
81c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten}
82c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten
837126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenbool CompareDetachedPanels(Panel* panel1, Panel* panel2) {
847126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  return ComparePanelsByPosition(panel1, panel2);
857126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten}
867126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
877126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten}  // namespace
887126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
8958432eb9cea995c69b4f905e68b38c1b8216edebGlenn Kasten// static
907126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenbool PanelManager::shorten_time_intervals_ = false;
917126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
927126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten// static
937126c25d7c037e5086216cf540ecf40779c3585aGlenn KastenPanelManager* PanelManager::GetInstance() {
947126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  static base::LazyInstance<PanelManager> instance = LAZY_INSTANCE_INITIALIZER;
957126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  return instance.Pointer();
967126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten}
977126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
987126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten// static
9958432eb9cea995c69b4f905e68b38c1b8216edebGlenn Kastenvoid PanelManager::SetDisplaySettingsProviderForTesting(
1007126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    DisplaySettingsProvider* provider) {
1017126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  display_settings_provider_for_testing = provider;
1027126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten}
1037126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
1047126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten// static
1057126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenbool PanelManager::ShouldUsePanels(const std::string& extension_id) {
1067126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten#if defined(USE_X11) && !defined(OS_CHROMEOS)
1077126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  // If --enable-panels is on, always use panels on Linux.
1087126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnablePanels))
1097126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    return true;
1107126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
1117126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  // Otherwise, panels are only supported on tested window managers.
1127126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  ui::WindowManagerName wm_type = ui::GuessWindowManager();
1137126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  if (wm_type != ui::WM_COMPIZ &&
1147126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten      wm_type != ui::WM_ICE_WM &&
1157126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten      wm_type != ui::WM_KWIN &&
1167126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten      wm_type != ui::WM_METACITY &&
1177126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten      wm_type != ui::WM_MUFFIN &&
1187126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten      wm_type != ui::WM_MUTTER &&
1197126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten      wm_type != ui::WM_XFWM4) {
1207126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    return false;
1217126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  }
1227126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten#endif  // USE_X11 && !OS_CHROMEOS
1237126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
1247126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
1257126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  if (channel == chrome::VersionInfo::CHANNEL_STABLE ||
1267126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten      channel == chrome::VersionInfo::CHANNEL_BETA) {
1277126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    return CommandLine::ForCurrentProcess()->HasSwitch(
1287126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        switches::kEnablePanels) ||
1297126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        extension_id == std::string("nckgahadagoaajjgafhacjanaoiihapd") ||
1307126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        extension_id == std::string("ljclpkphhpbpinifbeabbhlfddcpfdde") ||
1317126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        extension_id == std::string("ppleadejekpmccmnpjdimmlfljlkdfej") ||
1327126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten        extension_id == std::string("eggnbpckecmjlblplehfpjjdhhidfdoj");
1337126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  }
1347126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
1357126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  return true;
1367126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten}
1377126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
1387126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten// static
13958432eb9cea995c69b4f905e68b38c1b8216edebGlenn Kastenbool PanelManager::IsPanelStackingEnabled() {
1407126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  // Stacked panel mode is not supported in linux-aura.
1417126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten#if defined(OS_LINUX)
1427126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  return false;
1437126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten#else
1447126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  return true;
1457126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten#endif
1467126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten}
1477126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
1487126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten// static
1497126c25d7c037e5086216cf540ecf40779c3585aGlenn Kastenbool PanelManager::CanUseSystemMinimize() {
1507126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten#if defined(USE_X11) && !defined(OS_CHROMEOS)
1517126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  static base::nix::DesktopEnvironment desktop_env =
1527126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten      base::nix::DESKTOP_ENVIRONMENT_OTHER;
1537126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  if (desktop_env == base::nix::DESKTOP_ENVIRONMENT_OTHER) {
1547126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    scoped_ptr<base::Environment> env(base::Environment::Create());
1557126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    desktop_env = base::nix::GetDesktopEnvironment(env.get());
1567126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  }
1577126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  return desktop_env != base::nix::DESKTOP_ENVIRONMENT_UNITY;
1587126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten#else
1594076e5009bfe70bc06a78f5aafec77af6c03201dGlenn Kasten  return true;
1604076e5009bfe70bc06a78f5aafec77af6c03201dGlenn Kasten#endif
161e047d27324083628b51bad0661a184f6cb96c56dGlenn Kasten}
1627126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
1637126c25d7c037e5086216cf540ecf40779c3585aGlenn KastenPanelManager::PanelManager()
1647126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    : panel_mouse_watcher_(PanelMouseWatcher::Create()),
1657126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten      auto_sizing_enabled_(true) {
1667126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  // DisplaySettingsProvider should be created before the creation of
1677126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  // collections since some collection might depend on it.
1687126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  if (display_settings_provider_for_testing)
1697126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    display_settings_provider_.reset(display_settings_provider_for_testing);
1707126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  else
1717126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten    display_settings_provider_.reset(DisplaySettingsProvider::Create());
1727126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  display_settings_provider_->AddDisplayObserver(this);
1737126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
1747126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  detached_collection_.reset(new DetachedPanelCollection(this));
1757126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  docked_collection_.reset(new DockedPanelCollection(this));
1767126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  drag_controller_.reset(new PanelDragController(this));
1777126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten  resize_controller_.reset(new PanelResizeController(this));
1787126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten}
1797126c25d7c037e5086216cf540ecf40779c3585aGlenn Kasten
180c2b9d79c4b59caff965076f445f5a735a360b084Glenn KastenPanelManager::~PanelManager() {
181c2b9d79c4b59caff965076f445f5a735a360b084Glenn Kasten  display_settings_provider_->RemoveDisplayObserver(this);
182
183  // Docked collection should be disposed explicitly before
184  // DisplaySettingsProvider is gone since docked collection needs to remove
185  // the observer from DisplaySettingsProvider.
186  docked_collection_.reset();
187}
188
189gfx::Point PanelManager::GetDefaultDetachedPanelOrigin() {
190  return detached_collection_->GetDefaultPanelOrigin();
191}
192
193void PanelManager::OnDisplayChanged() {
194  docked_collection_->OnDisplayChanged();
195  detached_collection_->OnDisplayChanged();
196  for (Stacks::const_iterator iter = stacks_.begin();
197       iter != stacks_.end(); iter++)
198    (*iter)->OnDisplayChanged();
199}
200
201void PanelManager::OnFullScreenModeChanged(bool is_full_screen) {
202  std::vector<Panel*> all_panels = panels();
203  for (std::vector<Panel*>::const_iterator iter = all_panels.begin();
204       iter != all_panels.end(); ++iter) {
205    (*iter)->FullScreenModeChanged(is_full_screen);
206  }
207}
208
209int PanelManager::GetMaxPanelWidth(const gfx::Rect& work_area) const {
210  return static_cast<int>(work_area.width() * kPanelMaxWidthFactor);
211}
212
213int PanelManager::GetMaxPanelHeight(const gfx::Rect& work_area) const {
214  return static_cast<int>(work_area.height() * kPanelMaxHeightFactor);
215}
216
217Panel* PanelManager::CreatePanel(const std::string& app_name,
218                                 Profile* profile,
219                                 const GURL& url,
220                                 const gfx::Rect& requested_bounds,
221                                 CreateMode mode) {
222  // Need to sync the display area if no panel is present. This is because:
223  // 1) Display area is not initialized until first panel is created.
224  // 2) On windows, display settings notification is tied to a window. When
225  //    display settings are changed at the time that no panel exists, we do
226  //    not receive any notification.
227  if (num_panels() == 0) {
228    display_settings_provider_->OnDisplaySettingsChanged();
229    display_settings_provider_->AddFullScreenObserver(this);
230  }
231
232  // Compute initial bounds for the panel.
233  int width = requested_bounds.width();
234  int height = requested_bounds.height();
235  if (width == 0)
236    width = height * kPanelDefaultWidthToHeightRatio;
237  else if (height == 0)
238    height = width / kPanelDefaultWidthToHeightRatio;
239
240  gfx::Rect work_area =
241      display_settings_provider_->GetWorkAreaMatching(requested_bounds);
242  gfx::Size min_size(panel::kPanelMinWidth, panel::kPanelMinHeight);
243  gfx::Size max_size(GetMaxPanelWidth(work_area), GetMaxPanelHeight(work_area));
244  if (width < min_size.width())
245    width = min_size.width();
246  else if (width > max_size.width())
247    width = max_size.width();
248
249  if (height < min_size.height())
250    height = min_size.height();
251  else if (height > max_size.height())
252    height = max_size.height();
253
254  // Create the panel.
255  Panel* panel = new Panel(profile, app_name, min_size, max_size);
256
257  // Find the appropriate panel collection to hold the new panel.
258  gfx::Rect adjusted_requested_bounds(
259      requested_bounds.x(), requested_bounds.y(), width, height);
260  PanelCollection::PositioningMask positioning_mask;
261  PanelCollection* collection = GetCollectionForNewPanel(
262      panel, adjusted_requested_bounds, mode, &positioning_mask);
263
264  // Let the panel collection decide the initial bounds.
265  gfx::Rect bounds = collection->GetInitialPanelBounds(
266      adjusted_requested_bounds);
267  bounds.AdjustToFit(work_area);
268
269  panel->Initialize(url, bounds, collection->UsesAlwaysOnTopPanels());
270
271  // Auto resizable feature is enabled only if no initial size is requested.
272  if (auto_sizing_enabled() && requested_bounds.width() == 0 &&
273      requested_bounds.height() == 0) {
274    panel->SetAutoResizable(true);
275  }
276
277  // Add the panel to the panel collection.
278  collection->AddPanel(panel, positioning_mask);
279  collection->UpdatePanelOnCollectionChange(panel);
280
281  return panel;
282}
283
284PanelCollection* PanelManager::GetCollectionForNewPanel(
285    Panel* new_panel,
286    const gfx::Rect& bounds,
287    CreateMode mode,
288    PanelCollection::PositioningMask* positioning_mask) {
289  if (mode == CREATE_AS_DOCKED) {
290    // Delay layout refreshes in case multiple panels are created within
291    // a short time of one another or the focus changes shortly after panel
292    // is created to avoid excessive screen redraws.
293    *positioning_mask = PanelCollection::DELAY_LAYOUT_REFRESH;
294    return docked_collection_.get();
295  }
296
297  DCHECK_EQ(CREATE_AS_DETACHED, mode);
298  *positioning_mask = PanelCollection::DEFAULT_POSITION;
299
300  // If the stacking support is not enabled, new panel will still be created as
301  // detached.
302  if (!IsPanelStackingEnabled())
303    return detached_collection_.get();
304
305  // If there're stacks, try to find a stack that can fit new panel.
306  if (!stacks_.empty()) {
307    // Perform the search as:
308    // 1) Search from the stack with more panels to the stack with least panels.
309    // 2) Amongs the stacks with same number of panels, search from the right-
310    //    most stack to the left-most stack.
311    // 3) Among the stack with same number of panels and same x position,
312    //    search from the top-most stack to the bottom-most stack.
313    // 4) If there is not enough space to fit new panel even with all inactive
314    //    panels being collapsed, move to next stack.
315    stacks_.sort(ComparerNumberOfPanelsInStack);
316    for (Stacks::const_iterator iter = stacks_.begin();
317         iter != stacks_.end(); iter++) {
318      StackedPanelCollection* stack = *iter;
319
320      // Do not add to other stack that is from differnt extension or profile.
321      // Note that the check is based on bottom panel.
322      Panel* panel = stack->bottom_panel();
323      if (panel->profile() != new_panel->profile() ||
324          panel->extension_id() != new_panel->extension_id())
325        continue;
326
327      // Do not add to the stack that is minimized by the system.
328      if (stack->IsMinimized())
329        continue;
330
331      // Do not stack with the panel that is not shown in current virtual
332      // desktop.
333      if (!panel->IsShownOnActiveDesktop())
334        continue;
335
336      if (bounds.height() <= stack->GetMaximiumAvailableBottomSpace()) {
337        *positioning_mask = static_cast<PanelCollection::PositioningMask>(
338            *positioning_mask | PanelCollection::COLLAPSE_TO_FIT);
339        return stack;
340      }
341    }
342  }
343
344  // Then try to find a detached panel to which new panel can stack.
345  if (detached_collection_->num_panels()) {
346    // Perform the search as:
347    // 1) Search from the right-most detached panel to the left-most detached
348    //    panel.
349    // 2) Among the detached panels with same x position, search from the
350    //    top-most detached panel to the bottom-most deatched panel.
351    // 3) If there is not enough space beneath the detached panel, even by
352    //    collapsing it if it is inactive, to fit new panel, move to next
353    //    detached panel.
354    detached_collection_->SortPanels(CompareDetachedPanels);
355
356    for (DetachedPanelCollection::Panels::const_iterator iter =
357             detached_collection_->panels().begin();
358         iter != detached_collection_->panels().end(); ++iter) {
359      Panel* panel = *iter;
360
361      // Do not stack with other panel that is from differnt extension or
362      // profile.
363      if (panel->profile() != new_panel->profile() ||
364          panel->extension_id() != new_panel->extension_id())
365        continue;
366
367      // Do not stack with the panel that is minimized by the system.
368      if (panel->IsMinimizedBySystem())
369        continue;
370
371      // Do not stack with the panel that is not shown in the active desktop.
372      if (!panel->IsShownOnActiveDesktop())
373        continue;
374
375      gfx::Rect work_area =
376          display_settings_provider_->GetWorkAreaMatching(panel->GetBounds());
377      int max_available_space =
378          work_area.bottom() - panel->GetBounds().y() -
379          (panel->IsActive() ? panel->GetBounds().height()
380                             : panel::kTitlebarHeight);
381      if (bounds.height() <= max_available_space) {
382        StackedPanelCollection* new_stack = CreateStack();
383        MovePanelToCollection(panel,
384                              new_stack,
385                              PanelCollection::DEFAULT_POSITION);
386        *positioning_mask = static_cast<PanelCollection::PositioningMask>(
387            *positioning_mask | PanelCollection::COLLAPSE_TO_FIT);
388        return new_stack;
389      }
390    }
391  }
392
393  return detached_collection_.get();
394}
395
396void PanelManager::OnPanelClosed(Panel* panel) {
397  if (num_panels() == 1) {
398    display_settings_provider_->RemoveFullScreenObserver(this);
399  }
400
401  drag_controller_->OnPanelClosed(panel);
402  resize_controller_->OnPanelClosed(panel);
403
404  // Note that we need to keep track of panel's collection since it will be
405  // gone once RemovePanel is called.
406  PanelCollection* collection = panel->collection();
407  collection->RemovePanel(panel, PanelCollection::PANEL_CLOSED);
408
409  // If only one panel is left in the stack, move it out of the stack.
410  // Also make sure that this detached panel will be expanded if not yet.
411  if (collection->type() == PanelCollection::STACKED) {
412    StackedPanelCollection* stack =
413        static_cast<StackedPanelCollection*>(collection);
414    DCHECK_GE(stack->num_panels(), 1);
415    if (stack->num_panels() == 1) {
416      Panel* top_panel = stack->top_panel();
417      MovePanelToCollection(top_panel,
418                            detached_collection(),
419                            PanelCollection::DEFAULT_POSITION);
420      if (top_panel->expansion_state() != Panel::EXPANDED)
421        top_panel->SetExpansionState(Panel::EXPANDED);
422      RemoveStack(stack);
423    }
424  }
425
426  content::NotificationService::current()->Notify(
427      chrome::NOTIFICATION_PANEL_CLOSED,
428      content::Source<Panel>(panel),
429      content::NotificationService::NoDetails());
430}
431
432StackedPanelCollection* PanelManager::CreateStack() {
433  StackedPanelCollection* stack = new StackedPanelCollection(this);
434  stacks_.push_back(stack);
435  return stack;
436}
437
438void PanelManager::RemoveStack(StackedPanelCollection* stack) {
439  DCHECK_EQ(0, stack->num_panels());
440  stacks_.remove(stack);
441  stack->CloseAll();
442  delete stack;
443}
444
445void PanelManager::StartDragging(Panel* panel,
446                                 const gfx::Point& mouse_location) {
447  drag_controller_->StartDragging(panel, mouse_location);
448}
449
450void PanelManager::Drag(const gfx::Point& mouse_location) {
451  drag_controller_->Drag(mouse_location);
452}
453
454void PanelManager::EndDragging(bool cancelled) {
455  drag_controller_->EndDragging(cancelled);
456}
457
458void PanelManager::StartResizingByMouse(Panel* panel,
459                                        const gfx::Point& mouse_location,
460                                        int component) {
461  if (panel->CanResizeByMouse() != panel::NOT_RESIZABLE &&
462      component != HTNOWHERE) {
463    resize_controller_->StartResizing(panel, mouse_location, component);
464  }
465}
466
467void PanelManager::ResizeByMouse(const gfx::Point& mouse_location) {
468  if (resize_controller_->IsResizing())
469    resize_controller_->Resize(mouse_location);
470}
471
472void PanelManager::EndResizingByMouse(bool cancelled) {
473  if (resize_controller_->IsResizing()) {
474    Panel* resized_panel = resize_controller_->EndResizing(cancelled);
475    if (!cancelled && resized_panel->collection())
476      resized_panel->collection()->RefreshLayout();
477  }
478}
479
480void PanelManager::OnPanelExpansionStateChanged(Panel* panel) {
481  panel->collection()->OnPanelExpansionStateChanged(panel);
482}
483
484void PanelManager::MovePanelToCollection(
485    Panel* panel,
486    PanelCollection* target_collection,
487    PanelCollection::PositioningMask positioning_mask) {
488  DCHECK(panel);
489  PanelCollection* current_collection = panel->collection();
490  DCHECK(current_collection);
491  DCHECK_NE(current_collection, target_collection);
492  current_collection->RemovePanel(panel,
493                                  PanelCollection::PANEL_CHANGED_COLLECTION);
494
495  target_collection->AddPanel(panel, positioning_mask);
496  target_collection->UpdatePanelOnCollectionChange(panel);
497  panel->SetAlwaysOnTop(target_collection->UsesAlwaysOnTopPanels());
498}
499
500bool PanelManager::ShouldBringUpTitlebars(int mouse_x, int mouse_y) const {
501  return docked_collection_->ShouldBringUpTitlebars(mouse_x, mouse_y);
502}
503
504void PanelManager::BringUpOrDownTitlebars(bool bring_up) {
505  docked_collection_->BringUpOrDownTitlebars(bring_up);
506}
507
508void PanelManager::CloseAll() {
509  DCHECK(!drag_controller_->is_dragging());
510
511  detached_collection_->CloseAll();
512  docked_collection_->CloseAll();
513}
514
515int PanelManager::num_panels() const {
516  int count = detached_collection_->num_panels() +
517              docked_collection_->num_panels();
518  for (Stacks::const_iterator iter = stacks_.begin();
519       iter != stacks_.end(); iter++)
520    count += (*iter)->num_panels();
521  return count;
522}
523
524std::vector<Panel*> PanelManager::panels() const {
525  std::vector<Panel*> panels;
526  for (DetachedPanelCollection::Panels::const_iterator iter =
527           detached_collection_->panels().begin();
528       iter != detached_collection_->panels().end(); ++iter)
529    panels.push_back(*iter);
530  for (DockedPanelCollection::Panels::const_iterator iter =
531           docked_collection_->panels().begin();
532       iter != docked_collection_->panels().end(); ++iter)
533    panels.push_back(*iter);
534  for (Stacks::const_iterator stack_iter = stacks_.begin();
535       stack_iter != stacks_.end(); stack_iter++) {
536    for (StackedPanelCollection::Panels::const_iterator iter =
537             (*stack_iter)->panels().begin();
538         iter != (*stack_iter)->panels().end(); ++iter) {
539      panels.push_back(*iter);
540    }
541  }
542  return panels;
543}
544
545std::vector<Panel*> PanelManager::GetDetachedAndStackedPanels() const {
546  std::vector<Panel*> panels;
547  for (DetachedPanelCollection::Panels::const_iterator iter =
548           detached_collection_->panels().begin();
549       iter != detached_collection_->panels().end(); ++iter)
550    panels.push_back(*iter);
551  for (Stacks::const_iterator stack_iter = stacks_.begin();
552       stack_iter != stacks_.end(); stack_iter++) {
553    for (StackedPanelCollection::Panels::const_iterator iter =
554             (*stack_iter)->panels().begin();
555         iter != (*stack_iter)->panels().end(); ++iter) {
556      panels.push_back(*iter);
557    }
558  }
559  return panels;
560}
561
562void PanelManager::SetMouseWatcher(PanelMouseWatcher* watcher) {
563  panel_mouse_watcher_.reset(watcher);
564}
565
566void PanelManager::OnPanelAnimationEnded(Panel* panel) {
567  content::NotificationService::current()->Notify(
568      chrome::NOTIFICATION_PANEL_BOUNDS_ANIMATIONS_FINISHED,
569      content::Source<Panel>(panel),
570      content::NotificationService::NoDetails());
571}
572