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