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