- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / panels / stacked_panel_collection.cc
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.
4
5 #include "chrome/browser/ui/panels/stacked_panel_collection.h"
6
7 #include <algorithm>
8 #include "base/auto_reset.h"
9 #include "base/logging.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/ui/panels/detached_panel_collection.h"
12 #include "chrome/browser/ui/panels/display_settings_provider.h"
13 #include "chrome/browser/ui/panels/native_panel_stack_window.h"
14 #include "chrome/browser/ui/panels/panel.h"
15 #include "chrome/browser/ui/panels/panel_constants.h"
16 #include "chrome/browser/ui/panels/panel_manager.h"
17 #include "chrome/common/extensions/extension.h"
18
19 StackedPanelCollection::StackedPanelCollection(PanelManager* panel_manager)
20     : PanelCollection(PanelCollection::STACKED),
21       panel_manager_(panel_manager),
22       primary_stack_window_(NULL),
23       secondary_stack_window_(NULL),
24       minimizing_all_(false) {
25 }
26
27 StackedPanelCollection::~StackedPanelCollection() {
28   DCHECK(panels_.empty());
29   DCHECK(most_recently_active_panels_.empty());
30 }
31
32 void StackedPanelCollection::OnDisplayChanged() {
33   if (panels_.empty())
34     return;
35
36   gfx::Rect enclosing_bounds = GetEnclosingBounds();
37   gfx::Rect work_area = panel_manager_->display_settings_provider()->
38       GetWorkAreaMatching(enclosing_bounds);
39
40   // If the height of the whole stack is bigger than the height of the new work
41   // area, try to reduce the stack height by collapsing panels. In rare case,
42   // all panels are collapsed and there is still not enough space. We simply
43   // let the stack go beyond the work area limit.
44   if (enclosing_bounds.height() > work_area.height()) {
45     int needed_space = enclosing_bounds.height() - work_area.height();
46     MinimizePanelsForSpace(needed_space);
47   }
48
49   gfx::Rect top_bounds = top_panel()->GetBounds();
50   int common_width = top_bounds.width();
51   if (common_width > work_area.width())
52     common_width = work_area.width();
53
54   int common_x = top_bounds.x();
55   if (common_x + common_width > work_area.right())
56     common_x = work_area.right() - common_width;
57   if (common_x < work_area.x())
58     common_x = work_area.x();
59
60   int total_height = bottom_panel()->GetBounds().bottom() - top_bounds.y();
61   int start_y = top_bounds.y();
62   if (start_y + total_height > work_area.bottom())
63     start_y = work_area.bottom() - total_height;
64   if (start_y < work_area.y())
65     start_y = work_area.y();
66
67   RefreshLayoutWithTopPanelStartingAt(
68       gfx::Point(common_x, start_y), common_width);
69 }
70
71 void StackedPanelCollection::RefreshLayout() {
72   if (panels_.empty())
73     return;
74   gfx::Rect top_bounds = top_panel()->GetBounds();
75   RefreshLayoutWithTopPanelStartingAt(top_bounds.origin(), top_bounds.width());
76 }
77
78 void StackedPanelCollection::RefreshLayoutWithTopPanelStartingAt(
79     const gfx::Point& start_position, int common_width) {
80   if (panels_.empty())
81     return;
82
83   // If only one panel is left in the stack, we only need to check if it should
84   // be moved to |start_y| position.
85   if (panels_.size() == 1) {
86     Panel* panel = panels_.front();
87     gfx::Rect bounds = panel->GetBounds();
88     if (bounds.origin() != start_position) {
89       bounds.set_origin(start_position);
90       panel->SetPanelBounds(bounds);
91     }
92     return;
93   }
94
95   // We do not update bounds for affected panels one by one. Instead, all
96   // changes are bundled and performed synchronously.
97   primary_stack_window_->BeginBatchUpdatePanelBounds(true);
98   if (secondary_stack_window_)
99     secondary_stack_window_->BeginBatchUpdatePanelBounds(true);
100
101   int y = start_position.y();
102   int common_x = start_position.x();
103   for (Panels::const_iterator iter = panels_.begin();
104        iter != panels_.end(); ++iter) {
105     Panel* panel = *iter;
106
107     // The visibility of minimize button might need to be updated due to that
108     // top panel might change when a panel is being added or removed from
109     // the stack.
110     panel->UpdateMinimizeRestoreButtonVisibility();
111
112     // Don't update the stacked panel that is in preview mode.
113     gfx::Rect bounds = panel->GetBounds();
114     if (panel->in_preview_mode()) {
115       y += bounds.height();
116       continue;
117     }
118
119     // Update the restored size.
120     gfx::Size full_size = panel->full_size();
121     full_size.set_width(common_width);
122     panel->set_full_size(full_size);
123
124     // Recompute the bounds.
125     bounds.SetRect(
126         common_x,
127         y,
128         common_width,
129         panel->expansion_state() == Panel::EXPANDED ?
130             panel->full_size().height() : panel->TitleOnlyHeight());
131
132     GetStackWindowForPanel(panel)->AddPanelBoundsForBatchUpdate(panel, bounds);
133
134     y += bounds.height();
135   }
136
137   primary_stack_window_->EndBatchUpdatePanelBounds();
138   if (secondary_stack_window_)
139     secondary_stack_window_->EndBatchUpdatePanelBounds();
140 }
141
142 string16 StackedPanelCollection::GetTitle() const {
143   if (panels_.empty())
144     return string16();
145
146   Panel* panel = panels_.front();
147   const extensions::Extension* extension = panel->GetExtension();
148   return UTF8ToUTF16(extension && !extension->name().empty() ?
149       extension->name() : panel->app_name());
150 }
151
152 gfx::Image StackedPanelCollection::GetIcon() const {
153   if (panels_.empty())
154     return gfx::Image();
155   return panels_.front()->app_icon();
156 }
157
158 void StackedPanelCollection::PanelBoundsBatchUpdateCompleted() {
159   if (!secondary_stack_window_ || panels_.empty())
160     return;
161
162   if (top_panel()->in_preview_mode() != bottom_panel()->in_preview_mode() ||
163       primary_stack_window_->IsAnimatingPanelBounds() ||
164       secondary_stack_window_->IsAnimatingPanelBounds())
165     return;
166
167   // Move all panels from secondary stack window to primary stack window.
168   primary_stack_window_->MergeWith(secondary_stack_window_);
169   secondary_stack_window_->Close();
170   secondary_stack_window_ = NULL;
171 }
172
173 gfx::Rect StackedPanelCollection::GetEnclosingBounds() const {
174   gfx::Rect enclosing_bounds = top_panel()->GetBounds();
175   enclosing_bounds.set_height(
176       bottom_panel()->GetBounds().bottom() - enclosing_bounds.y());
177   return enclosing_bounds;
178 }
179
180 int StackedPanelCollection::MinimizePanelsForSpace(int needed_space) {
181   int available_space = GetCurrentAvailableBottomSpace();
182
183   // Only the most recently active panel might be active.
184   Panel* active_panel = NULL;
185   if (!most_recently_active_panels_.empty()) {
186     Panel* most_recently_active_panel = most_recently_active_panels_.front();
187     if (most_recently_active_panel->IsActive())
188       active_panel = most_recently_active_panel;
189   }
190
191   for (Panels::const_reverse_iterator iter =
192            most_recently_active_panels_.rbegin();
193        iter != most_recently_active_panels_.rend() &&
194            available_space < needed_space;
195        ++iter) {
196     Panel* current_panel = *iter;
197     if (current_panel != active_panel && !IsPanelMinimized(current_panel)) {
198       available_space +=
199           current_panel->GetBounds().height() - panel::kTitlebarHeight;
200       MinimizePanel(current_panel);
201     }
202   }
203   return available_space;
204 }
205
206 void StackedPanelCollection::AddPanel(Panel* panel,
207                                       PositioningMask positioning_mask) {
208   DCHECK_NE(this, panel->collection());
209   panel->set_collection(this);
210   Panel* adjacent_panel = NULL;
211   if (positioning_mask & PanelCollection::TOP_POSITION) {
212     adjacent_panel = top_panel();
213     panels_.push_front(panel);
214   } else {
215     // To fit the new panel within the working area, collapse unfocused panels
216     // in the least recent active order until there is enough space.
217     if (positioning_mask & PanelCollection::COLLAPSE_TO_FIT) {
218       int needed_space = panel->GetBounds().height();
219       int available_space = MinimizePanelsForSpace(needed_space);
220       DCHECK(available_space >= needed_space);
221     }
222
223     adjacent_panel = bottom_panel();
224     panels_.push_back(panel);
225   }
226
227   if (panel->IsActive())
228     most_recently_active_panels_.push_front(panel);
229   else
230     most_recently_active_panels_.push_back(panel);
231
232   if (adjacent_panel)
233     UpdatePanelCornerStyle(adjacent_panel);
234
235   // The secondary stack window should be used when one of the following occurs:
236   // 1) Some panels but not all panels are being dragged. This is because
237   //    those panels being dragged might not be fully aligned with other panels
238   //    not being dragged.
239   // 2) The newly added panel is not fully aligned with the existing panel, in
240   //    terms of both x and width.
241   NativePanelStackWindow* stack_window;
242   if (top_panel()->in_preview_mode() == bottom_panel()->in_preview_mode() &&
243       top_panel()->GetBounds().x() == bottom_panel()->GetBounds().x() &&
244       top_panel()->GetBounds().width() == bottom_panel()->GetBounds().width()) {
245     if (!primary_stack_window_)
246       primary_stack_window_ = NativePanelStackWindow::Create(this);
247     stack_window = primary_stack_window_;
248   } else {
249     if (!secondary_stack_window_)
250       secondary_stack_window_ = NativePanelStackWindow::Create(this);
251     stack_window = secondary_stack_window_;
252   }
253   stack_window->AddPanel(panel);
254
255   if ((positioning_mask & NO_LAYOUT_REFRESH) == 0)
256     RefreshLayout();
257 }
258
259 void StackedPanelCollection::RemovePanel(Panel* panel, RemovalReason reason) {
260   bool is_top = panel == top_panel();
261   bool is_bottom = panel == bottom_panel();
262
263   // If the top panel is being closed, all panels below it should move up. To
264   // do this, the top y position of top panel needs to be tracked first.
265   bool top_panel_closed = false;
266   gfx::Point top_origin;
267   int top_width = 0;
268   if (reason == PanelCollection::PANEL_CLOSED && is_top) {
269     top_panel_closed = true;
270     top_origin = panel->GetBounds().origin();
271     top_width = panel->GetBounds().width();
272   }
273
274   panel->set_collection(NULL);
275   panels_.remove(panel);
276   most_recently_active_panels_.remove(panel);
277
278   if (is_top) {
279     Panel* new_top_panel = top_panel();
280     if (new_top_panel)
281       UpdatePanelCornerStyle(new_top_panel);
282   } else if (is_bottom) {
283     Panel* new_bottom_panel = bottom_panel();
284     if (new_bottom_panel)
285       UpdatePanelCornerStyle(new_bottom_panel);
286   }
287
288   // If an active panel is being closed, try to focus the next recently active
289   // panel in the stack that is not minimized.
290   if (reason == PanelCollection::PANEL_CLOSED &&
291       panel->IsActive() &&
292       !most_recently_active_panels_.empty()) {
293     for (Panels::const_iterator iter = most_recently_active_panels_.begin();
294        iter != most_recently_active_panels_.end(); ++iter) {
295       Panel* other_panel = *iter;
296       if (!IsPanelMinimized(other_panel)) {
297         other_panel->Activate();
298         break;
299       }
300     }
301   }
302
303   // If the top panel is closed, move up all other panels to stay at the same
304   // y position as the top panel being closed.
305   if (top_panel_closed)
306     RefreshLayoutWithTopPanelStartingAt(top_origin, top_width);
307   else if (reason == PanelCollection::PANEL_CLOSED)
308     RefreshLayout();
309
310   // Remove the panel from the corresponding stack window.
311   GetStackWindowForPanel(panel)->RemovePanel(panel);
312
313   // Close the secondary stack window if no panel is is shown inside it.
314   // Note that we do not need to do this for primary stack window since the
315   // whole stack will be gone when only one panel is left.
316   if (secondary_stack_window_ && secondary_stack_window_->IsEmpty()) {
317     secondary_stack_window_->Close();
318     secondary_stack_window_ = NULL;
319   }
320 }
321
322 void StackedPanelCollection::CloseAll() {
323   // Make a copy as closing panels can modify the iterator.
324   Panels panels_copy = panels_;
325
326   for (Panels::const_iterator iter = panels_copy.begin();
327        iter != panels_copy.end(); ++iter)
328     (*iter)->Close();
329
330   if (primary_stack_window_) {
331     primary_stack_window_->Close();
332     primary_stack_window_ = NULL;
333   }
334   if (secondary_stack_window_) {
335     secondary_stack_window_->Close();
336     secondary_stack_window_ = NULL;
337   }
338 }
339
340 void StackedPanelCollection::OnPanelAttentionStateChanged(Panel* panel) {
341   if ((panel->attention_mode() & Panel::USE_SYSTEM_ATTENTION) != 0)
342     primary_stack_window_->DrawSystemAttention(panel->IsDrawingAttention());
343 }
344
345 void StackedPanelCollection::OnPanelTitlebarClicked(
346     Panel* panel, panel::ClickModifier modifier) {
347   bool expanded = panel->expansion_state() == Panel::EXPANDED;
348   if (modifier == panel::APPLY_TO_ALL) {
349     if (expanded)
350       MinimizeAll();
351     else
352       RestoreAll(panel);
353   } else {
354     if (expanded)
355       MinimizePanel(panel);
356     else
357       RestorePanel(panel);
358   }
359 }
360
361 void StackedPanelCollection::ResizePanelWindow(
362     Panel* panel,
363     const gfx::Size& preferred_window_size) {
364 }
365
366 void StackedPanelCollection::ActivatePanel(Panel* panel) {
367   // Make sure the panel is expanded when activated so the user input
368   // does not go into a collapsed window.
369   if (panel->IsMinimized())
370     panel->SetExpansionState(Panel::EXPANDED);
371 }
372
373 void StackedPanelCollection::MinimizePanel(Panel* panel) {
374   panel->SetExpansionState(Panel::TITLE_ONLY);
375 }
376
377 void StackedPanelCollection::RestorePanel(Panel* panel) {
378   // Ensure that the panel could fit within the work area after it is expanded.
379   // First, try to collapse the unfocused panel in the least recent active
380   // order in order to get enough space.
381   int needed_space = panel->full_size().height() - panel->TitleOnlyHeight();
382   int available_space = MinimizePanelsForSpace(needed_space);
383
384   // If there is still not enough space, try to move up the stack.
385   int space_beyond_available = needed_space - available_space;
386   if (space_beyond_available > 0) {
387     int top_available_space = GetCurrentAvailableTopSpace();
388     int move_delta = (space_beyond_available > top_available_space) ?
389         top_available_space : space_beyond_available;
390     for (Panels::const_iterator iter = panels_.begin();
391          iter != panels_.end(); iter++) {
392       Panel* current_panel = *iter;
393       gfx::Rect bounds = current_panel->GetBounds();
394       bounds.set_y(bounds.y() - move_delta);
395       current_panel->SetPanelBounds(bounds);
396     }
397     available_space += move_delta;
398   }
399
400   // If there is still not enough space, shrink the restored height to make it
401   // fit at the last resort. Note that the restored height cannot be shrunk less
402   // than the minimum panel height. If this is the case, we will just let it
403   // expand beyond the screen boundary.
404   space_beyond_available = needed_space - available_space;
405   if (space_beyond_available > 0) {
406     gfx::Size full_size = panel->full_size();
407     int reduced_height = full_size.height() - space_beyond_available;
408     if (reduced_height < panel::kPanelMinHeight)
409       reduced_height = panel::kPanelMinHeight;
410     full_size.set_height(reduced_height);
411     panel->set_full_size(full_size);
412   }
413
414   panel->SetExpansionState(Panel::EXPANDED);
415 }
416
417 void StackedPanelCollection::MinimizeAll() {
418   // Set minimizing_all_ to prevent deactivation of each panel when it
419   // is minimized. See comments in OnPanelExpansionStateChanged.
420   base::AutoReset<bool> pin(&minimizing_all_, true);
421   Panel* minimized_active_panel = NULL;
422   for (Panels::const_iterator iter = panels_.begin();
423        iter != panels_.end(); ++iter) {
424     if ((*iter)->IsActive())
425       minimized_active_panel = *iter;
426     MinimizePanel(*iter);
427   }
428
429   // When a single panel is minimized, it is deactivated to ensure that
430   // a minimized panel does not have focus. However, when minimizing all,
431   // the deactivation is only done once after all panels are minimized,
432   // rather than per minimized panel, both for efficiency and to avoid
433   // temporary activations of random not-yet-minimized panels.
434   if (minimized_active_panel) {
435     minimized_active_panel->Deactivate();
436     // Layout will be refreshed in response to (de)activation notification.
437   }
438 }
439
440 void StackedPanelCollection::RestoreAll(Panel* panel_clicked) {
441   // Expand the panel being clicked first. This is to make sure at least one
442   // panel that is clicked by the user will be expanded.
443   RestorePanel(panel_clicked);
444
445   // Try to expand all other panels starting from the most recently active
446   // panel.
447   for (Panels::const_iterator iter = most_recently_active_panels_.begin();
448        iter != most_recently_active_panels_.end(); ++iter) {
449     // If the stack already extends to both top and bottom of the work area,
450     // stop now since we cannot fit any more expanded panels.
451     if (GetCurrentAvailableTopSpace() == 0 &&
452         GetCurrentAvailableBottomSpace() == 0) {
453       break;
454     }
455
456     Panel* panel = *iter;
457     if (panel != panel_clicked)
458       RestorePanel(panel);
459   }
460 }
461
462 void StackedPanelCollection::OnMinimizeButtonClicked(
463     Panel* panel, panel::ClickModifier modifier) {
464   // The minimize button is only present in the top panel.
465   DCHECK_EQ(top_panel(), panel);
466
467   primary_stack_window_->Minimize();
468 }
469
470 void StackedPanelCollection::OnRestoreButtonClicked(
471     Panel* panel, panel::ClickModifier modifier) {
472   NOTREACHED();
473 }
474
475 bool StackedPanelCollection::CanShowMinimizeButton(const Panel* panel) const {
476   // Only the top panel in the stack shows the minimize button.
477   return PanelManager::CanUseSystemMinimize() && panel == top_panel();
478 }
479
480 bool StackedPanelCollection::CanShowRestoreButton(const Panel* panel) const {
481   return false;
482 }
483
484 bool StackedPanelCollection::IsPanelMinimized(const Panel* panel) const {
485   return panel->expansion_state() != Panel::EXPANDED;
486 }
487
488 bool StackedPanelCollection::UsesAlwaysOnTopPanels() const {
489   return false;
490 }
491
492 void StackedPanelCollection::SavePanelPlacement(Panel* panel) {
493   DCHECK(!saved_panel_placement_.panel);
494   saved_panel_placement_.panel = panel;
495
496   if (top_panel() != panel)
497     saved_panel_placement_.top_panel = top_panel();
498   else
499     saved_panel_placement_.position = panel->GetBounds().origin();
500
501   saved_panel_placement_.top_panel = top_panel() != panel ? top_panel() : NULL;
502 }
503
504 void StackedPanelCollection::RestorePanelToSavedPlacement() {
505   DCHECK(saved_panel_placement_.panel);
506
507   if (saved_panel_placement_.top_panel) {
508     // Restore the top panel if it has been moved out of the stack. This could
509     // happen when there're 2 panels in the stack and the bottom panel is being
510     // dragged out of the stack and thus cause both panels become detached.
511     if (saved_panel_placement_.top_panel->stack() != this) {
512       DCHECK_EQ(PanelCollection::DETACHED,
513                 saved_panel_placement_.top_panel->collection()->type());
514       panel_manager_->MovePanelToCollection(
515           saved_panel_placement_.top_panel,
516           this,
517           static_cast<PanelCollection::PositioningMask>(
518               PanelCollection::TOP_POSITION |
519               PanelCollection::NO_LAYOUT_REFRESH));
520     }
521     RefreshLayout();
522   } else {
523     // Restore the position when the top panel is being dragged.
524     DCHECK_EQ(top_panel(), saved_panel_placement_.panel);
525     RefreshLayoutWithTopPanelStartingAt(saved_panel_placement_.position,
526                                         top_panel()->GetBounds().width());
527   }
528
529   DiscardSavedPanelPlacement();
530 }
531
532 void StackedPanelCollection::DiscardSavedPanelPlacement() {
533   DCHECK(saved_panel_placement_.panel);
534   saved_panel_placement_.panel = NULL;
535   saved_panel_placement_.top_panel = NULL;
536 }
537
538 panel::Resizability StackedPanelCollection::GetPanelResizability(
539     const Panel* panel) const {
540   // The panel in the stack can be resized by the following rules:
541   // * If collapsed, it can only be resized by its left or right edge.
542   // * Otherwise, it can be resized by its left or right edge plus:
543   //   % top edge and corners, if it is at the top;
544   //   % bottom edge, if it is not at the bottom.
545   //   % bottom edge and corners, if it is at the bottom.
546   panel::Resizability resizability = static_cast<panel::Resizability>(
547       panel::RESIZABLE_LEFT | panel::RESIZABLE_RIGHT);
548   if (panel->IsMinimized())
549     return resizability;
550   if (panel == top_panel()) {
551     resizability = static_cast<panel::Resizability>(resizability |
552         panel::RESIZABLE_TOP | panel::RESIZABLE_TOP_LEFT |
553         panel::RESIZABLE_TOP_RIGHT);
554   }
555   if (panel == bottom_panel()) {
556     resizability = static_cast<panel::Resizability>(resizability |
557         panel::RESIZABLE_BOTTOM | panel::RESIZABLE_BOTTOM_LEFT |
558         panel::RESIZABLE_BOTTOM_RIGHT);
559   } else {
560     resizability = static_cast<panel::Resizability>(resizability |
561         panel::RESIZABLE_BOTTOM);
562   }
563   return resizability;
564 }
565
566 void StackedPanelCollection::OnPanelResizedByMouse(
567     Panel* resized_panel, const gfx::Rect& new_bounds) {
568   resized_panel->set_full_size(new_bounds.size());
569
570   DCHECK(!secondary_stack_window_);
571   primary_stack_window_->BeginBatchUpdatePanelBounds(false);
572
573   // The delta x and width can be computed from the difference between
574   // the panel being resized and any other panel.
575   Panel* other_panel = resized_panel == top_panel() ? bottom_panel()
576                                                     : top_panel();
577   gfx::Rect other_bounds = other_panel->GetBounds();
578   int delta_x = new_bounds.x() - other_bounds.x();
579   int delta_width = new_bounds.width() - other_bounds.width();
580
581   gfx::Rect previous_bounds;
582   bool resized_panel_found = false;
583   bool panel_below_resized_panel_updated = false;
584   for (Panels::const_iterator iter = panels_.begin();
585        iter != panels_.end(); iter++) {
586     Panel* panel = *iter;
587     if (panel == resized_panel) {
588       // |new_bounds| should be used since the panel bounds have not been
589       // updated yet.
590       previous_bounds = new_bounds;
591       resized_panel_found = true;
592       primary_stack_window_->AddPanelBoundsForBatchUpdate(panel, new_bounds);
593       continue;
594     }
595
596     gfx::Rect bounds = panel->GetBounds();
597     bounds.set_x(bounds.x() + delta_x);
598     bounds.set_width(bounds.width() + delta_width);
599
600     // If the panel below the panel being resized is expanded, update its
601     // height to offset the height change of the panel being resized.
602     // For example, the stack has P1 and P2 (from top to bottom). P1's height
603     // is 100 and P2's height is 120. If P1's bottom increases by 10, P2's
604     // height needs to shrink by 10.
605     if (resized_panel_found) {
606       if (!panel_below_resized_panel_updated && !panel->IsMinimized()) {
607         int old_bottom = bounds.bottom();
608         bounds.set_y(previous_bounds.bottom());
609         bounds.set_height(old_bottom - bounds.y());
610       } else {
611         bounds.set_y(previous_bounds.bottom());
612       }
613       panel_below_resized_panel_updated = true;
614     }
615
616     if (!panel->IsMinimized())
617       panel->set_full_size(bounds.size());
618
619     primary_stack_window_->AddPanelBoundsForBatchUpdate(panel, bounds);
620     previous_bounds = bounds;
621   }
622
623   primary_stack_window_->EndBatchUpdatePanelBounds();
624 }
625
626 bool StackedPanelCollection::HasPanel(Panel* panel) const {
627   return std::find(panels_.begin(), panels_.end(), panel) != panels_.end();
628 }
629
630 void StackedPanelCollection::UpdatePanelOnCollectionChange(Panel* panel) {
631   panel->set_attention_mode(
632       static_cast<Panel::AttentionMode>(Panel::USE_PANEL_ATTENTION |
633                                         Panel::USE_SYSTEM_ATTENTION));
634   panel->ShowShadow(false);
635   panel->UpdateMinimizeRestoreButtonVisibility();
636   UpdatePanelCornerStyle(panel);
637 }
638
639 void StackedPanelCollection::OnPanelExpansionStateChanged(Panel* panel) {
640   DCHECK_NE(Panel::MINIMIZED, panel->expansion_state());
641
642   // Ensure minimized panel does not get the focus. If minimizing all,
643   // the active panel will be deactivated once when all panels are minimized
644   // rather than per minimized panel.
645   if (panel->expansion_state() != Panel::EXPANDED && !minimizing_all_ &&
646       panel->IsActive()) {
647     panel->Deactivate();
648   }
649
650   // The bounds change per expansion state will be done in RefreshLayout.
651   RefreshLayout();
652 }
653
654 void StackedPanelCollection::OnPanelActiveStateChanged(Panel* panel) {
655   if (!panel->IsActive())
656     return;
657
658   // Move the panel to the front if not yet.
659   Panels::iterator iter = std::find(most_recently_active_panels_.begin(),
660       most_recently_active_panels_.end(), panel);
661   DCHECK(iter != most_recently_active_panels_.end());
662   if (iter != most_recently_active_panels_.begin()) {
663     most_recently_active_panels_.erase(iter);
664     most_recently_active_panels_.push_front(panel);
665   }
666
667   GetStackWindowForPanel(panel)->OnPanelActivated(panel);
668 }
669
670 gfx::Rect StackedPanelCollection::GetInitialPanelBounds(
671       const gfx::Rect& requested_bounds) const {
672   DCHECK(!panels_.empty());
673   gfx::Rect bottom_panel_bounds = bottom_panel()->GetBounds();
674   return gfx::Rect(bottom_panel_bounds.x(),
675                    bottom_panel_bounds.bottom(),
676                    bottom_panel_bounds.width(),
677                    requested_bounds.height());
678 }
679
680 Panel* StackedPanelCollection::GetPanelAbove(Panel* panel) const {
681   DCHECK(panel);
682
683   if (panels_.size() < 2)
684     return NULL;
685   Panels::const_iterator iter = panels_.begin();
686   Panel* above_panel = *iter;
687   for (; iter != panels_.end(); ++iter) {
688     if (*iter == panel)
689       return above_panel;
690     above_panel = *iter;
691   }
692   return NULL;
693 }
694
695 Panel* StackedPanelCollection::GetPanelBelow(Panel* panel) const {
696   DCHECK(panel);
697
698   if (panels_.size() < 2)
699     return NULL;
700   Panels::const_iterator iter =
701       std::find(panels_.begin(), panels_.end(), panel);
702   if (iter == panels_.end())
703     return NULL;
704   ++iter;
705   return iter == panels_.end() ? NULL : *iter;
706 }
707
708 void StackedPanelCollection::MoveAllDraggingPanelsInstantly(
709     const gfx::Vector2d& delta_origin) {
710   for (Panels::const_iterator iter = panels_.begin();
711        iter != panels_.end(); iter++) {
712     Panel* panel = *iter;
713     if (panel->in_preview_mode()) {
714       GetStackWindowForPanel(panel)->MovePanelsBy(delta_origin);
715       return;
716     }
717   }
718 }
719
720 bool StackedPanelCollection::IsMinimized() const {
721   return primary_stack_window_->IsMinimized();
722 }
723
724 bool StackedPanelCollection::IsAnimatingPanelBounds(Panel* panel) const {
725   return GetStackWindowForPanel(panel)->IsAnimatingPanelBounds();
726 }
727
728 void StackedPanelCollection::UpdatePanelCornerStyle(Panel* panel) {
729   panel::CornerStyle corner_style;
730   bool at_top = panel == top_panel();
731   bool at_bottom = panel == bottom_panel();
732   if (at_top && at_bottom)
733     corner_style = panel::ALL_ROUNDED;
734   else if (at_top)
735     corner_style = panel::TOP_ROUNDED;
736   else if (at_bottom)
737     corner_style = panel::BOTTOM_ROUNDED;
738   else
739     corner_style = panel::NOT_ROUNDED;
740   panel->SetWindowCornerStyle(corner_style);
741 }
742
743 gfx::Rect StackedPanelCollection::GetWorkArea() const {
744   if (panels_.empty())
745     return panel_manager_->display_settings_provider()->GetPrimaryWorkArea();
746   return panel_manager_->display_settings_provider()->GetWorkAreaMatching(
747       GetEnclosingBounds());
748 }
749
750 int StackedPanelCollection::GetCurrentAvailableTopSpace() const {
751   gfx::Rect work_area = GetWorkArea();
752   if (panels_.empty())
753     return work_area.height();
754
755   int available_space = top_panel()->GetBounds().y() - work_area.y();
756   if (available_space < 0)
757     available_space = 0;
758   return available_space;
759 }
760
761 int StackedPanelCollection::GetCurrentAvailableBottomSpace() const {
762   gfx::Rect work_area = GetWorkArea();
763   if (panels_.empty())
764     return work_area.height();
765
766   int available_space = work_area.bottom() -
767       bottom_panel()->GetBounds().bottom();
768   if (available_space < 0)
769     available_space = 0;
770   return available_space;
771 }
772
773 int StackedPanelCollection::GetMaximiumAvailableBottomSpace() const {
774   gfx::Rect work_area = GetWorkArea();
775   if (panels_.empty())
776     return work_area.height();
777
778   int bottom = top_panel()->GetBounds().y();
779   for (Panels::const_iterator iter = panels_.begin();
780        iter != panels_.end(); iter++) {
781     Panel* panel = *iter;
782     // Only the most recently active panel might be active.
783     if (iter == panels_.begin() && panel->IsActive())
784       bottom += panel->GetBounds().height();
785     else
786       bottom += panel::kTitlebarHeight;
787   }
788   int available_space = work_area.bottom() - bottom;
789   if (available_space < 0)
790     available_space = 0;
791   return available_space;
792 }
793
794 NativePanelStackWindow* StackedPanelCollection::GetStackWindowForPanel(
795     Panel* panel) const {
796   return secondary_stack_window_ && secondary_stack_window_->HasPanel(panel) ?
797       secondary_stack_window_ : primary_stack_window_;
798 }