panel_view.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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/views/panels/panel_view.h"
6
7#include <map>
8#include "base/logging.h"
9#include "base/utf_string_conversions.h"
10#include "chrome/app/chrome_command_ids.h"
11#include "chrome/browser/profiles/profile.h"
12#include "chrome/browser/ui/panels/panel.h"
13#include "chrome/browser/ui/panels/panel_bounds_animation.h"
14#include "chrome/browser/ui/panels/panel_manager.h"
15#include "chrome/browser/ui/views/panels/panel_frame_view.h"
16#include "content/public/browser/render_view_host.h"
17#include "content/public/browser/render_widget_host_view.h"
18#include "content/public/browser/web_contents_view.h"
19#include "ui/gfx/image/image.h"
20#include "ui/gfx/path.h"
21#include "ui/gfx/screen.h"
22#include "ui/views/controls/button/image_button.h"
23#include "ui/views/controls/webview/webview.h"
24#include "ui/views/widget/widget.h"
25
26#if defined(OS_WIN) && !defined(USE_ASH) && !defined(USE_AURA)
27#include "base/win/windows_version.h"
28#include "chrome/browser/shell_integration.h"
29#include "chrome/browser/ui/panels/taskbar_window_thumbnailer_win.h"
30#include "ui/base/win/shell.h"
31#include "ui/gfx/icon_util.h"
32#endif
33
34namespace {
35
36// Supported accelerators.
37// Note: We can't use the acclerator table defined in chrome/browser/ui/views
38// due to checkdeps violation.
39struct AcceleratorMapping {
40  ui::KeyboardCode keycode;
41  int modifiers;
42  int command_id;
43};
44const AcceleratorMapping kPanelAcceleratorMap[] = {
45  { ui::VKEY_W, ui::EF_CONTROL_DOWN, IDC_CLOSE_WINDOW },
46  { ui::VKEY_W, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, IDC_CLOSE_WINDOW },
47  { ui::VKEY_F4, ui::EF_ALT_DOWN, IDC_CLOSE_WINDOW },
48  { ui::VKEY_R, ui::EF_CONTROL_DOWN, IDC_RELOAD },
49  { ui::VKEY_F5, ui::EF_NONE, IDC_RELOAD },
50  { ui::VKEY_R, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN,
51      IDC_RELOAD_IGNORING_CACHE },
52  { ui::VKEY_F5, ui::EF_CONTROL_DOWN, IDC_RELOAD_IGNORING_CACHE },
53  { ui::VKEY_F5, ui::EF_SHIFT_DOWN, IDC_RELOAD_IGNORING_CACHE },
54  { ui::VKEY_ESCAPE, ui::EF_NONE, IDC_STOP },
55  { ui::VKEY_OEM_MINUS, ui::EF_CONTROL_DOWN, IDC_ZOOM_MINUS },
56  { ui::VKEY_SUBTRACT, ui::EF_CONTROL_DOWN, IDC_ZOOM_MINUS },
57  { ui::VKEY_0, ui::EF_CONTROL_DOWN, IDC_ZOOM_NORMAL },
58  { ui::VKEY_NUMPAD0, ui::EF_CONTROL_DOWN, IDC_ZOOM_NORMAL },
59  { ui::VKEY_OEM_PLUS, ui::EF_CONTROL_DOWN, IDC_ZOOM_PLUS },
60  { ui::VKEY_ADD, ui::EF_CONTROL_DOWN, IDC_ZOOM_PLUS },
61  { ui::VKEY_I, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, IDC_DEV_TOOLS },
62  { ui::VKEY_J, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN,
63    IDC_DEV_TOOLS_CONSOLE },
64};
65
66const std::map<ui::Accelerator, int>& GetAcceleratorTable() {
67  static std::map<ui::Accelerator, int>* accelerators = NULL;
68  if (!accelerators) {
69    accelerators = new std::map<ui::Accelerator, int>();
70    for (size_t i = 0; i < arraysize(kPanelAcceleratorMap); ++i) {
71      ui::Accelerator accelerator(kPanelAcceleratorMap[i].keycode,
72                                  kPanelAcceleratorMap[i].modifiers);
73      (*accelerators)[accelerator] = kPanelAcceleratorMap[i].command_id;
74    }
75  }
76  return *accelerators;
77}
78
79// NativePanelTesting implementation.
80class NativePanelTestingWin : public NativePanelTesting {
81 public:
82  explicit NativePanelTestingWin(PanelView* panel_view);
83
84 private:
85  virtual void PressLeftMouseButtonTitlebar(
86      const gfx::Point& mouse_location, panel::ClickModifier modifier) OVERRIDE;
87  virtual void ReleaseMouseButtonTitlebar(
88      panel::ClickModifier modifier) OVERRIDE;
89  virtual void DragTitlebar(const gfx::Point& mouse_location) OVERRIDE;
90  virtual void CancelDragTitlebar() OVERRIDE;
91  virtual void FinishDragTitlebar() OVERRIDE;
92  virtual bool VerifyDrawingAttention() const OVERRIDE;
93  virtual bool VerifyActiveState(bool is_active) OVERRIDE;
94  virtual bool VerifyAppIcon() const OVERRIDE;
95  virtual bool IsWindowSizeKnown() const OVERRIDE;
96  virtual bool IsAnimatingBounds() const OVERRIDE;
97  virtual bool IsButtonVisible(
98      panel::TitlebarButtonType button_type) const OVERRIDE;
99
100  PanelView* panel_view_;
101};
102
103NativePanelTestingWin::NativePanelTestingWin(PanelView* panel_view)
104    : panel_view_(panel_view) {
105}
106
107void NativePanelTestingWin::PressLeftMouseButtonTitlebar(
108    const gfx::Point& mouse_location, panel::ClickModifier modifier) {
109  panel_view_->OnTitlebarMousePressed(mouse_location);
110}
111
112void NativePanelTestingWin::ReleaseMouseButtonTitlebar(
113    panel::ClickModifier modifier) {
114  panel_view_->OnTitlebarMouseReleased(modifier);
115}
116
117void NativePanelTestingWin::DragTitlebar(const gfx::Point& mouse_location) {
118  panel_view_->OnTitlebarMouseDragged(mouse_location);
119}
120
121void NativePanelTestingWin::CancelDragTitlebar() {
122  panel_view_->OnTitlebarMouseCaptureLost();
123}
124
125void NativePanelTestingWin::FinishDragTitlebar() {
126  panel_view_->OnTitlebarMouseReleased(panel::NO_MODIFIER);
127}
128
129bool NativePanelTestingWin::VerifyDrawingAttention() const {
130  return panel_view_->GetFrameView()->paint_state() ==
131         PanelFrameView::PAINT_FOR_ATTENTION;
132}
133
134bool NativePanelTestingWin::VerifyActiveState(bool is_active) {
135  return panel_view_->GetFrameView()->paint_state() ==
136         (is_active ? PanelFrameView::PAINT_AS_ACTIVE
137                    : PanelFrameView::PAINT_AS_INACTIVE);
138}
139
140bool NativePanelTestingWin::VerifyAppIcon() const {
141#if defined(OS_WIN) && !defined(USE_AURA)
142  // We only care about Windows 7 and later.
143  if (base::win::GetVersion() < base::win::VERSION_WIN7)
144    return true;
145
146  HWND native_window = panel_view_->GetNativePanelHandle();
147  HICON app_icon = reinterpret_cast<HICON>(
148      ::SendMessage(native_window, WM_GETICON, ICON_BIG, 0L));
149  if (!app_icon)
150    return false;
151  scoped_ptr<SkBitmap> bitmap(IconUtil::CreateSkBitmapFromHICON(app_icon));
152  return bitmap.get() &&
153         bitmap->width() == panel::kPanelAppIconSize &&
154         bitmap->height() == panel::kPanelAppIconSize;
155#else
156  return true;
157#endif
158}
159
160bool NativePanelTestingWin::IsWindowSizeKnown() const {
161  return true;
162}
163
164bool NativePanelTestingWin::IsAnimatingBounds() const {
165  return panel_view_->IsAnimatingBounds();
166}
167
168bool NativePanelTestingWin::IsButtonVisible(
169    panel::TitlebarButtonType button_type) const {
170  PanelFrameView* frame_view = panel_view_->GetFrameView();
171
172  switch (button_type) {
173    case panel::CLOSE_BUTTON:
174      return frame_view->close_button()->visible();
175    case panel::MINIMIZE_BUTTON:
176      return frame_view->minimize_button()->visible();
177    case panel::RESTORE_BUTTON:
178      return frame_view->restore_button()->visible();
179    default:
180      NOTREACHED();
181  }
182  return false;
183}
184
185}  // namespace
186
187// static
188NativePanel* Panel::CreateNativePanel(Panel* panel, const gfx::Rect& bounds) {
189  return new PanelView(panel, bounds);
190}
191
192// The panel window has to be created as always-on-top. We cannot create it
193// as non-always-on-top and then change it to always-on-top because Windows
194// system might deny making a window always-on-top if the application is not
195// a foreground application. In addition, we do not know if the panel should
196// be created as always-on-top at its creation time. To solve this issue,
197// always_on_top_ is default to true because we can always change from
198// always-on-top to not always-on-top but not the other way around.
199PanelView::PanelView(Panel* panel, const gfx::Rect& bounds)
200    : panel_(panel),
201      bounds_(bounds),
202      window_(NULL),
203      web_view_(NULL),
204      always_on_top_(true),
205      focused_(false),
206      mouse_pressed_(false),
207      mouse_dragging_state_(NO_DRAGGING),
208      is_drawing_attention_(false),
209      force_to_paint_as_inactive_(false),
210      old_focused_view_(NULL) {
211  window_ = new views::Widget;
212  views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
213  params.delegate = this;
214  params.remove_standard_frame = true;
215  params.keep_on_top = true;
216  params.bounds = bounds;
217  window_->Init(params);
218  window_->set_frame_type(views::Widget::FRAME_TYPE_FORCE_CUSTOM);
219  window_->set_focus_on_creation(false);
220  window_->AddObserver(this);
221
222  web_view_ = new views::WebView(NULL);
223  AddChildView(web_view_);
224
225  OnViewWasResized();
226
227  // Register accelarators supported by panels.
228  views::FocusManager* focus_manager = GetFocusManager();
229  const std::map<ui::Accelerator, int>& accelerator_table =
230      GetAcceleratorTable();
231  for (std::map<ui::Accelerator, int>::const_iterator iter =
232           accelerator_table.begin();
233       iter != accelerator_table.end(); ++iter) {
234    focus_manager->RegisterAccelerator(
235        iter->first, ui::AcceleratorManager::kNormalPriority, this);
236  }
237
238#if defined(OS_WIN) && !defined(USE_ASH) && !defined(USE_AURA)
239  ui::win::SetAppIdForWindow(
240      ShellIntegration::GetAppModelIdForProfile(UTF8ToWide(panel->app_name()),
241                                                panel->profile()->GetPath()),
242      window_->GetNativeWindow());
243#endif
244}
245
246PanelView::~PanelView() {
247}
248
249void PanelView::ShowPanel() {
250  ShowPanelInactive();
251  ActivatePanel();
252
253  // Give web contents view a chance to set focus to the appropriate element
254  // when it is created for the first time.
255  content::WebContents* web_contents = panel_->GetWebContents();
256  if (web_contents)
257    web_contents->GetView()->RestoreFocus();
258}
259
260void PanelView::ShowPanelInactive() {
261  if (window_->IsVisible())
262    return;
263  window_->ShowInactive();
264  // No animation is used for initial creation of a panel on Win.
265  // Signal immediately that pending actions can be performed.
266  panel_->manager()->OnPanelAnimationEnded(panel_.get());
267}
268
269gfx::Rect PanelView::GetPanelBounds() const {
270  return bounds_;
271}
272
273void PanelView::SetPanelBounds(const gfx::Rect& bounds) {
274  SetBoundsInternal(bounds, true);
275}
276
277void PanelView::SetPanelBoundsInstantly(const gfx::Rect& bounds) {
278  SetBoundsInternal(bounds, false);
279}
280
281void PanelView::SetBoundsInternal(const gfx::Rect& new_bounds, bool animate) {
282  if (bounds_ == new_bounds)
283    return;
284
285  bounds_ = new_bounds;
286
287  if (!animate) {
288    // If no animation is in progress, apply bounds change instantly. Otherwise,
289    // continue the animation with new target bounds.
290    if (!IsAnimatingBounds())
291      SetWidgetBounds(bounds_);
292    return;
293  }
294
295  animation_start_bounds_ = window_->GetWindowBoundsInScreen();
296
297  bounds_animator_.reset(new PanelBoundsAnimation(
298      this, panel_.get(), animation_start_bounds_, new_bounds));
299  bounds_animator_->Start();
300}
301
302void PanelView::AnimationEnded(const ui::Animation* animation) {
303  panel_->manager()->OnPanelAnimationEnded(panel_.get());
304}
305
306void PanelView::AnimationProgressed(const ui::Animation* animation) {
307  gfx::Rect new_bounds = bounds_animator_->CurrentValueBetween(
308      animation_start_bounds_, bounds_);
309  SetWidgetBounds(new_bounds);
310}
311
312void PanelView::SetWidgetBounds(const gfx::Rect& new_bounds) {
313#if defined(OS_WIN) && !defined(USE_ASH) && !defined(USE_AURA)
314  // An overlapped window is a top-level window that has a titlebar, border,
315  // and client area. The Windows system will automatically put the shadow
316  // around the whole window. Also the system will enforce the minimum height
317  // (38 pixels based on observation) for the overlapped window such that it
318  // will always has the space for the titlebar.
319  //
320  // On contrast, a popup window is a bare minimum window without border and
321  // titlebar by default. It is often used for the popup menu and the window
322  // with short life. The Windows system does not add the shadow around the
323  // whole window though CS_DROPSHADOW class style could be passed to add the
324  // drop shadow which is only around the right and bottom edges.
325  //
326  // The height of the title-only or minimized panel is smaller than the minimum
327  // overlapped window height. If the panel still uses the overlapped window
328  // style, Windows system will automatically increase the window height. To
329  // work around this limitation, we temporarily change the window style to
330  // popup when the height to set is smaller than the minimum overlapped window
331  // height and then restore the window style to overlapped when the height
332  // grows.
333  static const int kMinimumOverlappedWindowHeight = 38;
334  gfx::Rect old_bounds = GetWidget()->GetRestoredBounds();
335  if (old_bounds.height() > kMinimumOverlappedWindowHeight &&
336      new_bounds.height() <= kMinimumOverlappedWindowHeight) {
337    // When the panel height shrinks below the minimum overlapped window height,
338    // change the window style to popup such that we can show the title-only
339    // and minimized panel without additional height being added by the system.
340    UpdateWindowAttribute(GWL_STYLE,
341                          WS_POPUP,
342                          WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU,
343                          true);
344  } else if (old_bounds.height() <= kMinimumOverlappedWindowHeight &&
345             new_bounds.height() > kMinimumOverlappedWindowHeight) {
346    // Change the window style back to overlappped when the panel height grow
347    // taller than the minimum overlapped window height.
348    UpdateWindowAttribute(GWL_STYLE,
349                          WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU,
350                          WS_POPUP,
351                          true);
352  }
353#endif
354
355  GetWidget()->SetBounds(new_bounds);
356}
357
358void PanelView::ClosePanel() {
359  // We're already closing. Do nothing.
360  if (!window_)
361    return;
362
363  if (!panel_->ShouldCloseWindow())
364    return;
365
366  // Cancel any currently running animation since we're closing down.
367  if (bounds_animator_.get())
368    bounds_animator_.reset();
369
370  if (panel_->GetWebContents()) {
371    // Still have web contents. Allow renderer to shut down.
372    // When web contents are destroyed, we will be called back again.
373    panel_->OnWindowClosing();
374    return;
375  }
376
377  panel_->OnNativePanelClosed();
378  window_->Close();
379  window_ = NULL;
380}
381
382void PanelView::ActivatePanel() {
383  window_->Activate();
384}
385
386void PanelView::DeactivatePanel() {
387  if (!focused_)
388    return;
389
390#if defined(OS_WIN) && !defined(AURA) && !defined(USE_ASH)
391  // Need custom behavior for always-on-top panels to avoid
392  // the OS activating a minimized panel when this one is
393  // deactivated.
394  if (always_on_top_) {
395    ::SetForegroundWindow(::GetDesktopWindow());
396    return;
397  }
398#endif
399
400  window_->Deactivate();
401}
402
403bool PanelView::IsPanelActive() const {
404  return focused_;
405}
406
407void PanelView::PreventActivationByOS(bool prevent_activation) {
408#if defined(OS_WIN) && !defined(USE_ASH) && !defined(USE_AURA)
409  // Set the flags "NoActivate" and "AppWindow" to make sure
410  // the minimized panels do not get activated by the OS, but
411  // do appear in the taskbar and Alt-Tab menu.
412  int value_to_change = WS_EX_NOACTIVATE | WS_EX_APPWINDOW;
413  if (prevent_activation)
414    UpdateWindowAttribute(GWL_EXSTYLE, value_to_change, 0, false);
415  else
416    UpdateWindowAttribute(GWL_EXSTYLE, 0, value_to_change, false);
417#endif
418}
419
420gfx::NativeWindow PanelView::GetNativePanelHandle() {
421  return window_->GetNativeWindow();
422}
423
424void PanelView::UpdatePanelTitleBar() {
425  UpdateWindowTitle();
426  UpdateWindowIcon();
427}
428
429void PanelView::UpdatePanelLoadingAnimations(bool should_animate) {
430  GetFrameView()->UpdateThrobber();
431}
432
433void PanelView::NotifyPanelOnUserChangedTheme() {
434  GetFrameView()->SchedulePaint();
435}
436
437void PanelView::PanelWebContentsFocused(content::WebContents* contents) {
438  web_view_->OnWebContentsFocused(contents);
439}
440
441void PanelView::PanelCut() {
442  // Nothing to do since we do not have panel-specific system menu.
443  NOTREACHED();
444}
445
446void PanelView::PanelCopy() {
447  // Nothing to do since we do not have panel-specific system menu.
448  NOTREACHED();
449}
450
451void PanelView::PanelPaste() {
452  // Nothing to do since we do not have panel-specific system menu.
453  NOTREACHED();
454}
455
456void PanelView::DrawAttention(bool draw_attention) {
457  DCHECK((panel_->attention_mode() & Panel::USE_PANEL_ATTENTION) != 0);
458
459  if (is_drawing_attention_ == draw_attention)
460    return;
461  is_drawing_attention_ = draw_attention;
462  GetFrameView()->SchedulePaint();
463
464  if ((panel_->attention_mode() & Panel::USE_SYSTEM_ATTENTION) != 0)
465    window_->FlashFrame(draw_attention);
466}
467
468bool PanelView::IsDrawingAttention() const {
469  return is_drawing_attention_;
470}
471
472void PanelView::HandlePanelKeyboardEvent(
473    const content::NativeWebKeyboardEvent& event) {
474  views::FocusManager* focus_manager = GetFocusManager();
475  if (focus_manager->shortcut_handling_suspended())
476    return;
477
478  ui::Accelerator accelerator(
479      static_cast<ui::KeyboardCode>(event.windowsKeyCode),
480      content::GetModifiersFromNativeWebKeyboardEvent(event));
481  if (event.type == WebKit::WebInputEvent::KeyUp)
482    accelerator.set_type(ui::ET_KEY_RELEASED);
483  focus_manager->ProcessAccelerator(accelerator);
484}
485
486void PanelView::FullScreenModeChanged(bool is_full_screen) {
487  if (is_full_screen) {
488    if (window_->IsVisible())
489      window_->Hide();
490  } else {
491    ShowPanelInactive();
492  }
493}
494
495bool PanelView::IsPanelAlwaysOnTop() const {
496  return always_on_top_;
497}
498
499void PanelView::SetPanelAlwaysOnTop(bool on_top) {
500  if (always_on_top_ == on_top)
501    return;
502  always_on_top_ = on_top;
503
504  window_->SetAlwaysOnTop(on_top);
505  window_->non_client_view()->Layout();
506  window_->client_view()->Layout();
507}
508
509void PanelView::EnableResizeByMouse(bool enable) {
510  // Nothing to do since we use system resizing.
511}
512
513void PanelView::UpdatePanelMinimizeRestoreButtonVisibility() {
514  GetFrameView()->UpdateTitlebarMinimizeRestoreButtonVisibility();
515}
516
517void PanelView::PanelExpansionStateChanging(Panel::ExpansionState old_state,
518                                            Panel::ExpansionState new_state) {
519#if defined(OS_WIN) && !defined(USE_ASH) && !defined(USE_AURA)
520  // Live preview is only available since Windows 7.
521  if (base::win::GetVersion() < base::win::VERSION_WIN7)
522    return;
523
524  bool is_minimized = old_state != Panel::EXPANDED;
525  bool will_be_minimized = new_state != Panel::EXPANDED;
526  if (is_minimized == will_be_minimized)
527    return;
528
529  HWND native_window = window_->GetNativeWindow();
530
531  if (!thumbnailer_.get()) {
532    DCHECK(native_window);
533    thumbnailer_.reset(new TaskbarWindowThumbnailerWin(native_window));
534    ui::HWNDSubclass::AddFilterToTarget(native_window, thumbnailer_.get());
535  }
536
537  // Cache the image at this point.
538  if (will_be_minimized) {
539    // If the panel is still active (we will deactivate the minimizd panel at
540    // later time), we need to paint it immediately as inactive so that we can
541    // take a snapshot of inactive panel.
542    if (focused_) {
543      force_to_paint_as_inactive_ = true;
544      ::RedrawWindow(native_window, NULL, NULL,
545                     RDW_NOCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW);
546    }
547
548    thumbnailer_->Start();
549  } else {
550    force_to_paint_as_inactive_ = false;
551    thumbnailer_->Stop();
552  }
553
554#endif
555}
556
557gfx::Size PanelView::WindowSizeFromContentSize(
558    const gfx::Size& content_size) const {
559  gfx::Size frame = GetFrameView()->NonClientAreaSize();
560  return gfx::Size(content_size.width() + frame.width(),
561                   content_size.height() + frame.height());
562}
563
564gfx::Size PanelView::ContentSizeFromWindowSize(
565    const gfx::Size& window_size) const {
566  gfx::Size frame = GetFrameView()->NonClientAreaSize();
567  return gfx::Size(window_size.width() - frame.width(),
568                   window_size.height() - frame.height());
569}
570
571int PanelView::TitleOnlyHeight() const {
572  return panel::kTitlebarHeight;
573}
574
575void PanelView::AttachWebContents(content::WebContents* contents) {
576  web_view_->SetWebContents(contents);
577}
578
579void PanelView::DetachWebContents(content::WebContents* contents) {
580  web_view_->SetWebContents(NULL);
581}
582
583NativePanelTesting* PanelView::CreateNativePanelTesting() {
584  return new NativePanelTestingWin(this);
585}
586
587void PanelView::OnDisplayChanged() {
588  panel_->manager()->display_settings_provider()->OnDisplaySettingsChanged();
589}
590
591void PanelView::OnWorkAreaChanged() {
592  panel_->manager()->display_settings_provider()->OnDisplaySettingsChanged();
593}
594
595bool PanelView::WillProcessWorkAreaChange() const {
596  return true;
597}
598
599views::View* PanelView::GetContentsView() {
600  return this;
601}
602
603views::NonClientFrameView* PanelView::CreateNonClientFrameView(
604    views::Widget* widget) {
605  PanelFrameView* frame_view = new PanelFrameView(this);
606  frame_view->Init();
607  return frame_view;
608}
609
610bool PanelView::CanResize() const {
611  return true;
612}
613
614bool PanelView::CanMaximize() const {
615  return false;
616}
617
618string16 PanelView::GetWindowTitle() const {
619  return panel_->GetWindowTitle();
620}
621
622gfx::ImageSkia PanelView::GetWindowAppIcon() {
623  gfx::Image app_icon = panel_->app_icon();
624  if (app_icon.IsEmpty())
625    return GetWindowIcon();
626  else
627    return *app_icon.ToImageSkia();
628}
629
630gfx::ImageSkia PanelView::GetWindowIcon() {
631  gfx::Image icon = panel_->GetCurrentPageIcon();
632  return icon.IsEmpty() ? gfx::ImageSkia() : *icon.ToImageSkia();
633}
634
635void PanelView::WindowClosing() {
636  // When closing a panel via window.close, API or the close button,
637  // ClosePanel() is called first, destroying the native |window_|
638  // which results in this method being called. ClosePanel() sets
639  // |window_| to NULL.
640  // If we still have a |window_| here, the close was triggered by the OS,
641  // (e.g. clicking on taskbar menu), which destroys the native |window_|
642  // without invoking ClosePanel() beforehand.
643  if (window_) {
644    panel_->OnWindowClosing();
645    ClosePanel();
646    DCHECK(!window_);
647  }
648}
649
650void PanelView::DeleteDelegate() {
651  delete this;
652}
653
654void PanelView::OnWindowBeginUserBoundsChange() {
655  panel_->OnPanelStartUserResizing();
656}
657
658void PanelView::OnWindowEndUserBoundsChange() {
659  panel_->OnPanelEndUserResizing();
660
661  // No need to proceed with post-resizing update when there is no size change.
662  gfx::Rect new_bounds = window_->GetWindowBoundsInScreen();
663  if (bounds_ == new_bounds)
664    return;
665  bounds_ = new_bounds;
666
667  panel_->IncreaseMaxSize(bounds_.size());
668  panel_->set_full_size(bounds_.size());
669
670  panel_->panel_strip()->RefreshLayout();
671}
672
673views::Widget* PanelView::GetWidget() {
674  return window_;
675}
676
677const views::Widget* PanelView::GetWidget() const {
678  return window_;
679}
680
681void PanelView::UpdateLoadingAnimations(bool should_animate) {
682  GetFrameView()->UpdateThrobber();
683}
684
685void PanelView::UpdateWindowTitle() {
686  window_->UpdateWindowTitle();
687  GetFrameView()->UpdateTitle();
688}
689
690void PanelView::UpdateWindowIcon() {
691  window_->UpdateWindowIcon();
692  GetFrameView()->UpdateIcon();
693}
694
695void PanelView::Layout() {
696  // |web_view_| might not be created yet when the window is first created.
697  if (web_view_)
698    web_view_->SetBounds(0, 0, width(), height());
699  OnViewWasResized();
700}
701
702gfx::Size PanelView::GetMinimumSize() {
703  return gfx::Size();
704}
705
706gfx::Size PanelView::GetMaximumSize() {
707  return gfx::Size();
708}
709
710bool PanelView::AcceleratorPressed(const ui::Accelerator& accelerator) {
711  if (mouse_pressed_ && accelerator.key_code() == ui::VKEY_ESCAPE) {
712    OnTitlebarMouseCaptureLost();
713    return true;
714  }
715
716  // No other accelerator is allowed when the drag begins.
717  if (mouse_dragging_state_ == DRAGGING_STARTED)
718    return true;
719
720  const std::map<ui::Accelerator, int>& accelerator_table =
721      GetAcceleratorTable();
722  std::map<ui::Accelerator, int>::const_iterator iter =
723      accelerator_table.find(accelerator);
724  DCHECK(iter != accelerator_table.end());
725  return panel_->ExecuteCommandIfEnabled(iter->second);
726}
727
728void PanelView::OnWidgetActivationChanged(views::Widget* widget, bool active) {
729#if defined(OS_WIN) && !defined(USE_AURA)
730  // The panel window is in focus (actually accepting keystrokes) if it is
731  // active and belongs to a foreground application.
732  bool focused = active && widget->GetNativeWindow() == ::GetForegroundWindow();
733#else
734  NOTIMPLEMENTED();
735  bool focused = active;
736#endif
737
738  if (focused_ == focused)
739    return;
740  focused_ = focused;
741
742  // Expand the panel if the minimized panel is activated by means other than
743  // clicking on its titlebar. This is the workaround to support restoring the
744  // minimized panel by other means, like alt-tabbing, win-tabbing, or clicking
745  // the taskbar icon. Note that this workaround does not work for one edge
746  // case: the mouse happens to be at the minimized panel when the user tries to
747  // bring up the panel with the above alternatives.
748  // When the user clicks on the minimized panel, the panel expansion will be
749  // done when we process the mouse button pressed message.
750  if (focused_ && panel_->IsMinimized() &&
751      gfx::Screen::GetScreenFor(widget->GetNativeWindow())->
752          GetWindowAtCursorScreenPoint() != widget->GetNativeWindow()) {
753    panel_->Restore();
754  }
755
756  panel()->OnActiveStateChanged(focused);
757}
758
759bool PanelView::OnTitlebarMousePressed(const gfx::Point& mouse_location) {
760  mouse_pressed_ = true;
761  mouse_dragging_state_ = NO_DRAGGING;
762  last_mouse_location_ = mouse_location;
763  return true;
764}
765
766bool PanelView::OnTitlebarMouseDragged(const gfx::Point& mouse_location) {
767  if (!mouse_pressed_)
768    return false;
769
770  if (mouse_dragging_state_ == NO_DRAGGING &&
771      ExceededDragThreshold(mouse_location - last_mouse_location_)) {
772    // When a drag begins, we do not want to the client area to still receive
773    // the focus. We do not need to do this for the unfocused minimized panel.
774    if (!panel_->IsMinimized()) {
775      old_focused_view_ = GetFocusManager()->GetFocusedView();
776      GetFocusManager()->SetFocusedView(GetFrameView());
777    }
778
779    panel_->manager()->StartDragging(panel_.get(), last_mouse_location_);
780    mouse_dragging_state_ = DRAGGING_STARTED;
781  }
782  if (mouse_dragging_state_ == DRAGGING_STARTED) {
783    panel_->manager()->Drag(mouse_location);
784
785    // Once in drag, update |last_mouse_location_| on each drag fragment, since
786    // we already dragged the panel up to the current mouse location.
787    last_mouse_location_ = mouse_location;
788  }
789  return true;
790}
791
792bool PanelView::OnTitlebarMouseReleased(panel::ClickModifier modifier) {
793  if (mouse_dragging_state_ != NO_DRAGGING) {
794    // Ensure dragging a minimized panel does not leave it activated.
795    // Windows activates a panel on mouse-down, regardless of our attempts
796    // to prevent activation of a minimized panel. Now that we know mouse-down
797    // resulted in a mouse-drag, we need to ensure the minimized panel is
798    // deactivated.
799    if (panel_->IsMinimized() && focused_)
800      panel_->Deactivate();
801
802    if (mouse_dragging_state_ == DRAGGING_STARTED) {
803      // When a drag ends, restore the focus.
804      if (old_focused_view_) {
805        GetFocusManager()->SetFocusedView(old_focused_view_);
806        old_focused_view_ = NULL;
807      }
808      return EndDragging(false);
809    }
810
811    // The panel drag was cancelled before the mouse is released. Do not
812    // treat this as a click.
813    return true;
814  }
815
816  panel_->OnTitlebarClicked(modifier);
817  return true;
818}
819
820bool PanelView::OnTitlebarMouseCaptureLost() {
821  if (mouse_dragging_state_ == DRAGGING_STARTED)
822    return EndDragging(true);
823  return true;
824}
825
826bool PanelView::EndDragging(bool cancelled) {
827  // Only handle clicks that started in our window.
828  if (!mouse_pressed_)
829    return false;
830  mouse_pressed_ = false;
831
832  mouse_dragging_state_ = DRAGGING_ENDED;
833  panel_->manager()->EndDragging(cancelled);
834  return true;
835}
836
837PanelFrameView* PanelView::GetFrameView() const {
838  return static_cast<PanelFrameView*>(window_->non_client_view()->frame_view());
839}
840
841bool PanelView::IsAnimatingBounds() const {
842  return bounds_animator_.get() && bounds_animator_->is_animating();
843}
844
845bool PanelView::IsWithinResizingArea(const gfx::Point& mouse_location) const {
846  gfx::Rect bounds = window_->GetWindowBoundsInScreen();
847  DCHECK(bounds.Contains(mouse_location));
848  return mouse_location.x() < bounds.x() + kResizeInsideBoundsSize ||
849         mouse_location.x() >= bounds.right() - kResizeInsideBoundsSize ||
850         mouse_location.y() < bounds.y() + kResizeInsideBoundsSize ||
851         mouse_location.y() >= bounds.bottom() - kResizeInsideBoundsSize;
852}
853
854#if defined(OS_WIN) && !defined(USE_ASH) && !defined(USE_AURA)
855void PanelView::UpdateWindowAttribute(int attribute_index,
856                                      int attribute_value_to_set,
857                                      int attribute_value_to_reset,
858                                      bool update_frame) {
859  gfx::NativeWindow native_window = window_->GetNativeWindow();
860  int value = ::GetWindowLong(native_window, attribute_index);
861  int expected_value = value;
862  if (attribute_value_to_set)
863    expected_value |=  attribute_value_to_set;
864  if (attribute_value_to_reset)
865    expected_value &=  ~attribute_value_to_reset;
866  if (value != expected_value)
867    ::SetWindowLong(native_window, attribute_index, expected_value);
868
869  // Per MSDN, if any of the frame styles is changed, SetWindowPos with the
870  // SWP_FRAMECHANGED flag must be called in order for the cached window data
871  // to be updated properly.
872  // http://msdn.microsoft.com/en-us/library/windows/desktop/ms633591(v=vs.85).aspx
873  if (update_frame) {
874    ::SetWindowPos(native_window, NULL, 0, 0, 0, 0,
875                   SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE |
876                       SWP_NOZORDER | SWP_NOACTIVATE);
877  }
878}
879#endif
880
881void PanelView::OnViewWasResized() {
882#if defined(OS_WIN) && !defined(USE_ASH) && !defined(USE_AURA)
883  content::WebContents* web_contents = panel_->GetWebContents();
884  if (!web_view_ || !web_contents)
885    return;
886
887  // When the panel is frameless or has thin frame, the mouse resizing should
888  // also be triggered from the part of client area that is close to the window
889  // frame.
890  int width = web_view_->size().width();
891  int height = web_view_->size().height();
892  // Compute the thickness of the client area that needs to be counted towards
893  // mouse resizing.
894  int thickness_for_mouse_resizing =
895      kResizeInsideBoundsSize - GetFrameView()->BorderThickness();
896  DCHECK(thickness_for_mouse_resizing > 0);
897  SkRegion* region = new SkRegion;
898  region->op(0, 0, thickness_for_mouse_resizing, height, SkRegion::kUnion_Op);
899  region->op(width - thickness_for_mouse_resizing, 0, width, height,
900      SkRegion::kUnion_Op);
901  region->op(0, height - thickness_for_mouse_resizing, width, height,
902      SkRegion::kUnion_Op);
903  web_contents->GetRenderViewHost()->GetView()->SetClickthroughRegion(region);
904#endif
905}
906