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