blob: d598cfb817501ddc395cd2ada65f36c2f962d1ca [file] [log] [blame]
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/views/frame/browser_frame_win.h"
#include <dwmapi.h>
#include <shellapi.h>
#include <set>
#include "app/resource_bundle.h"
#include "app/theme_provider.h"
#include "app/win_util.h"
#include "base/win_util.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/browser_theme_provider.h"
#include "chrome/browser/views/frame/app_panel_browser_frame_view.h"
#include "chrome/browser/views/frame/browser_non_client_frame_view.h"
#include "chrome/browser/views/frame/browser_root_view.h"
#include "chrome/browser/views/frame/browser_view.h"
#include "chrome/browser/views/frame/glass_browser_frame_view.h"
#include "chrome/browser/views/frame/opaque_browser_frame_view.h"
#include "grit/theme_resources.h"
#include "views/screen.h"
#include "views/window/window_delegate.h"
// static
static const int kClientEdgeThickness = 3;
static const int kTabDragWindowAlpha = 200;
// We need to offset the DWMFrame into the toolbar so that the blackness
// doesn't show up on our rounded corners.
static const int kDWMFrameTopOffset = 3;
// static (Factory method.)
BrowserFrame* BrowserFrame::Create(BrowserView* browser_view,
Profile* profile) {
BrowserFrameWin* frame = new BrowserFrameWin(browser_view, profile);
frame->Init();
return frame;
}
// static
const gfx::Font& BrowserFrame::GetTitleFont() {
static gfx::Font* title_font = new gfx::Font(win_util::GetWindowTitleFont());
return *title_font;
}
///////////////////////////////////////////////////////////////////////////////
// BrowserFrame, public:
BrowserFrameWin::BrowserFrameWin(BrowserView* browser_view, Profile* profile)
: WindowWin(browser_view),
browser_view_(browser_view),
root_view_(NULL),
frame_initialized_(false),
profile_(profile) {
browser_view_->set_frame(this);
GetNonClientView()->SetFrameView(CreateFrameViewForWindow());
// Don't focus anything on creation, selecting a tab will set the focus.
set_focus_on_creation(false);
}
void BrowserFrameWin::Init() {
WindowWin::Init(NULL, gfx::Rect());
}
BrowserFrameWin::~BrowserFrameWin() {
}
views::Window* BrowserFrameWin::GetWindow() {
return this;
}
int BrowserFrameWin::GetMinimizeButtonOffset() const {
TITLEBARINFOEX titlebar_info;
titlebar_info.cbSize = sizeof(TITLEBARINFOEX);
SendMessage(GetNativeView(), WM_GETTITLEBARINFOEX, 0, (WPARAM)&titlebar_info);
CPoint minimize_button_corner(titlebar_info.rgrect[2].left,
titlebar_info.rgrect[2].top);
MapWindowPoints(HWND_DESKTOP, GetNativeView(), &minimize_button_corner, 1);
return minimize_button_corner.x;
}
gfx::Rect BrowserFrameWin::GetBoundsForTabStrip(BaseTabStrip* tabstrip) const {
return browser_frame_view_->GetBoundsForTabStrip(tabstrip);
}
int BrowserFrameWin::GetHorizontalTabStripVerticalOffset(bool restored) const {
return browser_frame_view_->GetHorizontalTabStripVerticalOffset(restored);
}
void BrowserFrameWin::UpdateThrobber(bool running) {
browser_frame_view_->UpdateThrobber(running);
}
void BrowserFrameWin::ContinueDraggingDetachedTab() {
// Send the message directly, so that the window is positioned appropriately.
SendMessage(GetNativeWindow(), WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(0, 0));
}
ThemeProvider* BrowserFrameWin::GetThemeProviderForFrame() const {
// This is implemented for a different interface than GetThemeProvider is,
// but they mean the same things.
return GetThemeProvider();
}
bool BrowserFrameWin::AlwaysUseNativeFrame() const {
// App panel windows draw their own frame.
if (browser_view_->IsBrowserTypePanel())
return false;
if (browser_view_->browser()->type() == Browser::TYPE_EXTENSION_APP)
return false;
// We don't theme popup or app windows, so regardless of whether or not a
// theme is active for normal browser windows, we don't want to use the custom
// frame for popups/apps.
if (!browser_view_->IsBrowserTypeNormal() && win_util::ShouldUseVistaFrame())
return true;
// Otherwise, we use the native frame when we're told we should by the theme
// provider (e.g. no custom theme is active).
return GetThemeProvider()->ShouldUseNativeFrame();
}
views::View* BrowserFrameWin::GetFrameView() const {
return browser_frame_view_;
}
void BrowserFrameWin::TabStripDisplayModeChanged() {
GetRootView()->Layout();
UpdateDWMFrame();
GetRootView()->Layout();
}
///////////////////////////////////////////////////////////////////////////////
// BrowserFrame, views::WindowWin overrides:
gfx::Insets BrowserFrameWin::GetClientAreaInsets() const {
// Use the default client insets for an opaque frame or a glass popup/app
// frame.
if (!GetNonClientView()->UseNativeFrame() ||
!browser_view_->IsBrowserTypeNormal()) {
return WindowWin::GetClientAreaInsets();
}
int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME);
// In fullscreen mode, we have no frame. In restored mode, we draw our own
// client edge over part of the default frame.
if (IsFullscreen())
border_thickness = 0;
else if (!IsMaximized())
border_thickness -= kClientEdgeThickness;
return gfx::Insets(0, border_thickness, border_thickness, border_thickness);
}
bool BrowserFrameWin::GetAccelerator(int cmd_id,
menus::Accelerator* accelerator) {
return browser_view_->GetAccelerator(cmd_id, accelerator);
}
void BrowserFrameWin::OnEndSession(BOOL ending, UINT logoff) {
BrowserList::WindowsSessionEnding();
}
void BrowserFrameWin::OnEnterSizeMove() {
browser_view_->WindowMoveOrResizeStarted();
}
void BrowserFrameWin::OnExitSizeMove() {
WidgetWin::OnExitSizeMove();
}
void BrowserFrameWin::OnInitMenuPopup(HMENU menu, UINT position,
BOOL is_system_menu) {
browser_view_->PrepareToRunSystemMenu(menu);
}
LRESULT BrowserFrameWin::OnMouseActivate(HWND window, UINT hittest_code,
UINT message) {
return browser_view_->ActivateAppModalDialog() ? MA_NOACTIVATEANDEAT
: MA_ACTIVATE;
}
void BrowserFrameWin::OnMove(const CPoint& point) {
browser_view_->WindowMoved();
}
void BrowserFrameWin::OnMoving(UINT param, LPRECT new_bounds) {
browser_view_->WindowMoved();
}
LRESULT BrowserFrameWin::OnNCActivate(BOOL active) {
if (browser_view_->ActivateAppModalDialog())
return TRUE;
browser_view_->ActivationChanged(!!active);
return WindowWin::OnNCActivate(active);
}
LRESULT BrowserFrameWin::OnNCHitTest(const CPoint& pt) {
// Only do DWM hit-testing when we are using the native frame.
if (GetNonClientView()->UseNativeFrame()) {
LRESULT result;
if (DwmDefWindowProc(GetNativeView(), WM_NCHITTEST, 0,
MAKELPARAM(pt.x, pt.y), &result)) {
return result;
}
}
return WindowWin::OnNCHitTest(pt);
}
void BrowserFrameWin::OnWindowPosChanged(WINDOWPOS* window_pos) {
// Windows lies to us about the position of the minimize button before a
// window is visible. We use the position of the minimize button to place the
// distributor logo in official builds. When the window is shown, we need to
// re-layout and schedule a paint for the non-client frame view so that the
// distributor logo has the correct position when the window becomes visible.
// This fixes bugs where the distributor logo appears to overlay the minimize
// button. http://crbug.com/15520
// Note that we will call Layout every time SetWindowPos is called with
// SWP_SHOWWINDOW, however callers typically are careful about not specifying
// this flag unless necessary to avoid flicker.
if (window_pos->flags & SWP_SHOWWINDOW) {
GetNonClientView()->Layout();
GetNonClientView()->SchedulePaint();
}
UpdateDWMFrame();
// Let the default window procedure handle - IMPORTANT!
WindowWin::OnWindowPosChanged(window_pos);
// Windows lies to us about the position of the minimize button before a
// window is visible. We use this position to place the OTR avatar in RTL
// mode, so when the window is shown, we need to re-layout and schedule a
// paint for the non-client frame view so that the icon top has the correct
// position when the window becomes visible. This fixes bugs where the icon
// appears to overlay the minimize button.
// Note that we will call Layout every time SetWindowPos is called with
// SWP_SHOWWINDOW, however callers typically are careful about not specifying
// this flag unless necessary to avoid flicker.
if (window_pos->flags & SWP_SHOWWINDOW) {
GetNonClientView()->Layout();
GetNonClientView()->SchedulePaint();
}
}
ThemeProvider* BrowserFrameWin::GetThemeProvider() const {
return profile_->GetThemeProvider();
}
ThemeProvider* BrowserFrameWin::GetDefaultThemeProvider() const {
return profile_->GetThemeProvider();
}
///////////////////////////////////////////////////////////////////////////////
// BrowserFrame, views::CustomFrameWindow overrides:
int BrowserFrameWin::GetShowState() const {
return browser_view_->GetShowState();
}
void BrowserFrameWin::Activate() {
// When running under remote desktop, if the remote desktop client is not
// active on the users desktop, then none of the windows contained in the
// remote desktop will be activated. However, WindowWin::Activate will still
// bring this browser window to the foreground. We explicitly set ourselves
// as the last active browser window to ensure that we get treated as such by
// the rest of Chrome.
BrowserList::SetLastActive(browser_view_->browser());
WindowWin::Activate();
}
views::NonClientFrameView* BrowserFrameWin::CreateFrameViewForWindow() {
if (AlwaysUseNativeFrame())
browser_frame_view_ = new GlassBrowserFrameView(this, browser_view_);
else if (browser_view_->IsBrowserTypePanel())
browser_frame_view_ = new AppPanelBrowserFrameView(this, browser_view_);
else
browser_frame_view_ = new OpaqueBrowserFrameView(this, browser_view_);
return browser_frame_view_;
}
void BrowserFrameWin::UpdateFrameAfterFrameChange() {
// We need to update the glass region on or off before the base class adjusts
// the window region.
UpdateDWMFrame();
WindowWin::UpdateFrameAfterFrameChange();
}
views::RootView* BrowserFrameWin::CreateRootView() {
root_view_ = new BrowserRootView(browser_view_, this);
return root_view_;
}
///////////////////////////////////////////////////////////////////////////////
// BrowserFrame, private:
void BrowserFrameWin::UpdateDWMFrame() {
// Nothing to do yet, or we're not showing a DWM frame.
if (!GetClientView() || !AlwaysUseNativeFrame())
return;
MARGINS margins = { 0 };
if (browser_view_->IsBrowserTypeNormal()) {
// In fullscreen mode, we don't extend glass into the client area at all,
// because the GDI-drawn text in the web content composited over it will
// become semi-transparent over any glass area.
if (!IsMaximized() && !IsFullscreen()) {
margins.cxLeftWidth = kClientEdgeThickness + 1;
margins.cxRightWidth = kClientEdgeThickness + 1;
margins.cyBottomHeight = kClientEdgeThickness + 1;
margins.cyTopHeight = kClientEdgeThickness + 1;
}
// In maximized mode, we only have a titlebar strip of glass, no side/bottom
// borders.
if (!browser_view_->IsFullscreen()) {
gfx::Rect tabstrip_bounds(
GetBoundsForTabStrip(browser_view_->tabstrip()));
margins.cyTopHeight = (browser_view_->UseVerticalTabs() ?
tabstrip_bounds.y() : tabstrip_bounds.bottom()) + kDWMFrameTopOffset;
}
} else {
// For popup and app windows we want to use the default margins.
}
DwmExtendFrameIntoClientArea(GetNativeView(), &margins);
}