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