Index: chrome/views/custom_frame_window.cc |
=================================================================== |
--- chrome/views/custom_frame_window.cc (revision 10688) |
+++ chrome/views/custom_frame_window.cc (working copy) |
@@ -1,506 +0,0 @@ |
-// Copyright (c) 2006-2008 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/views/custom_frame_window.h" |
- |
-#include "base/gfx/point.h" |
-#include "base/gfx/size.h" |
-#include "chrome/common/gfx/chrome_canvas.h" |
-#include "chrome/common/gfx/path.h" |
-#include "chrome/common/l10n_util.h" |
-#include "chrome/common/win_util.h" |
-#include "chrome/views/client_view.h" |
-#include "chrome/views/default_non_client_view.h" |
-#include "chrome/views/root_view.h" |
-#include "chrome/views/window_delegate.h" |
-#include "grit/generated_resources.h" |
- |
-namespace views { |
- |
-// A scoping class that prevents a window from being able to redraw in response |
-// to invalidations that may occur within it for the lifetime of the object. |
-// |
-// Why would we want such a thing? Well, it turns out Windows has some |
-// "unorthodox" behavior when it comes to painting its non-client areas. |
-// Occasionally, Windows will paint portions of the default non-client area |
-// right over the top of the custom frame. This is not simply fixed by handling |
-// WM_NCPAINT/WM_PAINT, with some investigation it turns out that this |
-// rendering is being done *inside* the default implementation of some message |
-// handlers and functions: |
-// . WM_SETTEXT |
-// . WM_SETICON |
-// . WM_NCLBUTTONDOWN |
-// . EnableMenuItem, called from our WM_INITMENU handler |
-// The solution is to handle these messages and call DefWindowProc ourselves, |
-// but prevent the window from being able to update itself for the duration of |
-// the call. We do this with this class, which automatically calls its |
-// associated CustomFrameWindow's lock and unlock functions as it is created |
-// and destroyed. See documentation in those methods for the technique used. |
-// |
-// IMPORTANT: Do not use this scoping object for large scopes or periods of |
-// time! IT WILL PREVENT THE WINDOW FROM BEING REDRAWN! (duh). |
-// |
-// I would love to hear Raymond Chen's explanation for all this. And maybe a |
-// list of other messages that this applies to ;-) |
-class CustomFrameWindow::ScopedRedrawLock { |
- public: |
- explicit ScopedRedrawLock(CustomFrameWindow* window) : window_(window) { |
- window_->LockUpdates(); |
- } |
- |
- ~ScopedRedrawLock() { |
- window_->UnlockUpdates(); |
- } |
- |
- private: |
- // The window having its style changed. |
- CustomFrameWindow* window_; |
-}; |
- |
-HCURSOR CustomFrameWindow::resize_cursors_[6]; |
- |
-/////////////////////////////////////////////////////////////////////////////// |
-// CustomFrameWindow, public: |
- |
-CustomFrameWindow::CustomFrameWindow(WindowDelegate* window_delegate) |
- : Window(window_delegate), |
- is_active_(false), |
- lock_updates_(false), |
- saved_window_style_(0) { |
- InitClass(); |
- non_client_view_ = new DefaultNonClientView(this); |
-} |
- |
-CustomFrameWindow::CustomFrameWindow(WindowDelegate* window_delegate, |
- NonClientView* non_client_view) |
- : Window(window_delegate) { |
- InitClass(); |
- non_client_view_ = non_client_view; |
-} |
- |
-CustomFrameWindow::~CustomFrameWindow() { |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////// |
-// CustomFrameWindow, Window overrides: |
- |
-void CustomFrameWindow::Init(HWND parent, const gfx::Rect& bounds) { |
- // TODO(beng): (Cleanup) Right now, the only way to specify a different |
- // non-client view is to subclass this object and provide one |
- // by setting this member before calling Init. |
- if (!non_client_view_) |
- non_client_view_ = new DefaultNonClientView(this); |
- Window::Init(parent, bounds); |
- |
- ResetWindowRegion(); |
-} |
- |
-void CustomFrameWindow::UpdateWindowTitle() { |
- // Layout winds up causing the title to be re-validated during |
- // string measurement. |
- non_client_view_->Layout(); |
- // Must call the base class too so that places like the Task Bar get updated. |
- Window::UpdateWindowTitle(); |
-} |
- |
-void CustomFrameWindow::UpdateWindowIcon() { |
- // The icon will be re-validated during painting. |
- non_client_view_->SchedulePaint(); |
- // Call the base class so that places like the Task Bar get updated. |
- Window::UpdateWindowIcon(); |
-} |
- |
-void CustomFrameWindow::EnableClose(bool enable) { |
- non_client_view_->EnableClose(enable); |
- // Make sure the SysMenu changes to reflect this change as well. |
- Window::EnableClose(enable); |
-} |
- |
-void CustomFrameWindow::DisableInactiveRendering(bool disable) { |
- Window::DisableInactiveRendering(disable); |
- non_client_view_->set_paint_as_active(disable); |
- if (!disable) |
- non_client_view_->SchedulePaint(); |
-} |
- |
-void CustomFrameWindow::SizeWindowToDefault() { |
- gfx::Size pref = client_view()->GetPreferredSize(); |
- DCHECK(pref.width() > 0 && pref.height() > 0); |
- gfx::Size window_size = |
- non_client_view_->CalculateWindowSizeForClientSize(pref.width(), |
- pref.height()); |
- win_util::CenterAndSizeWindow(owning_window(), GetHWND(), |
- window_size.ToSIZE(), false); |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////// |
-// CustomFrameWindow, WidgetWin overrides: |
- |
-static void EnableMenuItem(HMENU menu, UINT command, bool enabled) { |
- UINT flags = MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_DISABLED | MF_GRAYED); |
- EnableMenuItem(menu, command, flags); |
-} |
- |
-void CustomFrameWindow::OnInitMenu(HMENU menu) { |
- bool is_minimized = IsMinimized(); |
- bool is_maximized = IsMaximized(); |
- bool is_restored = !is_minimized && !is_maximized; |
- |
- ScopedRedrawLock lock(this); |
- EnableMenuItem(menu, SC_RESTORE, !is_restored); |
- EnableMenuItem(menu, SC_MOVE, is_restored); |
- EnableMenuItem(menu, SC_SIZE, window_delegate()->CanResize() && is_restored); |
- EnableMenuItem(menu, SC_MAXIMIZE, |
- window_delegate()->CanMaximize() && !is_maximized); |
- EnableMenuItem(menu, SC_MINIMIZE, |
- window_delegate()->CanMaximize() && !is_minimized); |
-} |
- |
-void CustomFrameWindow::OnMouseLeave() { |
- bool process_mouse_exited = true; |
- POINT pt; |
- if (GetCursorPos(&pt)) { |
- LRESULT ht_component = |
- ::SendMessage(GetHWND(), WM_NCHITTEST, 0, MAKELPARAM(pt.x, pt.y)); |
- if (ht_component != HTNOWHERE) { |
- // If the mouse moved into a part of the window's non-client area, then |
- // don't send a mouse exited event since the mouse is still within the |
- // bounds of the ChromeView that's rendering the frame. Note that we do |
- // _NOT_ do this for windows with native frames, since in that case the |
- // mouse really will have left the bounds of the RootView. |
- process_mouse_exited = false; |
- } |
- } |
- |
- if (process_mouse_exited) |
- ProcessMouseExited(); |
-} |
- |
-LRESULT CustomFrameWindow::OnNCActivate(BOOL active) { |
- is_active_ = !!active; |
- |
- // We can get WM_NCACTIVATE before we're actually visible. If we're not |
- // visible, no need to paint. |
- if (IsWindowVisible(GetHWND())) { |
- non_client_view_->SchedulePaint(); |
- // We need to force a paint now, as a user dragging a window will block |
- // painting operations while the move is in progress. |
- PaintNow(root_view_->GetScheduledPaintRect()); |
- } |
- |
- // Defering to our parent as it is important that the NCActivate message gets |
- // DefProc'ed or the task bar won't show our process as active. |
- // See bug http://crbug.com/4513. |
- return Window::OnNCActivate(active); |
-} |
- |
-LRESULT CustomFrameWindow::OnNCCalcSize(BOOL mode, LPARAM l_param) { |
- // We need to repaint all when the window bounds change. |
- return WVR_REDRAW; |
-} |
- |
-LRESULT CustomFrameWindow::OnNCHitTest(const CPoint& point) { |
- // NC points are in screen coordinates. |
- CPoint temp = point; |
- MapWindowPoints(HWND_DESKTOP, GetHWND(), &temp, 1); |
- return non_client_view_->NonClientHitTest(gfx::Point(temp.x, temp.y)); |
-} |
- |
-struct ClipState { |
- // The window being painted. |
- HWND parent; |
- |
- // DC painting to. |
- HDC dc; |
- |
- // Origin of the window in terms of the screen. |
- int x; |
- int y; |
-}; |
- |
-// See comments in OnNCPaint for details of this function. |
-static BOOL CALLBACK ClipDCToChild(HWND window, LPARAM param) { |
- ClipState* clip_state = reinterpret_cast<ClipState*>(param); |
- if (GetParent(window) == clip_state->parent && IsWindowVisible(window)) { |
- RECT bounds; |
- GetWindowRect(window, &bounds); |
- ExcludeClipRect(clip_state->dc, |
- bounds.left - clip_state->x, |
- bounds.top - clip_state->y, |
- bounds.right - clip_state->x, |
- bounds.bottom - clip_state->y); |
- } |
- return TRUE; |
-} |
- |
-void CustomFrameWindow::OnNCPaint(HRGN rgn) { |
- // We have an NC region and need to paint it. We expand the NC region to |
- // include the dirty region of the root view. This is done to minimize |
- // paints. |
- CRect window_rect; |
- GetWindowRect(&window_rect); |
- |
- if (window_rect.Width() != root_view_->width() || |
- window_rect.Height() != root_view_->height()) { |
- // If the size of the window differs from the size of the root view it |
- // means we're being asked to paint before we've gotten a WM_SIZE. This can |
- // happen when the user is interactively resizing the window. To avoid |
- // mass flickering we don't do anything here. Once we get the WM_SIZE we'll |
- // reset the region of the window which triggers another WM_NCPAINT and |
- // all is well. |
- return; |
- } |
- |
- CRect dirty_region; |
- // A value of 1 indicates paint all. |
- if (!rgn || rgn == reinterpret_cast<HRGN>(1)) { |
- dirty_region = CRect(0, 0, window_rect.Width(), window_rect.Height()); |
- } else { |
- RECT rgn_bounding_box; |
- GetRgnBox(rgn, &rgn_bounding_box); |
- if (!IntersectRect(&dirty_region, &rgn_bounding_box, &window_rect)) |
- return; // Dirty region doesn't intersect window bounds, bale. |
- |
- // rgn_bounding_box is in screen coordinates. Map it to window coordinates. |
- OffsetRect(&dirty_region, -window_rect.left, -window_rect.top); |
- } |
- |
- // In theory GetDCEx should do what we want, but I couldn't get it to work. |
- // In particular the docs mentiond DCX_CLIPCHILDREN, but as far as I can tell |
- // it doesn't work at all. So, instead we get the DC for the window then |
- // manually clip out the children. |
- HDC dc = GetWindowDC(GetHWND()); |
- ClipState clip_state; |
- clip_state.x = window_rect.left; |
- clip_state.y = window_rect.top; |
- clip_state.parent = GetHWND(); |
- clip_state.dc = dc; |
- EnumChildWindows(GetHWND(), &ClipDCToChild, |
- reinterpret_cast<LPARAM>(&clip_state)); |
- |
- RootView* root_view = GetRootView(); |
- CRect old_paint_region = root_view->GetScheduledPaintRectConstrainedToSize(); |
- |
- if (!old_paint_region.IsRectEmpty()) { |
- // The root view has a region that needs to be painted. Include it in the |
- // region we're going to paint. |
- |
- CRect tmp = dirty_region; |
- UnionRect(&dirty_region, &tmp, &old_paint_region); |
- } |
- |
- root_view->SchedulePaint(gfx::Rect(dirty_region), false); |
- |
- // ChromeCanvasPaints destructor does the actual painting. As such, wrap the |
- // following in a block to force paint to occur so that we can release the dc. |
- { |
- ChromeCanvasPaint canvas(dc, opaque(), dirty_region.left, dirty_region.top, |
- dirty_region.Width(), dirty_region.Height()); |
- |
- root_view->ProcessPaint(&canvas); |
- } |
- |
- ReleaseDC(GetHWND(), dc); |
-} |
- |
-void CustomFrameWindow::OnNCLButtonDown(UINT ht_component, |
- const CPoint& point) { |
- switch (ht_component) { |
- case HTCLOSE: |
- case HTMINBUTTON: |
- case HTMAXBUTTON: { |
- // When the mouse is pressed down in these specific non-client areas, we |
- // need to tell the RootView to send the mouse pressed event (which sets |
- // capture, allowing subsequent WM_LBUTTONUP (note, _not_ WM_NCLBUTTONUP) |
- // to fire so that the appropriate WM_SYSCOMMAND can be sent by the |
- // applicable button's ButtonListener. We _have_ to do this this way |
- // rather than letting Windows just send the syscommand itself (as would |
- // happen if we never did this dance) because for some insane reason |
- // DefWindowProc for WM_NCLBUTTONDOWN also renders the pressed window |
- // control button appearance, in the Windows classic style, over our |
- // view! Ick! By handling this message we prevent Windows from doing this |
- // undesirable thing, but that means we need to roll the sys-command |
- // handling ourselves. |
- ProcessNCMousePress(point, MK_LBUTTON); |
- return; |
- } |
- default: |
- Window::OnNCLButtonDown(ht_component, point); |
- /* |
- if (!IsMsgHandled()) { |
- // Window::OnNCLButtonDown set the message as unhandled. This normally |
- // means WidgetWin::ProcessWindowMessage will pass it to |
- // DefWindowProc. Sadly, DefWindowProc for WM_NCLBUTTONDOWN does weird |
- // non-client painting, so we need to call it directly here inside a |
- // scoped update lock. |
- ScopedRedrawLock lock(this); |
- DefWindowProc(GetHWND(), WM_NCLBUTTONDOWN, ht_component, |
- MAKELPARAM(point.x, point.y)); |
- SetMsgHandled(TRUE); |
- } |
- */ |
- break; |
- } |
-} |
- |
-LRESULT CustomFrameWindow::OnNCUAHDrawCaption(UINT msg, WPARAM w_param, |
- LPARAM l_param) { |
- // See comment in widget_win.h at the definition of WM_NCUAHDRAWCAPTION for |
- // an explanation about why we need to handle this message. |
- SetMsgHandled(TRUE); |
- return 0; |
-} |
- |
-LRESULT CustomFrameWindow::OnNCUAHDrawFrame(UINT msg, WPARAM w_param, |
- LPARAM l_param) { |
- // See comment in widget_win.h at the definition of WM_NCUAHDRAWCAPTION for |
- // an explanation about why we need to handle this message. |
- SetMsgHandled(TRUE); |
- return 0; |
-} |
- |
-LRESULT CustomFrameWindow::OnSetCursor(HWND window, UINT hittest_code, |
- UINT message) { |
- int index = RC_NORMAL; |
- switch (hittest_code) { |
- case HTTOP: |
- case HTBOTTOM: |
- index = RC_VERTICAL; |
- break; |
- case HTTOPLEFT: |
- case HTBOTTOMRIGHT: |
- index = RC_NWSE; |
- break; |
- case HTTOPRIGHT: |
- case HTBOTTOMLEFT: |
- index = RC_NESW; |
- break; |
- case HTLEFT: |
- case HTRIGHT: |
- index = RC_HORIZONTAL; |
- break; |
- case HTCAPTION: |
- case HTCLIENT: |
- index = RC_NORMAL; |
- break; |
- } |
- SetCursor(resize_cursors_[index]); |
- return 0; |
-} |
- |
-LRESULT CustomFrameWindow::OnSetIcon(UINT size_type, HICON new_icon) { |
- ScopedRedrawLock lock(this); |
- return DefWindowProc(GetHWND(), WM_SETICON, size_type, |
- reinterpret_cast<LPARAM>(new_icon)); |
-} |
- |
-LRESULT CustomFrameWindow::OnSetText(const wchar_t* text) { |
- ScopedRedrawLock lock(this); |
- return DefWindowProc(GetHWND(), WM_SETTEXT, NULL, |
- reinterpret_cast<LPARAM>(text)); |
-} |
- |
-void CustomFrameWindow::OnSize(UINT param, const CSize& size) { |
- Window::OnSize(param, size); |
- |
- // ResetWindowRegion is going to trigger WM_NCPAINT. By doing it after we've |
- // invoked OnSize we ensure the RootView has been layed out. |
- ResetWindowRegion(); |
-} |
- |
-void CustomFrameWindow::OnSysCommand(UINT notification_code, CPoint click) { |
- // Windows uses the 4 lower order bits of |notification_code| for type- |
- // specific information so we must exclude this when comparing. |
- static const int sc_mask = 0xFFF0; |
- if ((notification_code & sc_mask) == SC_MINIMIZE || |
- (notification_code & sc_mask) == SC_MAXIMIZE || |
- (notification_code & sc_mask) == SC_RESTORE) { |
- non_client_view_->ResetWindowControls(); |
- } else if ((notification_code & sc_mask) == SC_MOVE || |
- (notification_code & sc_mask) == SC_SIZE) { |
- if (lock_updates_) { |
- // We were locked, before entering a resize or move modal loop. Now that |
- // we've begun to move the window, we need to unlock updates so that the |
- // sizing/moving feedback can be continuous. |
- UnlockUpdates(); |
- } |
- } |
- Window::OnSysCommand(notification_code, click); |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////// |
-// CustomFrameWindow, private: |
- |
-// static |
-void CustomFrameWindow::InitClass() { |
- static bool initialized = false; |
- if (!initialized) { |
- resize_cursors_[RC_NORMAL] = LoadCursor(NULL, IDC_ARROW); |
- resize_cursors_[RC_VERTICAL] = LoadCursor(NULL, IDC_SIZENS); |
- resize_cursors_[RC_HORIZONTAL] = LoadCursor(NULL, IDC_SIZEWE); |
- resize_cursors_[RC_NESW] = LoadCursor(NULL, IDC_SIZENESW); |
- resize_cursors_[RC_NWSE] = LoadCursor(NULL, IDC_SIZENWSE); |
- initialized = true; |
- } |
-} |
- |
-void CustomFrameWindow::LockUpdates() { |
- lock_updates_ = true; |
- saved_window_style_ = GetWindowLong(GetHWND(), GWL_STYLE); |
- SetWindowLong(GetHWND(), GWL_STYLE, saved_window_style_ & ~WS_VISIBLE); |
-} |
- |
-void CustomFrameWindow::UnlockUpdates() { |
- SetWindowLong(GetHWND(), GWL_STYLE, saved_window_style_); |
- lock_updates_ = false; |
-} |
- |
-void CustomFrameWindow::ResetWindowRegion() { |
- // Changing the window region is going to force a paint. Only change the |
- // window region if the region really differs. |
- HRGN current_rgn = CreateRectRgn(0, 0, 0, 0); |
- int current_rgn_result = GetWindowRgn(GetHWND(), current_rgn); |
- |
- CRect window_rect; |
- GetWindowRect(&window_rect); |
- HRGN new_region; |
- if (IsMaximized()) { |
- HMONITOR monitor = MonitorFromWindow(GetHWND(), MONITOR_DEFAULTTONEAREST); |
- MONITORINFO mi; |
- mi.cbSize = sizeof mi; |
- GetMonitorInfo(monitor, &mi); |
- CRect work_rect = mi.rcWork; |
- work_rect.OffsetRect(-window_rect.left, -window_rect.top); |
- new_region = CreateRectRgnIndirect(&work_rect); |
- } else { |
- gfx::Path window_mask; |
- non_client_view_->GetWindowMask(gfx::Size(window_rect.Width(), |
- window_rect.Height()), |
- &window_mask); |
- new_region = window_mask.CreateHRGN(); |
- } |
- |
- if (current_rgn_result == ERROR || !EqualRgn(current_rgn, new_region)) { |
- // SetWindowRgn takes ownership of the HRGN created by CreateHRGN. |
- SetWindowRgn(new_region, TRUE); |
- } else { |
- DeleteObject(new_region); |
- } |
- |
- DeleteObject(current_rgn); |
-} |
- |
-void CustomFrameWindow::ProcessNCMousePress(const CPoint& point, int flags) { |
- CPoint temp = point; |
- MapWindowPoints(HWND_DESKTOP, GetHWND(), &temp, 1); |
- UINT message_flags = 0; |
- if ((GetKeyState(VK_CONTROL) & 0x80) == 0x80) |
- message_flags |= MK_CONTROL; |
- if ((GetKeyState(VK_SHIFT) & 0x80) == 0x80) |
- message_flags |= MK_SHIFT; |
- message_flags |= flags; |
- ProcessMousePressed(temp, message_flags, false, false); |
-} |
- |
-} // namespace views |