19439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly// Copyright (c) 2013 The Chromium Authors. All rights reserved.
29439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly// Use of this source code is governed by a BSD-style license that can be
39439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly// found in the LICENSE file.
49439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
59439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "chrome/browser/ui/views/panels/panel_stack_view.h"
69439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
79439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "base/logging.h"
89439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "base/strings/utf_string_conversions.h"
99439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "chrome/browser/profiles/profile.h"
109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "chrome/browser/ui/panels/panel.h"
119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "chrome/browser/ui/panels/panel_manager.h"
129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "chrome/browser/ui/panels/stacked_panel_collection.h"
139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "chrome/browser/ui/views/panels/panel_view.h"
149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "ui/gfx/animation/linear_animation.h"
159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "ui/gfx/image/image_skia.h"
169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "ui/gfx/rect.h"
179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "ui/views/widget/widget.h"
189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#if defined(OS_WIN)
209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "base/win/windows_version.h"
219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "chrome/browser/shell_integration.h"
229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "ui/base/win/shell.h"
239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "ui/views/win/hwnd_util.h"
249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#endif
259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pellynamespace {
279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly// These values are experimental and subjective.
289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pellyconst int kDefaultFramerateHz = 50;
299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pellyconst int kSetBoundsAnimationMs = 180;
309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly// The widget window that acts as a background window for the stack of panels.
329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pellyclass PanelStackWindow : public views::WidgetObserver,
339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                         public views::WidgetDelegateView {
349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly public:
359439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly  PanelStackWindow(const gfx::Rect& bounds,
369439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                   NativePanelStackWindowDelegate* delegate);
3705ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun  virtual ~PanelStackWindow();
389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly  // Overridden from views::WidgetDelegate:
4005ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun  virtual base::string16 GetWindowTitle() const OVERRIDE;
4105ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun  virtual gfx::ImageSkia GetWindowAppIcon() OVERRIDE;
429439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly  virtual gfx::ImageSkia GetWindowIcon() OVERRIDE;
439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly  virtual views::Widget* GetWidget() OVERRIDE;
4405ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun  virtual const views::Widget* GetWidget() const OVERRIDE;
4505ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun
469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly  // Overridden from views::WidgetObserver:
479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly  virtual void OnWidgetClosing(views::Widget* widget) OVERRIDE;
489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly  virtual void OnWidgetDestroying(views::Widget* widget) OVERRIDE;
499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
5005ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun private:
5105ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun  views::Widget* window_;  // Weak pointer, own us.
5205ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun  NativePanelStackWindowDelegate* delegate_;  // Weak pointer.
5305ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun
5405ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun  DISALLOW_COPY_AND_ASSIGN(PanelStackWindow);
5505ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun};
5605ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun
5705ff98bbefda39b9ff26f8bca132cfd0248745c6Tao LiejunPanelStackWindow::PanelStackWindow(const gfx::Rect& bounds,
589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                   NativePanelStackWindowDelegate* delegate)
599439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    : window_(NULL),
609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly      delegate_(delegate) {
619439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly  window_ = new views::Widget;
629439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly  views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
639439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly  params.delegate = this;
6405ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun  params.remove_standard_frame = true;
6505ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun  params.bounds = bounds;
6605ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun  window_->Init(params);
6705ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun  window_->set_frame_type(views::Widget::FRAME_TYPE_FORCE_CUSTOM);
6805ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun  window_->set_focus_on_creation(false);
6905ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun  window_->AddObserver(this);
7005ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun  window_->ShowInactive();
7105ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun}
729439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
739439a7fe517b858bc5e5c654b459315e4722feb2Nick PellyPanelStackWindow::~PanelStackWindow() {
749439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly}
7505ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun
7605ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejunbase::string16 PanelStackWindow::GetWindowTitle() const {
7705ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun  return delegate_ ? delegate_->GetTitle() : base::string16();
789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly}
7905ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun
8005ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejungfx::ImageSkia PanelStackWindow::GetWindowAppIcon() {
812e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly  if (delegate_) {
829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    gfx::Image app_icon = delegate_->GetIcon();
839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    if (!app_icon.IsEmpty())
849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly      return *app_icon.ToImageSkia();
859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly  }
869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly  return gfx::ImageSkia();
879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly}
889439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pellygfx::ImageSkia PanelStackWindow::GetWindowIcon() {
9005ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun  return GetWindowAppIcon();
9105ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun}
9205ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun
9305ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejunviews::Widget* PanelStackWindow::GetWidget() {
949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly  return window_;
9505ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun}
9605ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun
9705ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejunconst views::Widget* PanelStackWindow::GetWidget() const {
9805ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun  return window_;
9905ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun}
1009439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
1013998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejunvoid PanelStackWindow::OnWidgetClosing(views::Widget* widget) {
1023998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun  delegate_ = NULL;
1039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly}
1049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
1059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pellyvoid PanelStackWindow::OnWidgetDestroying(views::Widget* widget) {
10605ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun  window_ = NULL;
1079439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly}
10805ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun
10905ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun}
1109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
11105ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun// static
11205ff98bbefda39b9ff26f8bca132cfd0248745c6Tao LiejunNativePanelStackWindow* NativePanelStackWindow::Create(
1139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    NativePanelStackWindowDelegate* delegate) {
1143998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun#if defined(OS_WIN)
1159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly  return new PanelStackView(delegate);
116#else
117  NOTIMPLEMENTED();
118  return NULL;
119#endif
120}
121
122PanelStackView::PanelStackView(NativePanelStackWindowDelegate* delegate)
123    : delegate_(delegate),
124      window_(NULL),
125      is_drawing_attention_(false),
126      animate_bounds_updates_(false),
127      bounds_updates_started_(false) {
128  DCHECK(delegate);
129  views::WidgetFocusManager::GetInstance()->AddFocusChangeListener(this);
130}
131
132PanelStackView::~PanelStackView() {
133#if defined(OS_WIN)
134  ui::HWNDSubclass::RemoveFilterFromAllTargets(this);
135#endif
136}
137
138void PanelStackView::Close() {
139  delegate_ = NULL;
140  if (bounds_animator_)
141    bounds_animator_.reset();
142  if (window_)
143    window_->Close();
144  views::WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(this);
145}
146
147void PanelStackView::AddPanel(Panel* panel) {
148  panels_.push_back(panel);
149
150  EnsureWindowCreated();
151  MakeStackWindowOwnPanelWindow(panel, this);
152  UpdateStackWindowBounds();
153
154  window_->UpdateWindowTitle();
155  window_->UpdateWindowIcon();
156}
157
158void PanelStackView::RemovePanel(Panel* panel) {
159  if (IsAnimatingPanelBounds()) {
160    // This panel is gone.
161    bounds_updates_.erase(panel);
162
163    // Abort the ongoing animation.
164    bounds_animator_->Stop();
165  }
166
167  panels_.remove(panel);
168
169  MakeStackWindowOwnPanelWindow(panel, NULL);
170  UpdateStackWindowBounds();
171}
172
173void PanelStackView::MergeWith(NativePanelStackWindow* another) {
174  PanelStackView* another_stack = static_cast<PanelStackView*>(another);
175
176  for (Panels::const_iterator iter = another_stack->panels_.begin();
177       iter != another_stack->panels_.end(); ++iter) {
178    Panel* panel = *iter;
179    panels_.push_back(panel);
180    MakeStackWindowOwnPanelWindow(panel, this);
181  }
182  another_stack->panels_.clear();
183
184  UpdateStackWindowBounds();
185}
186
187bool PanelStackView::IsEmpty() const {
188  return panels_.empty();
189}
190
191bool PanelStackView::HasPanel(Panel* panel) const {
192  return std::find(panels_.begin(), panels_.end(), panel) != panels_.end();
193}
194
195void PanelStackView::MovePanelsBy(const gfx::Vector2d& delta) {
196  BeginBatchUpdatePanelBounds(false);
197  for (Panels::const_iterator iter = panels_.begin();
198       iter != panels_.end(); ++iter) {
199    Panel* panel = *iter;
200    AddPanelBoundsForBatchUpdate(panel, panel->GetBounds() + delta);
201  }
202  EndBatchUpdatePanelBounds();
203}
204
205void PanelStackView::BeginBatchUpdatePanelBounds(bool animate) {
206  // If the batch animation is still in progress, continue the animation
207  // with the new target bounds even we want to update the bounds instantly
208  // this time.
209  if (!bounds_updates_started_) {
210    animate_bounds_updates_ = animate;
211    bounds_updates_started_ = true;
212  }
213}
214
215void PanelStackView::AddPanelBoundsForBatchUpdate(Panel* panel,
216                                                  const gfx::Rect& new_bounds) {
217  DCHECK(bounds_updates_started_);
218
219  // No need to track it if no change is needed.
220  if (panel->GetBounds() == new_bounds)
221    return;
222
223  // Old bounds are stored as the map value.
224  bounds_updates_[panel] = panel->GetBounds();
225
226  // New bounds are directly applied to the valued stored in native panel
227  // window.
228  static_cast<PanelView*>(panel->native_panel())->set_cached_bounds_directly(
229      new_bounds);
230}
231
232void PanelStackView::EndBatchUpdatePanelBounds() {
233  DCHECK(bounds_updates_started_);
234
235  if (bounds_updates_.empty() || !animate_bounds_updates_) {
236    if (!bounds_updates_.empty()) {
237      UpdatePanelsBounds();
238      bounds_updates_.clear();
239    }
240
241    bounds_updates_started_ = false;
242    NotifyBoundsUpdateCompleted();
243    return;
244  }
245
246  bounds_animator_.reset(new gfx::LinearAnimation(
247      PanelManager::AdjustTimeInterval(kSetBoundsAnimationMs),
248      kDefaultFramerateHz,
249      this));
250  bounds_animator_->Start();
251}
252
253void PanelStackView::NotifyBoundsUpdateCompleted() {
254  delegate_->PanelBoundsBatchUpdateCompleted();
255
256#if defined(OS_WIN)
257  // Refresh the thumbnail each time when any bounds updates are done.
258  RefreshLivePreviewThumbnail();
259#endif
260}
261
262bool PanelStackView::IsAnimatingPanelBounds() const {
263  return bounds_updates_started_ && animate_bounds_updates_;
264}
265
266void PanelStackView::Minimize() {
267#if defined(OS_WIN)
268  // When the stack window is minimized by the system, its snapshot could not
269  // be obtained. We need to capture the snapshot before the minimization.
270  if (thumbnailer_)
271    thumbnailer_->CaptureSnapshot();
272#endif
273
274  window_->Minimize();
275}
276
277bool PanelStackView::IsMinimized() const {
278  return window_ ? window_->IsMinimized() : false;
279}
280
281void PanelStackView::DrawSystemAttention(bool draw_attention) {
282  // The underlying call of FlashFrame, FlashWindowEx, seems not to work
283  // correctly if it is called more than once consecutively.
284  if (draw_attention == is_drawing_attention_)
285    return;
286  is_drawing_attention_ = draw_attention;
287
288#if defined(OS_WIN)
289  // Refresh the thumbnail when a panel could change something for the
290  // attention.
291  RefreshLivePreviewThumbnail();
292
293  if (draw_attention) {
294    // The default implementation of Widget::FlashFrame only flashes 5 times.
295    // We need more than that.
296    FLASHWINFO fwi;
297    fwi.cbSize = sizeof(fwi);
298    fwi.hwnd = views::HWNDForWidget(window_);
299    fwi.dwFlags = FLASHW_ALL;
300    fwi.uCount = panel::kNumberOfTimesToFlashPanelForAttention;
301    fwi.dwTimeout = 0;
302    ::FlashWindowEx(&fwi);
303  } else {
304    // Calling FlashWindowEx with FLASHW_STOP flag does not always work.
305    // Occasionally the taskbar icon could still remain in the flashed state.
306    // To work around this problem, we recreate the underlying window.
307    views::Widget* old_window = window_;
308    window_ = CreateWindowWithBounds(GetStackWindowBounds());
309
310    // New background window should also be minimized if the old one is.
311    if (old_window->IsMinimized())
312      window_->Minimize();
313
314    // Make sure the new background window stays at the same z-order as the old
315    // one.
316    ::SetWindowPos(views::HWNDForWidget(window_),
317                   views::HWNDForWidget(old_window),
318                   0, 0, 0, 0,
319                   SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
320    for (Panels::const_iterator iter = panels_.begin();
321         iter != panels_.end(); ++iter) {
322      MakeStackWindowOwnPanelWindow(*iter, this);
323    }
324
325    // Serve the snapshot to the new backgroud window.
326    if (thumbnailer_.get())
327      thumbnailer_->ReplaceWindow(views::HWNDForWidget(window_));
328
329    window_->UpdateWindowTitle();
330    window_->UpdateWindowIcon();
331    old_window->Close();
332  }
333#else
334  window_->FlashFrame(draw_attention);
335#endif
336}
337
338void PanelStackView::OnPanelActivated(Panel* panel) {
339  // Nothing to do.
340}
341
342void PanelStackView::OnNativeFocusChange(gfx::NativeView focused_before,
343                                         gfx::NativeView focused_now) {
344  // When the user selects the stacked panels via ALT-TAB or WIN-TAB, the
345  // background stack window, instead of the foreground panel window, receives
346  // WM_SETFOCUS message. To deal with this, we listen to the focus change event
347  // and activate the most recently active panel.
348  // Note that OnNativeFocusChange might be called when window_ has not be
349  // created yet.
350#if defined(OS_WIN)
351  if (!panels_.empty() && window_ && focused_now == window_->GetNativeView()) {
352    Panel* panel_to_focus =
353        panels_.front()->stack()->most_recently_active_panel();
354    if (panel_to_focus)
355      panel_to_focus->Activate();
356  }
357#endif
358}
359
360void PanelStackView::AnimationEnded(const gfx::Animation* animation) {
361  bounds_updates_started_ = false;
362
363  PanelManager* panel_manager = PanelManager::GetInstance();
364  for (BoundsUpdates::const_iterator iter = bounds_updates_.begin();
365       iter != bounds_updates_.end(); ++iter) {
366    panel_manager->OnPanelAnimationEnded(iter->first);
367  }
368  bounds_updates_.clear();
369
370  NotifyBoundsUpdateCompleted();
371}
372
373void PanelStackView::AnimationCanceled(const gfx::Animation* animation) {
374  // When the animation is aborted due to something like one of panels is gone,
375  // update panels to their taget bounds immediately.
376  UpdatePanelsBounds();
377
378  AnimationEnded(animation);
379}
380
381void PanelStackView::AnimationProgressed(const gfx::Animation* animation) {
382  UpdatePanelsBounds();
383}
384
385void PanelStackView::UpdatePanelsBounds() {
386#if defined(OS_WIN)
387  // Add an extra count for the background stack window.
388  HDWP defer_update = ::BeginDeferWindowPos(bounds_updates_.size() + 1);
389#endif
390
391  // Update the bounds for each panel in the update list.
392  gfx::Rect enclosing_bounds;
393  for (BoundsUpdates::const_iterator iter = bounds_updates_.begin();
394       iter != bounds_updates_.end(); ++iter) {
395    Panel* panel = iter->first;
396    gfx::Rect target_bounds = panel->GetBounds();
397    gfx::Rect current_bounds;
398    if (bounds_animator_ && bounds_animator_->is_animating()) {
399      current_bounds = bounds_animator_->CurrentValueBetween(
400          iter->second, target_bounds);
401    } else {
402      current_bounds = target_bounds;
403    }
404
405    PanelView* panel_view = static_cast<PanelView*>(panel->native_panel());
406#if defined(OS_WIN)
407    DeferUpdateNativeWindowBounds(defer_update,
408                                  panel_view->window(),
409                                  current_bounds);
410#else
411    panel_view->SetPanelBoundsInstantly(current_bounds);
412#endif
413
414    enclosing_bounds = UnionRects(enclosing_bounds, current_bounds);
415  }
416
417  // Compute the stack window bounds that enclose those panels that are not
418  // in the batch update list.
419  for (Panels::const_iterator iter = panels_.begin();
420       iter != panels_.end(); ++iter) {
421    Panel* panel = *iter;
422    if (bounds_updates_.find(panel) == bounds_updates_.end())
423      enclosing_bounds = UnionRects(enclosing_bounds, panel->GetBounds());
424  }
425
426  // Update the bounds of the background stack window.
427#if defined(OS_WIN)
428  DeferUpdateNativeWindowBounds(defer_update, window_, enclosing_bounds);
429#else
430  window_->SetBounds(enclosing_bounds);
431#endif
432
433#if defined(OS_WIN)
434  ::EndDeferWindowPos(defer_update);
435#endif
436}
437
438gfx::Rect PanelStackView::GetStackWindowBounds() const {
439  gfx::Rect enclosing_bounds;
440  for (Panels::const_iterator iter = panels_.begin();
441       iter != panels_.end(); ++iter) {
442    Panel* panel = *iter;
443    enclosing_bounds = UnionRects(enclosing_bounds, panel->GetBounds());
444  }
445  return enclosing_bounds;
446}
447
448void PanelStackView::UpdateStackWindowBounds() {
449  window_->SetBounds(GetStackWindowBounds());
450
451#if defined(OS_WIN)
452  // Refresh the thumbnail each time whne the stack window is changed, due to
453  // adding or removing a panel.
454  RefreshLivePreviewThumbnail();
455#endif
456}
457
458// static
459void PanelStackView::MakeStackWindowOwnPanelWindow(
460    Panel* panel, PanelStackView* stack_window) {
461#if defined(OS_WIN)
462  // The panel widget window might already be gone when a panel is closed.
463  views::Widget* panel_window =
464      static_cast<PanelView*>(panel->native_panel())->window();
465  if (!panel_window)
466    return;
467
468  HWND native_panel_window = views::HWNDForWidget(panel_window);
469  HWND native_stack_window =
470      stack_window ? views::HWNDForWidget(stack_window->window_) : NULL;
471
472  // The extended style WS_EX_APPWINDOW is used to force a top-level window onto
473  // the taskbar. In order for multiple stacked panels to appear as one, this
474  // bit needs to be cleared.
475  int value = ::GetWindowLong(native_panel_window, GWL_EXSTYLE);
476  ::SetWindowLong(
477      native_panel_window,
478      GWL_EXSTYLE,
479      native_stack_window ? (value & ~WS_EX_APPWINDOW)
480                          : (value | WS_EX_APPWINDOW));
481
482  // All the windows that share the same owner window will appear as a single
483  // window on the taskbar.
484  ::SetWindowLongPtr(native_panel_window,
485                     GWLP_HWNDPARENT,
486                     reinterpret_cast<LONG_PTR>(native_stack_window));
487
488  // Make sure the background stack window always stays behind the panel window.
489  if (native_stack_window) {
490    ::SetWindowPos(native_stack_window, native_panel_window, 0, 0, 0, 0,
491        SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
492  }
493
494#else
495  NOTIMPLEMENTED();
496#endif
497}
498
499views::Widget* PanelStackView::CreateWindowWithBounds(const gfx::Rect& bounds) {
500  PanelStackWindow* stack_window = new PanelStackWindow(bounds, delegate_);
501  views::Widget* window = stack_window->GetWidget();
502
503#if defined(OS_WIN)
504  DCHECK(!panels_.empty());
505  Panel* panel = panels_.front();
506  ui::win::SetAppIdForWindow(
507      ShellIntegration::GetAppModelIdForProfile(
508          base::UTF8ToWide(panel->app_name()), panel->profile()->GetPath()),
509      views::HWNDForWidget(window));
510
511  // Remove the filter for old window in case that we're recreating the window.
512  ui::HWNDSubclass::RemoveFilterFromAllTargets(this);
513
514  // Listen to WM_MOVING message in order to move all panels windows on top of
515  // the background window altogether when the background window is being moved
516  // by the user.
517  ui::HWNDSubclass::AddFilterToTarget(views::HWNDForWidget(window), this);
518#endif
519
520  return window;
521}
522
523void PanelStackView::EnsureWindowCreated() {
524  if (window_)
525    return;
526
527  // Empty size is not allowed so a temporary small size is passed. SetBounds
528  // will be called later to update the bounds.
529  window_ = CreateWindowWithBounds(gfx::Rect(0, 0, 1, 1));
530
531#if defined(OS_WIN)
532  if (base::win::GetVersion() >= base::win::VERSION_WIN7) {
533    HWND native_window = views::HWNDForWidget(window_);
534    thumbnailer_.reset(new TaskbarWindowThumbnailerWin(native_window, this));
535    thumbnailer_->Start();
536  }
537#endif
538}
539
540#if defined(OS_WIN)
541bool PanelStackView::FilterMessage(HWND hwnd,
542                                   UINT message,
543                                   WPARAM w_param,
544                                   LPARAM l_param,
545                                   LRESULT* l_result) {
546  switch (message) {
547    case WM_MOVING:
548      // When the background window is being moved by the user, all panels
549      // should also move.
550      gfx::Rect new_stack_bounds(*(reinterpret_cast<LPRECT>(l_param)));
551      MovePanelsBy(
552          new_stack_bounds.origin() - panels_.front()->GetBounds().origin());
553      break;
554  }
555  return false;
556}
557
558std::vector<HWND> PanelStackView::GetSnapshotWindowHandles() const {
559  std::vector<HWND> native_panel_windows;
560  for (Panels::const_iterator iter = panels_.begin();
561       iter != panels_.end(); ++iter) {
562    Panel* panel = *iter;
563    native_panel_windows.push_back(
564        views::HWNDForWidget(
565            static_cast<PanelView*>(panel->native_panel())->window()));
566  }
567  return native_panel_windows;
568}
569
570void PanelStackView::RefreshLivePreviewThumbnail() {
571  // Don't refresh the thumbnail when the stack window is system minimized
572  // because the snapshot could not be retrieved.
573  if (!thumbnailer_.get() || IsMinimized())
574    return;
575  thumbnailer_->InvalidateSnapshot();
576}
577
578void PanelStackView::DeferUpdateNativeWindowBounds(HDWP defer_window_pos_info,
579                                                   views::Widget* window,
580                                                   const gfx::Rect& bounds) {
581  ::DeferWindowPos(defer_window_pos_info,
582                    views::HWNDForWidget(window),
583                    NULL,
584                    bounds.x(),
585                    bounds.y(),
586                    bounds.width(),
587                    bounds.height(),
588                    SWP_NOACTIVATE | SWP_NOZORDER);
589}
590#endif
591