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 39 PanelManager* PanelManager::GetInstance() { 40 if (!panel_instance.get()) { 41 panel_instance.reset(new PanelManager()); 42 } 43 return panel_instance.get(); 44 } 45 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 57 PanelManager::~PanelManager() { 58 DCHECK(active_panels_.empty()); 59 DCHECK(pending_panels_.empty()); 60 DCHECK(panels_pending_to_remove_.empty()); 61 } 62 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 341 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