• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/ui/panels/panel_manager.h"
6 
7 #include <algorithm>
8 #include "base/logging.h"
9 #include "base/scoped_ptr.h"
10 #include "chrome/browser/ui/browser.h"
11 #include "chrome/browser/ui/panels/panel.h"
12 #include "chrome/browser/ui/window_sizer.h"
13 
14 namespace {
15 // Invalid panel index.
16 const size_t kInvalidPanelIndex = static_cast<size_t>(-1);
17 
18 // Minimum width and height of a panel.
19 const int kPanelMinWidthPixels = 64;
20 const int kPanelMinHeightPixels = 24;
21 
22 // Default width and height of a panel.
23 const int kPanelDefaultWidthPixels = 240;
24 const int kPanelDefaultHeightPixels = 290;
25 
26 // Maxmium width and height of a panel based on the factor of the working
27 // area.
28 const double kPanelMaxWidthFactor = 1.0;
29 const double kPanelMaxHeightFactor = 0.5;
30 
31 // Horizontal spacing between two panels.
32 const int kPanelsHorizontalSpacing = 4;
33 
34 // Single instance of PanelManager.
35 scoped_ptr<PanelManager> panel_instance;
36 } // namespace
37 
38 // static
GetInstance()39 PanelManager* PanelManager::GetInstance() {
40   if (!panel_instance.get()) {
41     panel_instance.reset(new PanelManager());
42   }
43   return panel_instance.get();
44 }
45 
PanelManager()46 PanelManager::PanelManager()
47     : max_width_(0),
48       max_height_(0),
49       min_x_(0),
50       current_x_(0),
51       bottom_edge_y_(0),
52       dragging_panel_index_(kInvalidPanelIndex),
53       dragging_panel_original_x_(0) {
54   OnDisplayChanged();
55 }
56 
~PanelManager()57 PanelManager::~PanelManager() {
58   DCHECK(active_panels_.empty());
59   DCHECK(pending_panels_.empty());
60   DCHECK(panels_pending_to_remove_.empty());
61 }
62 
OnDisplayChanged()63 void PanelManager::OnDisplayChanged() {
64   scoped_ptr<WindowSizer::MonitorInfoProvider> info_provider(
65       WindowSizer::CreateDefaultMonitorInfoProvider());
66   gfx::Rect work_area = info_provider->GetPrimaryMonitorWorkArea();
67 
68   min_x_ = work_area.x();
69   current_x_ = work_area.right();
70   bottom_edge_y_ = work_area.bottom();
71   max_width_ = static_cast<int>(work_area.width() * kPanelMaxWidthFactor);
72   max_height_ = static_cast<int>(work_area.height() * kPanelMaxHeightFactor);
73 
74   Rearrange(active_panels_.begin());
75 }
76 
CreatePanel(Browser * browser)77 Panel* PanelManager::CreatePanel(Browser* browser) {
78   gfx::Rect bounds = browser->override_bounds();
79   bool is_within_bounds = ComputeBoundsForNextPanel(&bounds, true);
80 
81   Panel* panel = new Panel(browser, bounds);
82   if (is_within_bounds)
83     active_panels_.push_back(panel);
84   else
85     pending_panels_.push_back(panel);
86 
87   return panel;
88 }
89 
ProcessPending()90 void PanelManager::ProcessPending() {
91   while (!pending_panels_.empty()) {
92     Panel* panel = pending_panels_.front();
93     gfx::Rect bounds = panel->bounds();
94     if (ComputeBoundsForNextPanel(&bounds, true)) {
95       // TODO(jianli): More work to support displaying pending panels.
96       active_panels_.push_back(panel);
97       pending_panels_.pop_front();
98     }
99   }
100 }
101 
Remove(Panel * panel)102 void PanelManager::Remove(Panel* panel) {
103   // If we're in the process of dragging, delay the removal.
104   if (dragging_panel_index_ != kInvalidPanelIndex) {
105     panels_pending_to_remove_.push_back(panel);
106     return;
107   }
108 
109   DoRemove(panel);
110 }
111 
DelayedRemove()112 void PanelManager::DelayedRemove() {
113   for (size_t i = 0; i < panels_pending_to_remove_.size(); ++i)
114     DoRemove(panels_pending_to_remove_[i]);
115   panels_pending_to_remove_.clear();
116 }
117 
DoRemove(Panel * panel)118 void PanelManager::DoRemove(Panel* panel) {
119   // Checks the active panel list.
120   ActivePanels::iterator iter =
121       find(active_panels_.begin(), active_panels_.end(), panel);
122   if (iter == active_panels_.end()) {
123     // Checks the pending panel list.
124     PendingPanels::iterator iter2 =
125         find(pending_panels_.begin(), pending_panels_.end(), panel);
126     if (iter2 != pending_panels_.end())
127       pending_panels_.erase(iter2);
128     return;
129   }
130 
131   current_x_ = (*iter)->bounds().x() + (*iter)->bounds().width();
132   Rearrange(active_panels_.erase(iter));
133 
134   ProcessPending();
135 }
136 
StartDragging(Panel * panel)137 void PanelManager::StartDragging(Panel* panel) {
138   for (size_t i = 0; i < active_panels_.size(); ++i) {
139     if (active_panels_[i] == panel) {
140       dragging_panel_index_ = i;
141       dragging_panel_bounds_ = panel->bounds();
142       dragging_panel_original_x_ = dragging_panel_bounds_.x();
143       break;
144     }
145   }
146 }
147 
Drag(int delta_x)148 void PanelManager::Drag(int delta_x) {
149   DCHECK(dragging_panel_index_ != kInvalidPanelIndex);
150 
151   if (!delta_x)
152     return;
153 
154   // Moves this panel to the dragging position.
155   gfx::Rect new_bounds(active_panels_[dragging_panel_index_]->bounds());
156   new_bounds.set_x(new_bounds.x() + delta_x);
157   active_panels_[dragging_panel_index_]->SetBounds(new_bounds);
158 
159   // Checks and processes other affected panels.
160   if (delta_x > 0)
161     DragPositive(delta_x);
162   else
163     DragNegative(delta_x);
164 }
165 
DragNegative(int delta_x)166 void PanelManager::DragNegative(int delta_x) {
167   DCHECK(delta_x < 0);
168 
169   Panel* dragging_panel = active_panels_[dragging_panel_index_];
170 
171   // This is the left corner of the dragging panel. We use it to check against
172   // all the panels on its left.
173   int dragging_panel_x = dragging_panel->bounds().x() + delta_x;
174 
175   // This is the right corner which a panel will be moved to.
176   int right_x_to_move_to =
177       dragging_panel_bounds_.x() + dragging_panel_bounds_.width();
178 
179   // Checks the panels to the left of the dragging panel.
180   size_t i = dragging_panel_index_;
181   size_t j = i + 1;
182   for (; j < active_panels_.size(); ++j, ++i) {
183     // Current panel will only be affected if the left corner of dragging
184     // panel goes beyond the middle position of the current panel.
185     if (dragging_panel_x > active_panels_[j]->bounds().x() +
186                            active_panels_[j]->bounds().width() / 2)
187       break;
188 
189     // Moves current panel to the new position.
190     gfx::Rect bounds(active_panels_[j]->bounds());
191     bounds.set_x(right_x_to_move_to - bounds.width());
192     right_x_to_move_to -= bounds.width() + kPanelsHorizontalSpacing;
193     active_panels_[j]->SetBounds(bounds);
194 
195     // Adjusts the index of current panel.
196     active_panels_[i] = active_panels_[j];
197   }
198 
199   // Adjusts the position and index of dragging panel as the result of moving
200   // other affected panels.
201   if (j != dragging_panel_index_ + 1) {
202     j--;
203     dragging_panel_bounds_.set_x(right_x_to_move_to -
204                                  dragging_panel_bounds_.width());
205     active_panels_[j] = dragging_panel;
206     dragging_panel_index_ = j;
207   }
208 }
209 
DragPositive(int delta_x)210 void PanelManager::DragPositive(int delta_x) {
211   DCHECK(delta_x > 0);
212 
213   Panel* dragging_panel = active_panels_[dragging_panel_index_];
214 
215   // This is the right corner of the dragging panel. We use it to check against
216   // all the panels on its right.
217   int dragging_panel_x = dragging_panel->bounds().x() +
218       dragging_panel->bounds().width() - 1 + delta_x;
219 
220   // This is the left corner which a panel will be moved to.
221   int left_x_to_move_to = dragging_panel_bounds_.x();
222 
223   // Checks the panels to the right of the dragging panel.
224   int i = static_cast<int>(dragging_panel_index_);
225   int j = i - 1;
226   for (; j >= 0; --j, --i) {
227     // Current panel will only be affected if the right corner of dragging
228     // panel goes beyond the middle position of the current panel.
229     if (dragging_panel_x < active_panels_[j]->bounds().x() +
230                            active_panels_[j]->bounds().width() / 2)
231       break;
232 
233     // Moves current panel to the new position.
234     gfx::Rect bounds(active_panels_[j]->bounds());
235     bounds.set_x(left_x_to_move_to);
236     left_x_to_move_to += bounds.width() + kPanelsHorizontalSpacing;
237     active_panels_[j]->SetBounds(bounds);
238 
239     // Adjusts the index of current panel.
240     active_panels_[i] = active_panels_[j];
241   }
242 
243   // Adjusts the position and index of dragging panel as the result of moving
244   // other affected panels.
245   if (j != static_cast<int>(dragging_panel_index_) - 1) {
246     j++;
247     dragging_panel_bounds_.set_x(left_x_to_move_to);
248     active_panels_[j] = dragging_panel;
249     dragging_panel_index_ = j;
250   }
251 }
252 
EndDragging(bool cancelled)253 void PanelManager::EndDragging(bool cancelled) {
254   DCHECK(dragging_panel_index_ != kInvalidPanelIndex);
255 
256   if (cancelled) {
257     Drag(dragging_panel_original_x_ -
258          active_panels_[dragging_panel_index_]->bounds().x());
259   } else {
260     active_panels_[dragging_panel_index_]->SetBounds(dragging_panel_bounds_);
261   }
262 
263   dragging_panel_index_ = kInvalidPanelIndex;
264 
265   DelayedRemove();
266 }
267 
Rearrange(ActivePanels::iterator iter_to_start)268 void PanelManager::Rearrange(ActivePanels::iterator iter_to_start) {
269   if (iter_to_start == active_panels_.end())
270     return;
271 
272   for (ActivePanels::iterator iter = iter_to_start;
273        iter != active_panels_.end(); ++iter) {
274     gfx::Rect new_bounds((*iter)->bounds());
275     ComputeBoundsForNextPanel(&new_bounds, false);
276     if (new_bounds != (*iter)->bounds())
277       (*iter)->SetBounds(new_bounds);
278   }
279 }
280 
ComputeBoundsForNextPanel(gfx::Rect * bounds,bool allow_size_change)281 bool PanelManager::ComputeBoundsForNextPanel(gfx::Rect* bounds,
282                                              bool allow_size_change) {
283   int width = bounds->width();
284   int height = bounds->height();
285 
286   // Update the width and/or height to fit into our constraint.
287   if (allow_size_change) {
288     if (width == 0 && height == 0) {
289       width = kPanelDefaultWidthPixels;
290       height = kPanelDefaultHeightPixels;
291     }
292 
293     if (width < kPanelMinWidthPixels)
294       width = kPanelMinWidthPixels;
295     else if (width > max_width_)
296       width = max_width_;
297 
298     if (height < kPanelMinHeightPixels)
299       height = kPanelMinHeightPixels;
300     else if (height > max_height_)
301       height = max_height_;
302   }
303 
304   int x = current_x_ - width;
305   int y = bottom_edge_y_ - height;
306 
307   if (x < min_x_)
308     return false;
309 
310   current_x_ -= width + kPanelsHorizontalSpacing;
311 
312   bounds->SetRect(x, y, width, height);
313   return true;
314 }
315 
MinimizeAll()316 void PanelManager::MinimizeAll() {
317   for (ActivePanels::const_iterator iter = active_panels_.begin();
318        iter != active_panels_.end(); ++iter) {
319     (*iter)->Minimize();
320   }
321 }
322 
RestoreAll()323 void PanelManager::RestoreAll() {
324   for (ActivePanels::const_iterator iter = active_panels_.begin();
325        iter != active_panels_.end(); ++iter) {
326     (*iter)->Restore();
327   }
328 }
329 
RemoveAllActive()330 void PanelManager::RemoveAllActive() {
331   // This should not be called when we're in the process of dragging.
332   DCHECK(dragging_panel_index_ == kInvalidPanelIndex);
333 
334   // Start from the bottom to avoid reshuffling.
335   for (int i = static_cast<int>(active_panels_.size()) -1; i >= 0; --i)
336     active_panels_[i]->Close();
337 
338   ProcessPending();
339 }
340 
AreAllMinimized() const341 bool PanelManager::AreAllMinimized() const {
342   for (ActivePanels::const_iterator iter = active_panels_.begin();
343        iter != active_panels_.end(); ++iter) {
344     if (!(*iter)->minimized())
345       return false;
346   }
347   return true;
348 }
349