Revert of Add button to add new FSP services to Files app. (patchset #8 id:140001...
[chromium-blink-merge.git] / chrome / browser / ui / panels / panel_manager.cc
blobd3c5f06d45c0f776ac262e083e077009071b80a0
1 // Copyright (c) 2012 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.
5 #include "chrome/browser/ui/panels/panel_manager.h"
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/message_loop/message_loop.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/ui/panels/detached_panel_collection.h"
14 #include "chrome/browser/ui/panels/docked_panel_collection.h"
15 #include "chrome/browser/ui/panels/panel_drag_controller.h"
16 #include "chrome/browser/ui/panels/panel_mouse_watcher.h"
17 #include "chrome/browser/ui/panels/panel_resize_controller.h"
18 #include "chrome/browser/ui/panels/stacked_panel_collection.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/common/chrome_version_info.h"
21 #include "content/public/browser/notification_service.h"
22 #include "content/public/browser/notification_source.h"
23 #include "ui/base/hit_test.h"
25 #if defined(USE_X11) && !defined(OS_CHROMEOS)
26 #include "base/environment.h"
27 #include "base/nix/xdg_util.h"
28 #include "ui/base/x/x11_util.h"
29 #endif
31 namespace {
32 // Maxmium width of a panel is based on a factor of the working area.
33 #if defined(OS_CHROMEOS)
34 // ChromeOS device screens are relatively small and limiting the width
35 // interferes with some apps (e.g. http://crbug.com/111121).
36 const double kPanelMaxWidthFactor = 0.80;
37 #else
38 const double kPanelMaxWidthFactor = 0.35;
39 #endif
41 // Maxmium height of a panel is based on a factor of the working area.
42 const double kPanelMaxHeightFactor = 0.5;
44 // Width to height ratio is used to compute the default width or height
45 // when only one value is provided.
46 const double kPanelDefaultWidthToHeightRatio = 1.62; // golden ratio
48 // The test code could call PanelManager::SetDisplaySettingsProviderForTesting
49 // to set this for testing purpose.
50 DisplaySettingsProvider* display_settings_provider_for_testing;
52 // The following comparers are used by std::list<>::sort to determine which
53 // stack or panel we want to seacrh first for adding new panel.
54 bool ComparePanelsByPosition(Panel* panel1, Panel* panel2) {
55 gfx::Rect bounds1 = panel1->GetBounds();
56 gfx::Rect bounds2 = panel2->GetBounds();
58 // When there're ties, the right-most stack will appear first.
59 if (bounds1.x() > bounds2.x())
60 return true;
61 if (bounds1.x() < bounds2.x())
62 return false;
64 // In the event of another draw, the top-most stack will appear first.
65 return bounds1.y() < bounds2.y();
68 bool ComparerNumberOfPanelsInStack(StackedPanelCollection* stack1,
69 StackedPanelCollection* stack2) {
70 // The stack with more panels will appear first.
71 int num_panels_in_stack1 = stack1->num_panels();
72 int num_panels_in_stack2 = stack2->num_panels();
73 if (num_panels_in_stack1 > num_panels_in_stack2)
74 return true;
75 if (num_panels_in_stack1 < num_panels_in_stack2)
76 return false;
78 DCHECK(num_panels_in_stack1);
80 return ComparePanelsByPosition(stack1->top_panel(), stack2->top_panel());
83 bool CompareDetachedPanels(Panel* panel1, Panel* panel2) {
84 return ComparePanelsByPosition(panel1, panel2);
87 } // namespace
89 // static
90 bool PanelManager::shorten_time_intervals_ = false;
92 // static
93 PanelManager* PanelManager::GetInstance() {
94 static base::LazyInstance<PanelManager> instance = LAZY_INSTANCE_INITIALIZER;
95 return instance.Pointer();
98 // static
99 void PanelManager::SetDisplaySettingsProviderForTesting(
100 DisplaySettingsProvider* provider) {
101 display_settings_provider_for_testing = provider;
104 // static
105 bool PanelManager::ShouldUsePanels(const std::string& extension_id) {
106 #if defined(USE_X11) && !defined(OS_CHROMEOS)
107 // If --enable-panels is on, always use panels on Linux.
108 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
109 switches::kEnablePanels))
110 return true;
112 // Otherwise, panels are only supported on tested window managers.
113 ui::WindowManagerName wm_type = ui::GuessWindowManager();
114 if (wm_type != ui::WM_COMPIZ &&
115 wm_type != ui::WM_ICE_WM &&
116 wm_type != ui::WM_KWIN &&
117 wm_type != ui::WM_METACITY &&
118 wm_type != ui::WM_MUFFIN &&
119 wm_type != ui::WM_MUTTER &&
120 wm_type != ui::WM_XFWM4) {
121 return false;
123 #endif // USE_X11 && !OS_CHROMEOS
125 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
126 if (channel == chrome::VersionInfo::CHANNEL_STABLE ||
127 channel == chrome::VersionInfo::CHANNEL_BETA) {
128 return base::CommandLine::ForCurrentProcess()->HasSwitch(
129 switches::kEnablePanels) ||
130 extension_id == std::string("nckgahadagoaajjgafhacjanaoiihapd") ||
131 extension_id == std::string("ljclpkphhpbpinifbeabbhlfddcpfdde") ||
132 extension_id == std::string("ppleadejekpmccmnpjdimmlfljlkdfej") ||
133 extension_id == std::string("eggnbpckecmjlblplehfpjjdhhidfdoj");
136 return true;
139 // static
140 bool PanelManager::IsPanelStackingEnabled() {
141 // Stacked panel mode is not supported in linux-aura.
142 #if defined(OS_LINUX)
143 return false;
144 #else
145 return true;
146 #endif
149 // static
150 bool PanelManager::CanUseSystemMinimize() {
151 #if defined(USE_X11) && !defined(OS_CHROMEOS)
152 static base::nix::DesktopEnvironment desktop_env =
153 base::nix::DESKTOP_ENVIRONMENT_OTHER;
154 if (desktop_env == base::nix::DESKTOP_ENVIRONMENT_OTHER) {
155 scoped_ptr<base::Environment> env(base::Environment::Create());
156 desktop_env = base::nix::GetDesktopEnvironment(env.get());
158 return desktop_env != base::nix::DESKTOP_ENVIRONMENT_UNITY;
159 #else
160 return true;
161 #endif
164 PanelManager::PanelManager()
165 : panel_mouse_watcher_(PanelMouseWatcher::Create()),
166 auto_sizing_enabled_(true) {
167 // DisplaySettingsProvider should be created before the creation of
168 // collections since some collection might depend on it.
169 if (display_settings_provider_for_testing)
170 display_settings_provider_.reset(display_settings_provider_for_testing);
171 else
172 display_settings_provider_.reset(DisplaySettingsProvider::Create());
173 display_settings_provider_->AddDisplayObserver(this);
175 detached_collection_.reset(new DetachedPanelCollection(this));
176 docked_collection_.reset(new DockedPanelCollection(this));
177 drag_controller_.reset(new PanelDragController(this));
178 resize_controller_.reset(new PanelResizeController(this));
181 PanelManager::~PanelManager() {
182 display_settings_provider_->RemoveDisplayObserver(this);
184 // Docked collection should be disposed explicitly before
185 // DisplaySettingsProvider is gone since docked collection needs to remove
186 // the observer from DisplaySettingsProvider.
187 docked_collection_.reset();
190 gfx::Point PanelManager::GetDefaultDetachedPanelOrigin() {
191 return detached_collection_->GetDefaultPanelOrigin();
194 void PanelManager::OnDisplayChanged() {
195 docked_collection_->OnDisplayChanged();
196 detached_collection_->OnDisplayChanged();
197 for (Stacks::const_iterator iter = stacks_.begin();
198 iter != stacks_.end(); iter++)
199 (*iter)->OnDisplayChanged();
202 void PanelManager::OnFullScreenModeChanged(bool is_full_screen) {
203 std::vector<Panel*> all_panels = panels();
204 for (std::vector<Panel*>::const_iterator iter = all_panels.begin();
205 iter != all_panels.end(); ++iter) {
206 (*iter)->FullScreenModeChanged(is_full_screen);
210 int PanelManager::GetMaxPanelWidth(const gfx::Rect& work_area) const {
211 return static_cast<int>(work_area.width() * kPanelMaxWidthFactor);
214 int PanelManager::GetMaxPanelHeight(const gfx::Rect& work_area) const {
215 return static_cast<int>(work_area.height() * kPanelMaxHeightFactor);
218 Panel* PanelManager::CreatePanel(const std::string& app_name,
219 Profile* profile,
220 const GURL& url,
221 const gfx::Rect& requested_bounds,
222 CreateMode mode) {
223 // Need to sync the display area if no panel is present. This is because:
224 // 1) Display area is not initialized until first panel is created.
225 // 2) On windows, display settings notification is tied to a window. When
226 // display settings are changed at the time that no panel exists, we do
227 // not receive any notification.
228 if (num_panels() == 0) {
229 display_settings_provider_->OnDisplaySettingsChanged();
230 display_settings_provider_->AddFullScreenObserver(this);
233 // Compute initial bounds for the panel.
234 int width = requested_bounds.width();
235 int height = requested_bounds.height();
236 if (width == 0)
237 width = height * kPanelDefaultWidthToHeightRatio;
238 else if (height == 0)
239 height = width / kPanelDefaultWidthToHeightRatio;
241 gfx::Rect work_area =
242 display_settings_provider_->GetWorkAreaMatching(requested_bounds);
243 gfx::Size min_size(panel::kPanelMinWidth, panel::kPanelMinHeight);
244 gfx::Size max_size(GetMaxPanelWidth(work_area), GetMaxPanelHeight(work_area));
245 if (width < min_size.width())
246 width = min_size.width();
247 else if (width > max_size.width())
248 width = max_size.width();
250 if (height < min_size.height())
251 height = min_size.height();
252 else if (height > max_size.height())
253 height = max_size.height();
255 // Create the panel.
256 Panel* panel = new Panel(profile, app_name, min_size, max_size);
258 // Find the appropriate panel collection to hold the new panel.
259 gfx::Rect adjusted_requested_bounds(
260 requested_bounds.x(), requested_bounds.y(), width, height);
261 PanelCollection::PositioningMask positioning_mask;
262 PanelCollection* collection = GetCollectionForNewPanel(
263 panel, adjusted_requested_bounds, mode, &positioning_mask);
265 // Let the panel collection decide the initial bounds.
266 gfx::Rect bounds = collection->GetInitialPanelBounds(
267 adjusted_requested_bounds);
268 bounds.AdjustToFit(work_area);
270 panel->Initialize(url, bounds, collection->UsesAlwaysOnTopPanels());
272 // Auto resizable feature is enabled only if no initial size is requested.
273 if (auto_sizing_enabled() && requested_bounds.width() == 0 &&
274 requested_bounds.height() == 0) {
275 panel->SetAutoResizable(true);
278 // Add the panel to the panel collection.
279 collection->AddPanel(panel, positioning_mask);
280 collection->UpdatePanelOnCollectionChange(panel);
282 return panel;
285 PanelCollection* PanelManager::GetCollectionForNewPanel(
286 Panel* new_panel,
287 const gfx::Rect& bounds,
288 CreateMode mode,
289 PanelCollection::PositioningMask* positioning_mask) {
290 if (mode == CREATE_AS_DOCKED) {
291 // Delay layout refreshes in case multiple panels are created within
292 // a short time of one another or the focus changes shortly after panel
293 // is created to avoid excessive screen redraws.
294 *positioning_mask = PanelCollection::DELAY_LAYOUT_REFRESH;
295 return docked_collection_.get();
298 DCHECK_EQ(CREATE_AS_DETACHED, mode);
299 *positioning_mask = PanelCollection::DEFAULT_POSITION;
301 // If the stacking support is not enabled, new panel will still be created as
302 // detached.
303 if (!IsPanelStackingEnabled())
304 return detached_collection_.get();
306 // If there're stacks, try to find a stack that can fit new panel.
307 if (!stacks_.empty()) {
308 // Perform the search as:
309 // 1) Search from the stack with more panels to the stack with least panels.
310 // 2) Amongs the stacks with same number of panels, search from the right-
311 // most stack to the left-most stack.
312 // 3) Among the stack with same number of panels and same x position,
313 // search from the top-most stack to the bottom-most stack.
314 // 4) If there is not enough space to fit new panel even with all inactive
315 // panels being collapsed, move to next stack.
316 stacks_.sort(ComparerNumberOfPanelsInStack);
317 for (Stacks::const_iterator iter = stacks_.begin();
318 iter != stacks_.end(); iter++) {
319 StackedPanelCollection* stack = *iter;
321 // Do not add to other stack that is from differnt extension or profile.
322 // Note that the check is based on bottom panel.
323 Panel* panel = stack->bottom_panel();
324 if (panel->profile() != new_panel->profile() ||
325 panel->extension_id() != new_panel->extension_id())
326 continue;
328 // Do not add to the stack that is minimized by the system.
329 if (stack->IsMinimized())
330 continue;
332 // Do not stack with the panel that is not shown in current virtual
333 // desktop.
334 if (!panel->IsShownOnActiveDesktop())
335 continue;
337 if (bounds.height() <= stack->GetMaximiumAvailableBottomSpace()) {
338 *positioning_mask = static_cast<PanelCollection::PositioningMask>(
339 *positioning_mask | PanelCollection::COLLAPSE_TO_FIT);
340 return stack;
345 // Then try to find a detached panel to which new panel can stack.
346 if (detached_collection_->num_panels()) {
347 // Perform the search as:
348 // 1) Search from the right-most detached panel to the left-most detached
349 // panel.
350 // 2) Among the detached panels with same x position, search from the
351 // top-most detached panel to the bottom-most deatched panel.
352 // 3) If there is not enough space beneath the detached panel, even by
353 // collapsing it if it is inactive, to fit new panel, move to next
354 // detached panel.
355 detached_collection_->SortPanels(CompareDetachedPanels);
357 for (DetachedPanelCollection::Panels::const_iterator iter =
358 detached_collection_->panels().begin();
359 iter != detached_collection_->panels().end(); ++iter) {
360 Panel* panel = *iter;
362 // Do not stack with other panel that is from differnt extension or
363 // profile.
364 if (panel->profile() != new_panel->profile() ||
365 panel->extension_id() != new_panel->extension_id())
366 continue;
368 // Do not stack with the panel that is minimized by the system.
369 if (panel->IsMinimizedBySystem())
370 continue;
372 // Do not stack with the panel that is not shown in the active desktop.
373 if (!panel->IsShownOnActiveDesktop())
374 continue;
376 gfx::Rect work_area =
377 display_settings_provider_->GetWorkAreaMatching(panel->GetBounds());
378 int max_available_space =
379 work_area.bottom() - panel->GetBounds().y() -
380 (panel->IsActive() ? panel->GetBounds().height()
381 : panel::kTitlebarHeight);
382 if (bounds.height() <= max_available_space) {
383 StackedPanelCollection* new_stack = CreateStack();
384 MovePanelToCollection(panel,
385 new_stack,
386 PanelCollection::DEFAULT_POSITION);
387 *positioning_mask = static_cast<PanelCollection::PositioningMask>(
388 *positioning_mask | PanelCollection::COLLAPSE_TO_FIT);
389 return new_stack;
394 return detached_collection_.get();
397 void PanelManager::OnPanelClosed(Panel* panel) {
398 if (num_panels() == 1) {
399 display_settings_provider_->RemoveFullScreenObserver(this);
402 drag_controller_->OnPanelClosed(panel);
403 resize_controller_->OnPanelClosed(panel);
405 // Note that we need to keep track of panel's collection since it will be
406 // gone once RemovePanel is called.
407 PanelCollection* collection = panel->collection();
408 collection->RemovePanel(panel, PanelCollection::PANEL_CLOSED);
410 // If only one panel is left in the stack, move it out of the stack.
411 // Also make sure that this detached panel will be expanded if not yet.
412 if (collection->type() == PanelCollection::STACKED) {
413 StackedPanelCollection* stack =
414 static_cast<StackedPanelCollection*>(collection);
415 DCHECK_GE(stack->num_panels(), 1);
416 if (stack->num_panels() == 1) {
417 Panel* top_panel = stack->top_panel();
418 MovePanelToCollection(top_panel,
419 detached_collection(),
420 PanelCollection::DEFAULT_POSITION);
421 if (top_panel->expansion_state() != Panel::EXPANDED)
422 top_panel->SetExpansionState(Panel::EXPANDED);
423 RemoveStack(stack);
427 content::NotificationService::current()->Notify(
428 chrome::NOTIFICATION_PANEL_CLOSED,
429 content::Source<Panel>(panel),
430 content::NotificationService::NoDetails());
433 StackedPanelCollection* PanelManager::CreateStack() {
434 StackedPanelCollection* stack = new StackedPanelCollection(this);
435 stacks_.push_back(stack);
436 return stack;
439 void PanelManager::RemoveStack(StackedPanelCollection* stack) {
440 DCHECK_EQ(0, stack->num_panels());
441 stacks_.remove(stack);
442 stack->CloseAll();
443 delete stack;
446 void PanelManager::StartDragging(Panel* panel,
447 const gfx::Point& mouse_location) {
448 drag_controller_->StartDragging(panel, mouse_location);
451 void PanelManager::Drag(const gfx::Point& mouse_location) {
452 drag_controller_->Drag(mouse_location);
455 void PanelManager::EndDragging(bool cancelled) {
456 drag_controller_->EndDragging(cancelled);
459 void PanelManager::StartResizingByMouse(Panel* panel,
460 const gfx::Point& mouse_location,
461 int component) {
462 if (panel->CanResizeByMouse() != panel::NOT_RESIZABLE &&
463 component != HTNOWHERE) {
464 resize_controller_->StartResizing(panel, mouse_location, component);
468 void PanelManager::ResizeByMouse(const gfx::Point& mouse_location) {
469 if (resize_controller_->IsResizing())
470 resize_controller_->Resize(mouse_location);
473 void PanelManager::EndResizingByMouse(bool cancelled) {
474 if (resize_controller_->IsResizing()) {
475 Panel* resized_panel = resize_controller_->EndResizing(cancelled);
476 if (!cancelled && resized_panel->collection())
477 resized_panel->collection()->RefreshLayout();
481 void PanelManager::OnPanelExpansionStateChanged(Panel* panel) {
482 panel->collection()->OnPanelExpansionStateChanged(panel);
485 void PanelManager::MovePanelToCollection(
486 Panel* panel,
487 PanelCollection* target_collection,
488 PanelCollection::PositioningMask positioning_mask) {
489 DCHECK(panel);
490 PanelCollection* current_collection = panel->collection();
491 DCHECK(current_collection);
492 DCHECK_NE(current_collection, target_collection);
493 current_collection->RemovePanel(panel,
494 PanelCollection::PANEL_CHANGED_COLLECTION);
496 target_collection->AddPanel(panel, positioning_mask);
497 target_collection->UpdatePanelOnCollectionChange(panel);
498 panel->SetAlwaysOnTop(target_collection->UsesAlwaysOnTopPanels());
501 bool PanelManager::ShouldBringUpTitlebars(int mouse_x, int mouse_y) const {
502 return docked_collection_->ShouldBringUpTitlebars(mouse_x, mouse_y);
505 void PanelManager::BringUpOrDownTitlebars(bool bring_up) {
506 docked_collection_->BringUpOrDownTitlebars(bring_up);
509 void PanelManager::CloseAll() {
510 DCHECK(!drag_controller_->is_dragging());
512 detached_collection_->CloseAll();
513 docked_collection_->CloseAll();
516 int PanelManager::num_panels() const {
517 int count = detached_collection_->num_panels() +
518 docked_collection_->num_panels();
519 for (Stacks::const_iterator iter = stacks_.begin();
520 iter != stacks_.end(); iter++)
521 count += (*iter)->num_panels();
522 return count;
525 std::vector<Panel*> PanelManager::panels() const {
526 std::vector<Panel*> panels;
527 for (DetachedPanelCollection::Panels::const_iterator iter =
528 detached_collection_->panels().begin();
529 iter != detached_collection_->panels().end(); ++iter)
530 panels.push_back(*iter);
531 for (DockedPanelCollection::Panels::const_iterator iter =
532 docked_collection_->panels().begin();
533 iter != docked_collection_->panels().end(); ++iter)
534 panels.push_back(*iter);
535 for (Stacks::const_iterator stack_iter = stacks_.begin();
536 stack_iter != stacks_.end(); stack_iter++) {
537 for (StackedPanelCollection::Panels::const_iterator iter =
538 (*stack_iter)->panels().begin();
539 iter != (*stack_iter)->panels().end(); ++iter) {
540 panels.push_back(*iter);
543 return panels;
546 std::vector<Panel*> PanelManager::GetDetachedAndStackedPanels() const {
547 std::vector<Panel*> panels;
548 for (DetachedPanelCollection::Panels::const_iterator iter =
549 detached_collection_->panels().begin();
550 iter != detached_collection_->panels().end(); ++iter)
551 panels.push_back(*iter);
552 for (Stacks::const_iterator stack_iter = stacks_.begin();
553 stack_iter != stacks_.end(); stack_iter++) {
554 for (StackedPanelCollection::Panels::const_iterator iter =
555 (*stack_iter)->panels().begin();
556 iter != (*stack_iter)->panels().end(); ++iter) {
557 panels.push_back(*iter);
560 return panels;
563 void PanelManager::SetMouseWatcher(PanelMouseWatcher* watcher) {
564 panel_mouse_watcher_.reset(watcher);
567 void PanelManager::OnPanelAnimationEnded(Panel* panel) {
568 content::NotificationService::current()->Notify(
569 chrome::NOTIFICATION_PANEL_BOUNDS_ANIMATIONS_FINISHED,
570 content::Source<Panel>(panel),
571 content::NotificationService::NoDetails());