browser_frame_win.cc revision 3f50c38dc070f4bb515c1b64450dae14f316474e
1// Copyright (c) 2010 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/views/frame/browser_frame_win.h"
6
7#include <dwmapi.h>
8#include <shellapi.h>
9
10#include <set>
11
12#include "app/win/hwnd_util.h"
13#include "app/win/win_util.h"
14#include "chrome/browser/accessibility/browser_accessibility_state.h"
15#include "chrome/browser/profiles/profile.h"
16#include "chrome/browser/browser_list.h"
17#include "chrome/browser/themes/browser_theme_provider.h"
18#include "chrome/browser/views/frame/browser_non_client_frame_view.h"
19#include "chrome/browser/views/frame/browser_root_view.h"
20#include "chrome/browser/views/frame/browser_view.h"
21#include "chrome/browser/views/frame/glass_browser_frame_view.h"
22#include "gfx/font.h"
23#include "grit/theme_resources.h"
24#include "views/screen.h"
25#include "views/window/window_delegate.h"
26
27// static
28static const int kClientEdgeThickness = 3;
29static const int kTabDragWindowAlpha = 200;
30// We need to offset the DWMFrame into the toolbar so that the blackness
31// doesn't show up on our rounded corners.
32static const int kDWMFrameTopOffset = 3;
33
34// static (Factory method.)
35BrowserFrame* BrowserFrame::Create(BrowserView* browser_view,
36                                   Profile* profile) {
37  BrowserFrameWin* frame = new BrowserFrameWin(browser_view, profile);
38  frame->Init();
39  return frame;
40}
41
42// static
43const gfx::Font& BrowserFrame::GetTitleFont() {
44  static gfx::Font* title_font = new gfx::Font(app::win::GetWindowTitleFont());
45  return *title_font;
46}
47
48///////////////////////////////////////////////////////////////////////////////
49// BrowserFrame, public:
50
51BrowserFrameWin::BrowserFrameWin(BrowserView* browser_view, Profile* profile)
52    : WindowWin(browser_view),
53      browser_view_(browser_view),
54      root_view_(NULL),
55      frame_initialized_(false),
56      profile_(profile) {
57  browser_view_->set_frame(this);
58  GetNonClientView()->SetFrameView(CreateFrameViewForWindow());
59  // Don't focus anything on creation, selecting a tab will set the focus.
60  set_focus_on_creation(false);
61}
62
63void BrowserFrameWin::Init() {
64  WindowWin::Init(NULL, gfx::Rect());
65}
66
67BrowserFrameWin::~BrowserFrameWin() {
68}
69
70views::Window* BrowserFrameWin::GetWindow() {
71  return this;
72}
73
74int BrowserFrameWin::GetMinimizeButtonOffset() const {
75  TITLEBARINFOEX titlebar_info;
76  titlebar_info.cbSize = sizeof(TITLEBARINFOEX);
77  SendMessage(GetNativeView(), WM_GETTITLEBARINFOEX, 0, (WPARAM)&titlebar_info);
78
79  CPoint minimize_button_corner(titlebar_info.rgrect[2].left,
80                                titlebar_info.rgrect[2].top);
81  MapWindowPoints(HWND_DESKTOP, GetNativeView(), &minimize_button_corner, 1);
82
83  return minimize_button_corner.x;
84}
85
86gfx::Rect BrowserFrameWin::GetBoundsForTabStrip(BaseTabStrip* tabstrip) const {
87  return browser_frame_view_->GetBoundsForTabStrip(tabstrip);
88}
89
90int BrowserFrameWin::GetHorizontalTabStripVerticalOffset(bool restored) const {
91  return browser_frame_view_->GetHorizontalTabStripVerticalOffset(restored);
92}
93
94void BrowserFrameWin::UpdateThrobber(bool running) {
95  browser_frame_view_->UpdateThrobber(running);
96}
97
98ThemeProvider* BrowserFrameWin::GetThemeProviderForFrame() const {
99  // This is implemented for a different interface than GetThemeProvider is,
100  // but they mean the same things.
101  return GetThemeProvider();
102}
103
104bool BrowserFrameWin::AlwaysUseNativeFrame() const {
105  // App panel windows draw their own frame.
106  if (browser_view_->IsBrowserTypePanel())
107    return false;
108
109  // We don't theme popup or app windows, so regardless of whether or not a
110  // theme is active for normal browser windows, we don't want to use the custom
111  // frame for popups/apps.
112  if (!browser_view_->IsBrowserTypeNormal() && app::win::ShouldUseVistaFrame())
113    return true;
114
115  // Otherwise, we use the native frame when we're told we should by the theme
116  // provider (e.g. no custom theme is active).
117  return GetThemeProvider()->ShouldUseNativeFrame();
118}
119
120views::View* BrowserFrameWin::GetFrameView() const {
121  return browser_frame_view_;
122}
123
124void BrowserFrameWin::TabStripDisplayModeChanged() {
125  if (GetRootView()->GetChildViewCount() > 0) {
126    // Make sure the child of the root view gets Layout again.
127    GetRootView()->GetChildViewAt(0)->InvalidateLayout();
128  }
129  GetRootView()->Layout();
130
131  UpdateDWMFrame();
132}
133
134///////////////////////////////////////////////////////////////////////////////
135// BrowserFrame, views::WindowWin overrides:
136
137gfx::Insets BrowserFrameWin::GetClientAreaInsets() const {
138  // Use the default client insets for an opaque frame or a glass popup/app
139  // frame.
140  if (!GetNonClientView()->UseNativeFrame() ||
141      !browser_view_->IsBrowserTypeNormal()) {
142    return WindowWin::GetClientAreaInsets();
143  }
144
145  int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME);
146  // In fullscreen mode, we have no frame. In restored mode, we draw our own
147  // client edge over part of the default frame.
148  if (IsFullscreen())
149    border_thickness = 0;
150  else if (!IsMaximized())
151    border_thickness -= kClientEdgeThickness;
152  return gfx::Insets(0, border_thickness, border_thickness, border_thickness);
153}
154
155bool BrowserFrameWin::GetAccelerator(int cmd_id,
156                                     menus::Accelerator* accelerator) {
157  return browser_view_->GetAccelerator(cmd_id, accelerator);
158}
159
160void BrowserFrameWin::OnEndSession(BOOL ending, UINT logoff) {
161  BrowserList::SessionEnding();
162}
163
164void BrowserFrameWin::OnEnterSizeMove() {
165  browser_view_->WindowMoveOrResizeStarted();
166}
167
168void BrowserFrameWin::OnExitSizeMove() {
169  WidgetWin::OnExitSizeMove();
170}
171
172void BrowserFrameWin::OnInitMenuPopup(HMENU menu, UINT position,
173                                      BOOL is_system_menu) {
174  browser_view_->PrepareToRunSystemMenu(menu);
175}
176
177LRESULT BrowserFrameWin::OnMouseActivate(HWND window, UINT hittest_code,
178                                         UINT message) {
179  return browser_view_->ActivateAppModalDialog() ? MA_NOACTIVATEANDEAT
180                                                 : MA_ACTIVATE;
181}
182
183void BrowserFrameWin::OnMove(const CPoint& point) {
184  browser_view_->WindowMoved();
185}
186
187void BrowserFrameWin::OnMoving(UINT param, LPRECT new_bounds) {
188  browser_view_->WindowMoved();
189}
190
191LRESULT BrowserFrameWin::OnNCActivate(BOOL active) {
192  if (browser_view_->ActivateAppModalDialog())
193    return TRUE;
194
195  browser_view_->ActivationChanged(!!active);
196  return WindowWin::OnNCActivate(active);
197}
198
199LRESULT BrowserFrameWin::OnNCHitTest(const CPoint& pt) {
200  // Only do DWM hit-testing when we are using the native frame.
201  if (GetNonClientView()->UseNativeFrame()) {
202    LRESULT result;
203    if (DwmDefWindowProc(GetNativeView(), WM_NCHITTEST, 0,
204                         MAKELPARAM(pt.x, pt.y), &result)) {
205      return result;
206    }
207  }
208  return WindowWin::OnNCHitTest(pt);
209}
210
211void BrowserFrameWin::OnWindowPosChanged(WINDOWPOS* window_pos) {
212  WindowWin::OnWindowPosChanged(window_pos);
213  UpdateDWMFrame();
214
215  // Windows lies to us about the position of the minimize button before a
216  // window is visible.  We use this position to place the OTR avatar in RTL
217  // mode, so when the window is shown, we need to re-layout and schedule a
218  // paint for the non-client frame view so that the icon top has the correct
219  // position when the window becomes visible.  This fixes bugs where the icon
220  // appears to overlay the minimize button.
221  // Note that we will call Layout every time SetWindowPos is called with
222  // SWP_SHOWWINDOW, however callers typically are careful about not specifying
223  // this flag unless necessary to avoid flicker.
224  if (window_pos->flags & SWP_SHOWWINDOW) {
225    GetNonClientView()->Layout();
226    GetNonClientView()->SchedulePaint();
227  }
228}
229
230ThemeProvider* BrowserFrameWin::GetThemeProvider() const {
231  return profile_->GetThemeProvider();
232}
233
234ThemeProvider* BrowserFrameWin::GetDefaultThemeProvider() const {
235  return profile_->GetThemeProvider();
236}
237
238void BrowserFrameWin::OnScreenReaderDetected() {
239  BrowserAccessibilityState::GetInstance()->OnScreenReaderDetected();
240  WindowWin::OnScreenReaderDetected();
241}
242
243///////////////////////////////////////////////////////////////////////////////
244// BrowserFrame, views::CustomFrameWindow overrides:
245
246int BrowserFrameWin::GetShowState() const {
247  return browser_view_->GetShowState();
248}
249
250void BrowserFrameWin::Activate() {
251  // When running under remote desktop, if the remote desktop client is not
252  // active on the users desktop, then none of the windows contained in the
253  // remote desktop will be activated.  However, WindowWin::Activate will still
254  // bring this browser window to the foreground.  We explicitly set ourselves
255  // as the last active browser window to ensure that we get treated as such by
256  // the rest of Chrome.
257  BrowserList::SetLastActive(browser_view_->browser());
258
259  WindowWin::Activate();
260}
261
262views::NonClientFrameView* BrowserFrameWin::CreateFrameViewForWindow() {
263  if (AlwaysUseNativeFrame())
264    browser_frame_view_ = new GlassBrowserFrameView(this, browser_view_);
265  else
266    browser_frame_view_ =
267        browser::CreateBrowserNonClientFrameView(this, browser_view_);
268  return browser_frame_view_;
269}
270
271void BrowserFrameWin::UpdateFrameAfterFrameChange() {
272  // We need to update the glass region on or off before the base class adjusts
273  // the window region.
274  UpdateDWMFrame();
275  WindowWin::UpdateFrameAfterFrameChange();
276}
277
278views::RootView* BrowserFrameWin::CreateRootView() {
279  root_view_ = new BrowserRootView(browser_view_, this);
280  return root_view_;
281}
282
283///////////////////////////////////////////////////////////////////////////////
284// BrowserFrame, private:
285
286void BrowserFrameWin::UpdateDWMFrame() {
287  // Nothing to do yet, or we're not showing a DWM frame.
288  if (!GetClientView() || !AlwaysUseNativeFrame())
289    return;
290
291  MARGINS margins = { 0 };
292  if (browser_view_->IsBrowserTypeNormal()) {
293    // In fullscreen mode, we don't extend glass into the client area at all,
294    // because the GDI-drawn text in the web content composited over it will
295    // become semi-transparent over any glass area.
296    if (!IsMaximized() && !IsFullscreen()) {
297      margins.cxLeftWidth = kClientEdgeThickness + 1;
298      margins.cxRightWidth = kClientEdgeThickness + 1;
299      margins.cyBottomHeight = kClientEdgeThickness + 1;
300      margins.cyTopHeight = kClientEdgeThickness + 1;
301    }
302    // In maximized mode, we only have a titlebar strip of glass, no side/bottom
303    // borders.
304    if (!browser_view_->IsFullscreen()) {
305      gfx::Rect tabstrip_bounds(
306          GetBoundsForTabStrip(browser_view_->tabstrip()));
307      margins.cyTopHeight = (browser_view_->UseVerticalTabs() ?
308          tabstrip_bounds.y() : tabstrip_bounds.bottom()) + kDWMFrameTopOffset;
309    }
310  } else {
311    // For popup and app windows we want to use the default margins.
312  }
313  DwmExtendFrameIntoClientArea(GetNativeView(), &margins);
314}
315