panel_view.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
1c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// found in the LICENSE file.
4c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
5c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "chrome/browser/ui/views/panels/panel_view.h"
6c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
7c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include <map>
8c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "base/logging.h"
9f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "base/message_loop.h"
10c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "base/strings/utf_string_conversions.h"
11c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "chrome/app/chrome_command_ids.h"
12c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "chrome/browser/profiles/profile.h"
13c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "chrome/browser/ui/panels/panel.h"
14c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "chrome/browser/ui/panels/panel_bounds_animation.h"
15c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "chrome/browser/ui/panels/panel_manager.h"
16c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "chrome/browser/ui/panels/stacked_panel_collection.h"
17c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "chrome/browser/ui/views/panels/panel_frame_view.h"
18c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "content/public/browser/render_view_host.h"
19c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "content/public/browser/render_widget_host_view.h"
20c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "content/public/browser/web_contents.h"
21c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "content/public/browser/web_contents_view.h"
22c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "ui/gfx/image/image.h"
23c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "ui/gfx/path.h"
24c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "ui/gfx/screen.h"
25c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "ui/views/controls/button/image_button.h"
26c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "ui/views/controls/webview/webview.h"
27c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "ui/views/widget/widget.h"
28c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
29c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#if defined(OS_WIN)
30c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "base/win/windows_version.h"
31c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "chrome/browser/shell_integration.h"
32c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "chrome/browser/ui/views/panels/taskbar_window_thumbnailer_win.h"
33c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "ui/base/win/shell.h"
34c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "ui/gfx/icon_util.h"
35c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "ui/views/win/hwnd_util.h"
36c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#endif
37c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
38c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochnamespace {
39c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
40c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// If the height of a stacked panel shrinks below this threshold during the
41c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// user resizing, it will be treated as minimized.
42c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochconst int kStackedPanelHeightShrinkThresholdToBecomeMinimized =
43c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    panel::kTitlebarHeight + 20;
44c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
45c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// Supported accelerators.
46c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// Note: We can't use the acclerator table defined in chrome/browser/ui/views
47c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// due to checkdeps violation.
48c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochstruct AcceleratorMapping {
49c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  ui::KeyboardCode keycode;
50c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  int modifiers;
51c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  int command_id;
52c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch};
53c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochconst AcceleratorMapping kPanelAcceleratorMap[] = {
54c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  { ui::VKEY_W, ui::EF_CONTROL_DOWN, IDC_CLOSE_WINDOW },
55c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  { ui::VKEY_W, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, IDC_CLOSE_WINDOW },
56c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  { ui::VKEY_F4, ui::EF_ALT_DOWN, IDC_CLOSE_WINDOW },
57c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  { ui::VKEY_R, ui::EF_CONTROL_DOWN, IDC_RELOAD },
58c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  { ui::VKEY_F5, ui::EF_NONE, IDC_RELOAD },
59c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  { ui::VKEY_R, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN,
60c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      IDC_RELOAD_IGNORING_CACHE },
61c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  { ui::VKEY_F5, ui::EF_CONTROL_DOWN, IDC_RELOAD_IGNORING_CACHE },
62c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  { ui::VKEY_F5, ui::EF_SHIFT_DOWN, IDC_RELOAD_IGNORING_CACHE },
63c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  { ui::VKEY_ESCAPE, ui::EF_NONE, IDC_STOP },
64c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  { ui::VKEY_OEM_MINUS, ui::EF_CONTROL_DOWN, IDC_ZOOM_MINUS },
65c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  { ui::VKEY_SUBTRACT, ui::EF_CONTROL_DOWN, IDC_ZOOM_MINUS },
66c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  { ui::VKEY_0, ui::EF_CONTROL_DOWN, IDC_ZOOM_NORMAL },
67c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  { ui::VKEY_NUMPAD0, ui::EF_CONTROL_DOWN, IDC_ZOOM_NORMAL },
68c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  { ui::VKEY_OEM_PLUS, ui::EF_CONTROL_DOWN, IDC_ZOOM_PLUS },
69c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  { ui::VKEY_ADD, ui::EF_CONTROL_DOWN, IDC_ZOOM_PLUS },
70c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  { ui::VKEY_I, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, IDC_DEV_TOOLS },
71c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  { ui::VKEY_J, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN,
72c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    IDC_DEV_TOOLS_CONSOLE },
73c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch};
74c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
75c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochconst std::map<ui::Accelerator, int>& GetAcceleratorTable() {
76c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  static std::map<ui::Accelerator, int>* accelerators = NULL;
77c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (!accelerators) {
78c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    accelerators = new std::map<ui::Accelerator, int>();
79c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    for (size_t i = 0; i < arraysize(kPanelAcceleratorMap); ++i) {
80c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      ui::Accelerator accelerator(kPanelAcceleratorMap[i].keycode,
81c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                                  kPanelAcceleratorMap[i].modifiers);
82c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      (*accelerators)[accelerator] = kPanelAcceleratorMap[i].command_id;
83c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    }
84c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  }
85c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return *accelerators;
86c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
87c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
88c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// NativePanelTesting implementation.
89c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochclass NativePanelTestingWin : public NativePanelTesting {
90c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch public:
91c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  explicit NativePanelTestingWin(PanelView* panel_view);
92c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
93c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch private:
94c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  virtual void PressLeftMouseButtonTitlebar(
95c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      const gfx::Point& mouse_location, panel::ClickModifier modifier) OVERRIDE;
96c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  virtual void ReleaseMouseButtonTitlebar(
97c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      panel::ClickModifier modifier) OVERRIDE;
98c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  virtual void DragTitlebar(const gfx::Point& mouse_location) OVERRIDE;
99c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  virtual void CancelDragTitlebar() OVERRIDE;
100c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  virtual void FinishDragTitlebar() OVERRIDE;
101c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  virtual bool VerifyDrawingAttention() const OVERRIDE;
102c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  virtual bool VerifyActiveState(bool is_active) OVERRIDE;
103c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  virtual bool VerifyAppIcon() const OVERRIDE;
104c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  virtual bool VerifySystemMinimizeState() const OVERRIDE;
105c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  virtual bool IsWindowSizeKnown() const OVERRIDE;
106c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  virtual bool IsAnimatingBounds() const OVERRIDE;
107c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  virtual bool IsButtonVisible(
108c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      panel::TitlebarButtonType button_type) const OVERRIDE;
109c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  virtual panel::CornerStyle GetWindowCornerStyle() const OVERRIDE;
110c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
111c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  PanelView* panel_view_;
112c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch};
113c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
114c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen MurdochNativePanelTestingWin::NativePanelTestingWin(PanelView* panel_view)
115c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    : panel_view_(panel_view) {
116c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
117c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
118c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochvoid NativePanelTestingWin::PressLeftMouseButtonTitlebar(
119c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    const gfx::Point& mouse_location, panel::ClickModifier modifier) {
120c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  panel_view_->OnTitlebarMousePressed(mouse_location);
121c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
122c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
123c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochvoid NativePanelTestingWin::ReleaseMouseButtonTitlebar(
124c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    panel::ClickModifier modifier) {
125c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  panel_view_->OnTitlebarMouseReleased(modifier);
126c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
127c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
128c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochvoid NativePanelTestingWin::DragTitlebar(const gfx::Point& mouse_location) {
129c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  panel_view_->OnTitlebarMouseDragged(mouse_location);
130c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
131c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
132c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochvoid NativePanelTestingWin::CancelDragTitlebar() {
133c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  panel_view_->OnTitlebarMouseCaptureLost();
134c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
135c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
136c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochvoid NativePanelTestingWin::FinishDragTitlebar() {
137c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  panel_view_->OnTitlebarMouseReleased(panel::NO_MODIFIER);
138c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
139c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
140c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochbool NativePanelTestingWin::VerifyDrawingAttention() const {
141c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  base::MessageLoop::current()->RunUntilIdle();
142c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return panel_view_->GetFrameView()->GetPaintState() ==
143c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch         PanelFrameView::PAINT_FOR_ATTENTION;
144c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
145c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
146c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochbool NativePanelTestingWin::VerifyActiveState(bool is_active) {
147c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return panel_view_->GetFrameView()->GetPaintState() ==
148c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch         (is_active ? PanelFrameView::PAINT_AS_ACTIVE
149c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                    : PanelFrameView::PAINT_AS_INACTIVE);
150c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
151c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
152c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochbool NativePanelTestingWin::VerifyAppIcon() const {
153c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#if defined(OS_WIN)
154c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // We only care about Windows 7 and later.
1551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (base::win::GetVersion() < base::win::VERSION_WIN7)
156c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    return true;
157c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
158c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  HWND native_window = views::HWNDForWidget(panel_view_->window());
159c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  HICON app_icon = reinterpret_cast<HICON>(
160c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      ::SendMessage(native_window, WM_GETICON, ICON_BIG, 0L));
161c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (!app_icon)
162c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    return false;
163c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  scoped_ptr<SkBitmap> bitmap(IconUtil::CreateSkBitmapFromHICON(app_icon));
164c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return bitmap.get() &&
165c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch         bitmap->width() == panel::kPanelAppIconSize &&
166c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch         bitmap->height() == panel::kPanelAppIconSize;
167c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#else
168c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return true;
169c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#endif
170c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
171c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
172c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochbool NativePanelTestingWin::VerifySystemMinimizeState() const {
173c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#if defined(OS_WIN)
174c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  HWND native_window = views::HWNDForWidget(panel_view_->window());
175c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  WINDOWPLACEMENT placement;
176c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (!::GetWindowPlacement(native_window, &placement))
177c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    return false;
178c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (placement.showCmd == SW_MINIMIZE || placement.showCmd == SW_SHOWMINIMIZED)
179c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    return true;
180c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
181c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // If the panel window has owner window, as in stacked mode, check its owner
182c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // window. Note that owner window, instead of parent window, is returned
183c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // though GWL_HWNDPARENT contains 'parent'.
1841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  HWND owner_window =
185c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      reinterpret_cast<HWND>(::GetWindowLongPtr(native_window,
186c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                                                GWLP_HWNDPARENT));
187c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (!owner_window || !::GetWindowPlacement(owner_window, &placement))
188c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    return false;
189c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return placement.showCmd == SW_MINIMIZE ||
190c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch         placement.showCmd == SW_SHOWMINIMIZED;
191c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#else
192c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return true;
193c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#endif
194c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
195c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
196c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochbool NativePanelTestingWin::IsWindowSizeKnown() const {
197c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return true;
198c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
199c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
200c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochbool NativePanelTestingWin::IsAnimatingBounds() const {
201c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return panel_view_->IsAnimatingBounds();
202c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
2031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
204c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochbool NativePanelTestingWin::IsButtonVisible(
205c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    panel::TitlebarButtonType button_type) const {
206c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  PanelFrameView* frame_view = panel_view_->GetFrameView();
207c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
208c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  switch (button_type) {
209c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    case panel::CLOSE_BUTTON:
210c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      return frame_view->close_button()->visible();
211c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    case panel::MINIMIZE_BUTTON:
212c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      return frame_view->minimize_button()->visible();
213c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    case panel::RESTORE_BUTTON:
214c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      return frame_view->restore_button()->visible();
215    default:
216      NOTREACHED();
217  }
218  return false;
219}
220
221panel::CornerStyle NativePanelTestingWin::GetWindowCornerStyle() const {
222  return panel_view_->GetFrameView()->corner_style();
223}
224
225}  // namespace
226
227// static
228NativePanel* Panel::CreateNativePanel(Panel* panel,
229                                      const gfx::Rect& bounds,
230                                      bool always_on_top) {
231  return new PanelView(panel, bounds, always_on_top);
232}
233
234// The panel window has to be created as always-on-top. We cannot create it
235// as non-always-on-top and then change it to always-on-top because Windows
236// system might deny making a window always-on-top if the application is not
237// a foreground application.
238PanelView::PanelView(Panel* panel, const gfx::Rect& bounds, bool always_on_top)
239    : panel_(panel),
240      bounds_(bounds),
241      window_(NULL),
242      window_closed_(false),
243      web_view_(NULL),
244      always_on_top_(always_on_top),
245      focused_(false),
246      user_resizing_(false),
247#if defined(OS_WIN)
248      user_resizing_interior_stacked_panel_edge_(false),
249#endif
250      mouse_pressed_(false),
251      mouse_dragging_state_(NO_DRAGGING),
252      is_drawing_attention_(false),
253      force_to_paint_as_inactive_(false),
254      old_focused_view_(NULL) {
255  window_ = new views::Widget;
256  views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
257  params.delegate = this;
258  params.remove_standard_frame = true;
259  params.keep_on_top = always_on_top;
260  params.bounds = bounds;
261  window_->Init(params);
262  window_->set_frame_type(views::Widget::FRAME_TYPE_FORCE_CUSTOM);
263  window_->set_focus_on_creation(false);
264  window_->AddObserver(this);
265
266  web_view_ = new views::WebView(NULL);
267  AddChildView(web_view_);
268
269  OnViewWasResized();
270
271  // Register accelarators supported by panels.
272  views::FocusManager* focus_manager = GetFocusManager();
273  const std::map<ui::Accelerator, int>& accelerator_table =
274      GetAcceleratorTable();
275  for (std::map<ui::Accelerator, int>::const_iterator iter =
276           accelerator_table.begin();
277       iter != accelerator_table.end(); ++iter) {
278    focus_manager->RegisterAccelerator(
279        iter->first, ui::AcceleratorManager::kNormalPriority, this);
280  }
281
282#if defined(OS_WIN)
283  ui::win::SetAppIdForWindow(
284      ShellIntegration::GetAppModelIdForProfile(UTF8ToWide(panel->app_name()),
285                                                panel->profile()->GetPath()),
286      views::HWNDForWidget(window_));
287#endif
288
289  views::WidgetFocusManager::GetInstance()->AddFocusChangeListener(this);
290}
291
292PanelView::~PanelView() {
293}
294
295void PanelView::ShowPanel() {
296  ShowPanelInactive();
297  ActivatePanel();
298}
299
300void PanelView::ShowPanelInactive() {
301  if (window_->IsVisible())
302    return;
303  window_->ShowInactive();
304  // No animation is used for initial creation of a panel on Win.
305  // Signal immediately that pending actions can be performed.
306  panel_->manager()->OnPanelAnimationEnded(panel_.get());
307}
308
309gfx::Rect PanelView::GetPanelBounds() const {
310  return bounds_;
311}
312
313void PanelView::SetPanelBounds(const gfx::Rect& bounds) {
314  SetBoundsInternal(bounds, true);
315}
316
317void PanelView::SetPanelBoundsInstantly(const gfx::Rect& bounds) {
318  SetBoundsInternal(bounds, false);
319}
320
321void PanelView::SetBoundsInternal(const gfx::Rect& new_bounds, bool animate) {
322  if (bounds_ == new_bounds)
323    return;
324
325  bounds_ = new_bounds;
326
327  if (!animate) {
328    // If no animation is in progress, apply bounds change instantly. Otherwise,
329    // continue the animation with new target bounds.
330    if (!IsAnimatingBounds())
331      SetWidgetBounds(bounds_);
332    return;
333  }
334
335  animation_start_bounds_ = window_->GetWindowBoundsInScreen();
336
337  bounds_animator_.reset(new PanelBoundsAnimation(
338      this, panel_.get(), animation_start_bounds_, new_bounds));
339  bounds_animator_->Start();
340}
341
342#if defined(OS_WIN)
343bool PanelView::FilterMessage(HWND hwnd,
344                              UINT message,
345                              WPARAM w_param,
346                              LPARAM l_param,
347                              LRESULT* l_result) {
348  switch (message) {
349    case WM_SIZING:
350      if (w_param == WMSZ_BOTTOM)
351        user_resizing_interior_stacked_panel_edge_ = true;
352      break;
353  }
354  return false;
355}
356#endif
357
358void PanelView::AnimationEnded(const ui::Animation* animation) {
359  panel_->manager()->OnPanelAnimationEnded(panel_.get());
360}
361
362void PanelView::AnimationProgressed(const ui::Animation* animation) {
363  gfx::Rect new_bounds = bounds_animator_->CurrentValueBetween(
364      animation_start_bounds_, bounds_);
365  SetWidgetBounds(new_bounds);
366}
367
368void PanelView::SetWidgetBounds(const gfx::Rect& new_bounds) {
369#if defined(OS_WIN)
370  // An overlapped window is a top-level window that has a titlebar, border,
371  // and client area. The Windows system will automatically put the shadow
372  // around the whole window. Also the system will enforce the minimum height
373  // (38 pixels based on observation) for the overlapped window such that it
374  // will always has the space for the titlebar.
375  //
376  // On contrast, a popup window is a bare minimum window without border and
377  // titlebar by default. It is often used for the popup menu and the window
378  // with short life. The Windows system does not add the shadow around the
379  // whole window though CS_DROPSHADOW class style could be passed to add the
380  // drop shadow which is only around the right and bottom edges.
381  //
382  // The height of the title-only or minimized panel is smaller than the minimum
383  // overlapped window height. If the panel still uses the overlapped window
384  // style, Windows system will automatically increase the window height. To
385  // work around this limitation, we temporarily change the window style to
386  // popup when the height to set is smaller than the minimum overlapped window
387  // height and then restore the window style to overlapped when the height
388  // grows.
389  static const int kMinimumOverlappedWindowHeight = 38;
390  gfx::Rect old_bounds = GetWidget()->GetRestoredBounds();
391  if (old_bounds.height() > kMinimumOverlappedWindowHeight &&
392      new_bounds.height() <= kMinimumOverlappedWindowHeight) {
393    // When the panel height shrinks below the minimum overlapped window height,
394    // change the window style to popup such that we can show the title-only
395    // and minimized panel without additional height being added by the system.
396    UpdateWindowAttribute(GWL_STYLE,
397                          WS_POPUP,
398                          WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU,
399                          true);
400  } else if (old_bounds.height() <= kMinimumOverlappedWindowHeight &&
401             new_bounds.height() > kMinimumOverlappedWindowHeight) {
402    // Change the window style back to overlappped when the panel height grow
403    // taller than the minimum overlapped window height.
404    UpdateWindowAttribute(GWL_STYLE,
405                          WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU,
406                          WS_POPUP,
407                          true);
408  }
409#endif
410
411  GetWidget()->SetBounds(new_bounds);
412}
413
414void PanelView::ClosePanel() {
415  // We're already closing. Do nothing.
416  if (window_closed_)
417    return;
418
419  if (!panel_->ShouldCloseWindow())
420    return;
421
422  // Cancel any currently running animation since we're closing down.
423  if (bounds_animator_.get())
424    bounds_animator_.reset();
425
426  if (panel_->GetWebContents()) {
427    // Still have web contents. Allow renderer to shut down.
428    // When web contents are destroyed, we will be called back again.
429    panel_->OnWindowClosing();
430    return;
431  }
432
433  views::WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(this);
434
435  panel_->OnNativePanelClosed();
436  if (window_)
437    window_->Close();
438  window_closed_ = true;
439}
440
441void PanelView::ActivatePanel() {
442  window_->Activate();
443}
444
445void PanelView::DeactivatePanel() {
446  if (!focused_)
447    return;
448
449#if defined(OS_WIN)
450  // Need custom behavior for always-on-top panels to avoid
451  // the OS activating a minimized panel when this one is
452  // deactivated.
453  if (always_on_top_) {
454    ::SetForegroundWindow(::GetDesktopWindow());
455    return;
456  }
457#endif
458
459  window_->Deactivate();
460}
461
462bool PanelView::IsPanelActive() const {
463  return focused_;
464}
465
466void PanelView::PreventActivationByOS(bool prevent_activation) {
467#if defined(OS_WIN)
468  // Set the flags "NoActivate" to make sure the minimized panels do not get
469  // activated by the OS. In addition, set "AppWindow" to make sure the
470  // minimized panels do appear in the taskbar and Alt-Tab menu if it is not
471  // in a stack.
472  int value_to_change = WS_EX_NOACTIVATE;
473  if (!panel_->stack())
474    value_to_change |= WS_EX_APPWINDOW;
475  if (prevent_activation)
476    UpdateWindowAttribute(GWL_EXSTYLE, value_to_change, 0, false);
477  else
478    UpdateWindowAttribute(GWL_EXSTYLE, 0, value_to_change, false);
479#endif
480}
481
482gfx::NativeWindow PanelView::GetNativePanelWindow() {
483  return window_->GetNativeWindow();
484}
485
486void PanelView::UpdatePanelTitleBar() {
487  UpdateWindowTitle();
488  UpdateWindowIcon();
489}
490
491void PanelView::UpdatePanelLoadingAnimations(bool should_animate) {
492  GetFrameView()->UpdateThrobber();
493}
494
495void PanelView::PanelWebContentsFocused(content::WebContents* contents) {
496  web_view_->OnWebContentsFocused(contents);
497}
498
499void PanelView::PanelCut() {
500  // Nothing to do since we do not have panel-specific system menu.
501  NOTREACHED();
502}
503
504void PanelView::PanelCopy() {
505  // Nothing to do since we do not have panel-specific system menu.
506  NOTREACHED();
507}
508
509void PanelView::PanelPaste() {
510  // Nothing to do since we do not have panel-specific system menu.
511  NOTREACHED();
512}
513
514void PanelView::DrawAttention(bool draw_attention) {
515  DCHECK((panel_->attention_mode() & Panel::USE_PANEL_ATTENTION) != 0);
516
517  if (is_drawing_attention_ == draw_attention)
518    return;
519  is_drawing_attention_ = draw_attention;
520  GetFrameView()->SchedulePaint();
521
522  if ((panel_->attention_mode() & Panel::USE_SYSTEM_ATTENTION) != 0) {
523#if defined(OS_WIN)
524    // The default implementation of Widget::FlashFrame only flashes 5 times.
525    // We need more than that.
526    FLASHWINFO fwi;
527    fwi.cbSize = sizeof(fwi);
528    fwi.hwnd = views::HWNDForWidget(window_);
529    if (draw_attention) {
530      fwi.dwFlags = FLASHW_ALL;
531      fwi.uCount = panel::kNumberOfTimesToFlashPanelForAttention;
532      fwi.dwTimeout = 0;
533    } else {
534      // TODO(jianli): calling FlashWindowEx with FLASHW_STOP flag for the
535      // panel window has the same problem as the stack window. However,
536      // we cannot take the similar fix since there is no background window
537      // to replace for the regular panel window. More investigation is needed.
538      fwi.dwFlags = FLASHW_STOP;
539    }
540    ::FlashWindowEx(&fwi);
541#else
542    window_->FlashFrame(draw_attention);
543#endif
544  }
545}
546
547bool PanelView::IsDrawingAttention() const {
548  return is_drawing_attention_;
549}
550
551void PanelView::HandlePanelKeyboardEvent(
552    const content::NativeWebKeyboardEvent& event) {
553  views::FocusManager* focus_manager = GetFocusManager();
554  if (focus_manager->shortcut_handling_suspended())
555    return;
556
557  ui::Accelerator accelerator(
558      static_cast<ui::KeyboardCode>(event.windowsKeyCode),
559      content::GetModifiersFromNativeWebKeyboardEvent(event));
560  if (event.type == WebKit::WebInputEvent::KeyUp)
561    accelerator.set_type(ui::ET_KEY_RELEASED);
562  focus_manager->ProcessAccelerator(accelerator);
563}
564
565void PanelView::FullScreenModeChanged(bool is_full_screen) {
566  if (is_full_screen) {
567    if (window_->IsVisible())
568      window_->Hide();
569  } else {
570    ShowPanelInactive();
571
572#if defined(OS_WIN)
573    // When hiding and showing again a top-most window that belongs to a
574    // background application (i.e. the application is not a foreground one),
575    // the window may loose top-most placement even though its WS_EX_TOPMOST
576    // bit is still set. Re-issuing SetWindowsPos() returns the window to its
577    // top-most placement.
578    if (always_on_top_)
579      window_->SetAlwaysOnTop(true);
580#endif
581  }
582}
583
584bool PanelView::IsPanelAlwaysOnTop() const {
585  return always_on_top_;
586}
587
588void PanelView::SetPanelAlwaysOnTop(bool on_top) {
589  if (always_on_top_ == on_top)
590    return;
591  always_on_top_ = on_top;
592
593  window_->SetAlwaysOnTop(on_top);
594  window_->non_client_view()->Layout();
595  window_->client_view()->Layout();
596}
597
598void PanelView::EnableResizeByMouse(bool enable) {
599  // Nothing to do since we use system resizing.
600}
601
602void PanelView::UpdatePanelMinimizeRestoreButtonVisibility() {
603  GetFrameView()->UpdateTitlebarMinimizeRestoreButtonVisibility();
604}
605
606void PanelView::SetWindowCornerStyle(panel::CornerStyle corner_style) {
607  GetFrameView()->SetWindowCornerStyle(corner_style);
608}
609
610void PanelView::PanelExpansionStateChanging(Panel::ExpansionState old_state,
611                                            Panel::ExpansionState new_state) {
612#if defined(OS_WIN)
613  // Live preview is only available since Windows 7.
614  if (base::win::GetVersion() < base::win::VERSION_WIN7)
615    return;
616
617  if (panel_->collection()->type() != PanelCollection::DOCKED)
618    return;
619
620  bool is_minimized = old_state != Panel::EXPANDED;
621  bool will_be_minimized = new_state != Panel::EXPANDED;
622  if (is_minimized == will_be_minimized)
623    return;
624
625  HWND native_window = views::HWNDForWidget(window_);
626
627  if (!thumbnailer_.get()) {
628    DCHECK(native_window);
629    thumbnailer_.reset(new TaskbarWindowThumbnailerWin(native_window, NULL));
630  }
631
632  // Cache the image at this point.
633  if (will_be_minimized) {
634    // If the panel is still active (we will deactivate the minimizd panel at
635    // later time), we need to paint it immediately as inactive so that we can
636    // take a snapshot of inactive panel.
637    if (focused_) {
638      force_to_paint_as_inactive_ = true;
639      ::RedrawWindow(native_window, NULL, NULL,
640                     RDW_NOCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW);
641    }
642
643    // Start the thumbnailer and capture the snapshot now.
644    thumbnailer_->Start();
645    thumbnailer_->CaptureSnapshot();
646  } else {
647    force_to_paint_as_inactive_ = false;
648    thumbnailer_->Stop();
649  }
650
651#endif
652}
653
654gfx::Size PanelView::WindowSizeFromContentSize(
655    const gfx::Size& content_size) const {
656  gfx::Size frame = GetFrameView()->NonClientAreaSize();
657  return gfx::Size(content_size.width() + frame.width(),
658                   content_size.height() + frame.height());
659}
660
661gfx::Size PanelView::ContentSizeFromWindowSize(
662    const gfx::Size& window_size) const {
663  gfx::Size frame = GetFrameView()->NonClientAreaSize();
664  return gfx::Size(window_size.width() - frame.width(),
665                   window_size.height() - frame.height());
666}
667
668int PanelView::TitleOnlyHeight() const {
669  return panel::kTitlebarHeight;
670}
671
672void PanelView::MinimizePanelBySystem() {
673  window_->Minimize();
674}
675
676bool PanelView::IsPanelMinimizedBySystem() const {
677  return window_->IsMinimized();
678}
679
680void PanelView::ShowShadow(bool show) {
681#if defined(OS_WIN)
682  // The overlapped window has the shadow while the popup window does not have
683  // the shadow.
684  int overlap_style = WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU;
685  int popup_style = WS_POPUP;
686  UpdateWindowAttribute(GWL_STYLE,
687                        show ? overlap_style : popup_style,
688                        show ? popup_style : overlap_style,
689                        true);
690#endif
691}
692
693void PanelView::AttachWebContents(content::WebContents* contents) {
694  web_view_->SetWebContents(contents);
695}
696
697void PanelView::DetachWebContents(content::WebContents* contents) {
698  web_view_->SetWebContents(NULL);
699}
700
701NativePanelTesting* PanelView::CreateNativePanelTesting() {
702  return new NativePanelTestingWin(this);
703}
704
705void PanelView::OnDisplayChanged() {
706  panel_->manager()->display_settings_provider()->OnDisplaySettingsChanged();
707}
708
709void PanelView::OnWorkAreaChanged() {
710  panel_->manager()->display_settings_provider()->OnDisplaySettingsChanged();
711}
712
713bool PanelView::WillProcessWorkAreaChange() const {
714  return true;
715}
716
717views::View* PanelView::GetContentsView() {
718  return this;
719}
720
721views::NonClientFrameView* PanelView::CreateNonClientFrameView(
722    views::Widget* widget) {
723  PanelFrameView* frame_view = new PanelFrameView(this);
724  frame_view->Init();
725  return frame_view;
726}
727
728bool PanelView::CanResize() const {
729  return true;
730}
731
732bool PanelView::CanMaximize() const {
733  return false;
734}
735
736string16 PanelView::GetWindowTitle() const {
737  return panel_->GetWindowTitle();
738}
739
740gfx::ImageSkia PanelView::GetWindowAppIcon() {
741  gfx::Image app_icon = panel_->app_icon();
742  if (app_icon.IsEmpty())
743    return GetWindowIcon();
744  else
745    return *app_icon.ToImageSkia();
746}
747
748gfx::ImageSkia PanelView::GetWindowIcon() {
749  gfx::Image icon = panel_->GetCurrentPageIcon();
750  return icon.IsEmpty() ? gfx::ImageSkia() : *icon.ToImageSkia();
751}
752
753void PanelView::WindowClosing() {
754  // When closing a panel via window.close, API or the close button,
755  // ClosePanel() is called first, destroying the native |window_|
756  // which results in this method being called. ClosePanel() sets
757  // |window_closed_| to NULL.
758  // If we still have a |window_closed_| here, the close was triggered by the
759  // OS, (e.g. clicking on taskbar menu), which destroys the native |window_|
760  // without invoking ClosePanel() beforehand.
761  if (!window_closed_) {
762    panel_->OnWindowClosing();
763    ClosePanel();
764    DCHECK(window_closed_);
765  }
766}
767
768void PanelView::DeleteDelegate() {
769  delete this;
770}
771
772void PanelView::OnWindowBeginUserBoundsChange() {
773  user_resizing_ = true;
774  panel_->OnPanelStartUserResizing();
775
776#if defined(OS_WIN)
777  StackedPanelCollection* stack = panel_->stack();
778  if (stack) {
779    // Listen to WM_SIZING message in order to find out whether the interior
780    // edge is being resized such that the specific maximum size could be
781    // passed to the system.
782    if (panel_->stack()->GetPanelBelow(panel_.get())) {
783      ui::HWNDSubclass::AddFilterToTarget(views::HWNDForWidget(window_), this);
784      user_resizing_interior_stacked_panel_edge_ = false;
785    }
786
787    // Keep track of the original full size of the resizing panel such that it
788    // can be restored to this size once it is shrunk to minimized state.
789    original_full_size_of_resizing_panel_ = panel_->full_size();
790
791    // Keep track of the original full size of the panel below the resizing
792    // panel such that it can be restored to this size once it is shrunk to
793    // minimized state.
794    Panel* below_panel = stack->GetPanelBelow(panel_.get());
795    if (below_panel && !below_panel->IsMinimized()) {
796      original_full_size_of_panel_below_resizing_panel_ =
797          below_panel->full_size();
798    }
799  }
800#endif
801}
802
803void PanelView::OnWindowEndUserBoundsChange() {
804  user_resizing_ = false;
805  panel_->OnPanelEndUserResizing();
806
807  // No need to proceed with post-resizing update when there is no size change.
808  gfx::Rect new_bounds = window_->GetWindowBoundsInScreen();
809  if (bounds_ == new_bounds)
810    return;
811  bounds_ = new_bounds;
812
813  panel_->IncreaseMaxSize(bounds_.size());
814  panel_->set_full_size(bounds_.size());
815
816#if defined(OS_WIN)
817  StackedPanelCollection* stack = panel_->stack();
818  if (stack) {
819    // No need to listen to WM_SIZING message any more.
820    ui::HWNDSubclass::RemoveFilterFromAllTargets(this);
821
822    // If the height of resizing panel shrinks close to the titlebar height,
823    // treate it as minimized. This could occur when the user is dragging
824    // 1) the top edge of the top panel downward to shrink it; or
825    // 2) the bottom edge of any panel upward to shrink it.
826    if (panel_->GetBounds().height() <
827            kStackedPanelHeightShrinkThresholdToBecomeMinimized) {
828      stack->MinimizePanel(panel_.get());
829      panel_->set_full_size(original_full_size_of_resizing_panel_);
830    }
831
832    // If the height of panel below the resizing panel shrinks close to the
833    // titlebar height, treat it as minimized. This could occur when the user
834    // is dragging the bottom edge of non-bottom panel downward to expand it
835    // and also shrink the panel below.
836    Panel* below_panel = stack->GetPanelBelow(panel_.get());
837    if (below_panel && !below_panel->IsMinimized() &&
838        below_panel->GetBounds().height() <
839            kStackedPanelHeightShrinkThresholdToBecomeMinimized) {
840      stack->MinimizePanel(below_panel);
841      below_panel->set_full_size(
842          original_full_size_of_panel_below_resizing_panel_);
843    }
844  }
845#endif
846
847  panel_->collection()->RefreshLayout();
848}
849
850views::Widget* PanelView::GetWidget() {
851  return window_;
852}
853
854const views::Widget* PanelView::GetWidget() const {
855  return window_;
856}
857
858void PanelView::UpdateLoadingAnimations(bool should_animate) {
859  GetFrameView()->UpdateThrobber();
860}
861
862void PanelView::UpdateWindowTitle() {
863  window_->UpdateWindowTitle();
864  GetFrameView()->UpdateTitle();
865}
866
867void PanelView::UpdateWindowIcon() {
868  window_->UpdateWindowIcon();
869  GetFrameView()->UpdateIcon();
870}
871
872void PanelView::Layout() {
873  // |web_view_| might not be created yet when the window is first created.
874  if (web_view_)
875    web_view_->SetBounds(0, 0, width(), height());
876  OnViewWasResized();
877}
878
879gfx::Size PanelView::GetMinimumSize() {
880  // If the panel is minimized, it can be rendered to very small size, like
881  // 4-pixel lines when it is docked. Otherwise, its size should not be less
882  // than its minimum size.
883  return panel_->IsMinimized() ? gfx::Size() :
884      gfx::Size(panel::kPanelMinWidth, panel::kPanelMinHeight);
885}
886
887gfx::Size PanelView::GetMaximumSize() {
888  // If the user is resizing a stacked panel by its bottom edge, make sure its
889  // height cannot grow more than what the panel below it could offer. This is
890  // because growing a stacked panel by y amount will shrink the panel below it
891  // by same amount and we do not want the panel below it being shrunk to be
892  // smaller than the titlebar.
893#if defined(OS_WIN)
894  if (panel_->stack() && user_resizing_interior_stacked_panel_edge_) {
895    Panel* below_panel = panel_->stack()->GetPanelBelow(panel_.get());
896    if (below_panel && !below_panel->IsMinimized()) {
897      return gfx::Size(0, below_panel->GetBounds().bottom() -
898          panel_->GetBounds().y() - panel::kTitlebarHeight);
899    }
900  }
901#endif
902  return gfx::Size();
903}
904
905bool PanelView::AcceleratorPressed(const ui::Accelerator& accelerator) {
906  if (mouse_pressed_ && accelerator.key_code() == ui::VKEY_ESCAPE) {
907    OnTitlebarMouseCaptureLost();
908    return true;
909  }
910
911  // No other accelerator is allowed when the drag begins.
912  if (mouse_dragging_state_ == DRAGGING_STARTED)
913    return true;
914
915  const std::map<ui::Accelerator, int>& accelerator_table =
916      GetAcceleratorTable();
917  std::map<ui::Accelerator, int>::const_iterator iter =
918      accelerator_table.find(accelerator);
919  DCHECK(iter != accelerator_table.end());
920  return panel_->ExecuteCommandIfEnabled(iter->second);
921}
922
923void PanelView::OnWidgetDestroying(views::Widget* widget) {
924  window_ = NULL;
925}
926
927void PanelView::OnWidgetActivationChanged(views::Widget* widget, bool active) {
928#if defined(OS_WIN)
929  // WM_NCACTIVATED could be sent when an active window is being destroyed on
930  // Windows. We need to guard against this.
931  if (window_closed_)
932    return;
933
934  // The panel window is in focus (actually accepting keystrokes) if it is
935  // active and belongs to a foreground application.
936  bool focused = active &&
937      views::HWNDForWidget(widget) == ::GetForegroundWindow();
938#else
939  NOTIMPLEMENTED();
940  bool focused = active;
941#endif
942
943  if (focused_ == focused)
944    return;
945  focused_ = focused;
946
947  // Expand the panel if the minimized panel is activated by means other than
948  // clicking on its titlebar. This is the workaround to support restoring the
949  // minimized panel by other means, like alt-tabbing, win-tabbing, or clicking
950  // the taskbar icon. Note that this workaround does not work for one edge
951  // case: the mouse happens to be at the minimized panel when the user tries to
952  // bring up the panel with the above alternatives.
953  // When the user clicks on the minimized panel, the panel expansion will be
954  // done when we process the mouse button pressed message.
955  if (focused_ && panel_->IsMinimized() &&
956      panel_->collection()->type() == PanelCollection::DOCKED &&
957      gfx::Screen::GetScreenFor(widget->GetNativeWindow())->
958          GetWindowAtCursorScreenPoint() != widget->GetNativeWindow()) {
959    panel_->Restore();
960  }
961
962  panel()->OnActiveStateChanged(focused);
963}
964
965void PanelView::OnWidgetBoundsChanged(views::Widget* widget,
966                                      const gfx::Rect& new_bounds) {
967  if (user_resizing_)
968    panel()->collection()->OnPanelResizedByMouse(panel(), new_bounds);
969}
970
971void PanelView::OnNativeFocusChange(gfx::NativeView focused_before,
972                                    gfx::NativeView focused_now) {
973  if (focused_now != window_->GetNativeView())
974    return;
975
976  // Give web contents view a chance to set focus to the appropriate element.
977  content::WebContents* web_contents = panel_->GetWebContents();
978  if (web_contents)
979    web_contents->GetView()->RestoreFocus();
980}
981
982bool PanelView::OnTitlebarMousePressed(const gfx::Point& mouse_location) {
983  mouse_pressed_ = true;
984  mouse_dragging_state_ = NO_DRAGGING;
985  last_mouse_location_ = mouse_location;
986  return true;
987}
988
989bool PanelView::OnTitlebarMouseDragged(const gfx::Point& mouse_location) {
990  if (!mouse_pressed_)
991    return false;
992
993  if (mouse_dragging_state_ == NO_DRAGGING &&
994      ExceededDragThreshold(mouse_location - last_mouse_location_)) {
995    // When a drag begins, we do not want to the client area to still receive
996    // the focus. We do not need to do this for the unfocused minimized panel.
997    if (!panel_->IsMinimized()) {
998      old_focused_view_ = GetFocusManager()->GetFocusedView();
999      GetFocusManager()->SetFocusedView(GetFrameView());
1000    }
1001
1002    panel_->manager()->StartDragging(panel_.get(), last_mouse_location_);
1003    mouse_dragging_state_ = DRAGGING_STARTED;
1004  }
1005  if (mouse_dragging_state_ == DRAGGING_STARTED) {
1006    panel_->manager()->Drag(mouse_location);
1007
1008    // Once in drag, update |last_mouse_location_| on each drag fragment, since
1009    // we already dragged the panel up to the current mouse location.
1010    last_mouse_location_ = mouse_location;
1011  }
1012  return true;
1013}
1014
1015bool PanelView::OnTitlebarMouseReleased(panel::ClickModifier modifier) {
1016  if (mouse_dragging_state_ != NO_DRAGGING) {
1017    // Ensure dragging a minimized panel does not leave it activated.
1018    // Windows activates a panel on mouse-down, regardless of our attempts
1019    // to prevent activation of a minimized panel. Now that we know mouse-down
1020    // resulted in a mouse-drag, we need to ensure the minimized panel is
1021    // deactivated.
1022    if (panel_->IsMinimized() && focused_)
1023      panel_->Deactivate();
1024
1025    if (mouse_dragging_state_ == DRAGGING_STARTED) {
1026      // When a drag ends, restore the focus.
1027      if (old_focused_view_) {
1028        GetFocusManager()->SetFocusedView(old_focused_view_);
1029        old_focused_view_ = NULL;
1030      }
1031      return EndDragging(false);
1032    }
1033
1034    // The panel drag was cancelled before the mouse is released. Do not
1035    // treat this as a click.
1036    return true;
1037  }
1038
1039  panel_->OnTitlebarClicked(modifier);
1040  return true;
1041}
1042
1043bool PanelView::OnTitlebarMouseCaptureLost() {
1044  if (mouse_dragging_state_ == DRAGGING_STARTED)
1045    return EndDragging(true);
1046  return true;
1047}
1048
1049bool PanelView::EndDragging(bool cancelled) {
1050  // Only handle clicks that started in our window.
1051  if (!mouse_pressed_)
1052    return false;
1053  mouse_pressed_ = false;
1054
1055  mouse_dragging_state_ = DRAGGING_ENDED;
1056  panel_->manager()->EndDragging(cancelled);
1057  return true;
1058}
1059
1060PanelFrameView* PanelView::GetFrameView() const {
1061  return static_cast<PanelFrameView*>(window_->non_client_view()->frame_view());
1062}
1063
1064bool PanelView::IsAnimatingBounds() const {
1065  if (bounds_animator_.get() && bounds_animator_->is_animating())
1066    return true;
1067  StackedPanelCollection* stack = panel_->stack();
1068  if (!stack)
1069    return false;
1070  return stack->IsAnimatingPanelBounds(panel_.get());
1071}
1072
1073bool PanelView::IsWithinResizingArea(const gfx::Point& mouse_location) const {
1074  gfx::Rect bounds = window_->GetWindowBoundsInScreen();
1075  DCHECK(bounds.Contains(mouse_location));
1076  return mouse_location.x() < bounds.x() + kResizeInsideBoundsSize ||
1077         mouse_location.x() >= bounds.right() - kResizeInsideBoundsSize ||
1078         mouse_location.y() < bounds.y() + kResizeInsideBoundsSize ||
1079         mouse_location.y() >= bounds.bottom() - kResizeInsideBoundsSize;
1080}
1081
1082#if defined(OS_WIN)
1083void PanelView::UpdateWindowAttribute(int attribute_index,
1084                                      int attribute_value_to_set,
1085                                      int attribute_value_to_reset,
1086                                      bool update_frame) {
1087  HWND native_window = views::HWNDForWidget(window_);
1088  int value = ::GetWindowLong(native_window, attribute_index);
1089  int expected_value = value;
1090  if (attribute_value_to_set)
1091    expected_value |=  attribute_value_to_set;
1092  if (attribute_value_to_reset)
1093    expected_value &=  ~attribute_value_to_reset;
1094  if (value != expected_value)
1095    ::SetWindowLong(native_window, attribute_index, expected_value);
1096
1097  // Per MSDN, if any of the frame styles is changed, SetWindowPos with the
1098  // SWP_FRAMECHANGED flag must be called in order for the cached window data
1099  // to be updated properly.
1100  // http://msdn.microsoft.com/en-us/library/windows/desktop/ms633591(v=vs.85).aspx
1101  if (update_frame) {
1102    ::SetWindowPos(native_window, NULL, 0, 0, 0, 0,
1103                   SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE |
1104                       SWP_NOZORDER | SWP_NOACTIVATE);
1105  }
1106}
1107#endif
1108
1109void PanelView::OnViewWasResized() {
1110#if defined(OS_WIN) && !defined(USE_AURA)
1111  content::WebContents* web_contents = panel_->GetWebContents();
1112  if (!web_view_ || !web_contents)
1113    return;
1114
1115  // When the panel is frameless or has thin frame, the mouse resizing should
1116  // also be triggered from the part of client area that is close to the window
1117  // frame.
1118  int width = web_view_->size().width();
1119  int height = web_view_->size().height();
1120  // Compute the thickness of the client area that needs to be counted towards
1121  // mouse resizing.
1122  int thickness_for_mouse_resizing =
1123      kResizeInsideBoundsSize - GetFrameView()->BorderThickness();
1124  DCHECK(thickness_for_mouse_resizing > 0);
1125  SkRegion* region = new SkRegion;
1126  region->op(0, 0, thickness_for_mouse_resizing, height, SkRegion::kUnion_Op);
1127  region->op(width - thickness_for_mouse_resizing, 0, width, height,
1128      SkRegion::kUnion_Op);
1129  region->op(0, height - thickness_for_mouse_resizing, width, height,
1130      SkRegion::kUnion_Op);
1131  web_contents->GetRenderViewHost()->GetView()->SetClickthroughRegion(region);
1132#endif
1133}
1134