Index: ui/views/win/hwnd_message_handler.cc |
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc |
index d1760ab61ac2a5e6a1db0361d1ddb6ebbab2be30..2caafa78386ce1552489ab6d73763e3111332357 100644 |
--- a/ui/views/win/hwnd_message_handler.cc |
+++ b/ui/views/win/hwnd_message_handler.cc |
@@ -1,2057 +1,2058 @@ |
-// Copyright (c) 2012 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 "ui/views/win/hwnd_message_handler.h" |
- |
-#include <dwmapi.h> |
-#include <shellapi.h> |
- |
-#include "base/bind.h" |
-#include "base/debug/trace_event.h" |
-#include "base/win/windows_version.h" |
-#include "ui/base/events/event.h" |
-#include "ui/base/events/event_utils.h" |
-#include "ui/base/keycodes/keyboard_code_conversion_win.h" |
-#include "ui/base/win/hwnd_util.h" |
-#include "ui/base/win/mouse_wheel_util.h" |
-#include "ui/base/win/shell.h" |
-#include "ui/gfx/canvas.h" |
-#include "ui/gfx/canvas_skia_paint.h" |
-#include "ui/gfx/icon_util.h" |
-#include "ui/gfx/insets.h" |
-#include "ui/gfx/path.h" |
-#include "ui/gfx/path_win.h" |
-#include "ui/gfx/screen.h" |
-#include "ui/native_theme/native_theme_win.h" |
-#include "ui/views/views_delegate.h" |
-#include "ui/views/widget/monitor_win.h" |
-#include "ui/views/widget/native_widget_win.h" |
-#include "ui/views/widget/widget_hwnd_utils.h" |
-#include "ui/views/win/fullscreen_handler.h" |
-#include "ui/views/win/hwnd_message_handler_delegate.h" |
-#include "ui/views/win/scoped_fullscreen_visibility.h" |
- |
-#if !defined(USE_AURA) |
-#include "ui/views/accessibility/native_view_accessibility_win.h" |
-#include "ui/views/widget/child_window_message_processor.h" |
-#endif |
- |
-namespace views { |
-namespace { |
- |
-// MoveLoopMouseWatcher is used to determine if the user canceled or completed a |
-// move. win32 doesn't appear to offer a way to determine the result of a move, |
-// so we install hooks to determine if we got a mouse up and assume the move |
-// completed. |
-class MoveLoopMouseWatcher { |
- public: |
- explicit MoveLoopMouseWatcher(HWNDMessageHandler* host); |
- ~MoveLoopMouseWatcher(); |
- |
- // Returns true if the mouse is up, or if we couldn't install the hook. |
- bool got_mouse_up() const { return got_mouse_up_; } |
- |
- private: |
- // Instance that owns the hook. We only allow one instance to hook the mouse |
- // at a time. |
- static MoveLoopMouseWatcher* instance_; |
- |
- // Key and mouse callbacks from the hook. |
- static LRESULT CALLBACK MouseHook(int n_code, WPARAM w_param, LPARAM l_param); |
- static LRESULT CALLBACK KeyHook(int n_code, WPARAM w_param, LPARAM l_param); |
- |
- void Unhook(); |
- |
- // HWNDMessageHandler that created us. |
- HWNDMessageHandler* host_; |
- |
- // Did we get a mouse up? |
- bool got_mouse_up_; |
- |
- // Hook identifiers. |
- HHOOK mouse_hook_; |
- HHOOK key_hook_; |
- |
- DISALLOW_COPY_AND_ASSIGN(MoveLoopMouseWatcher); |
-}; |
- |
-// static |
-MoveLoopMouseWatcher* MoveLoopMouseWatcher::instance_ = NULL; |
- |
-MoveLoopMouseWatcher::MoveLoopMouseWatcher(HWNDMessageHandler* host) |
- : host_(host), |
- got_mouse_up_(false), |
- mouse_hook_(NULL), |
- key_hook_(NULL) { |
- // Only one instance can be active at a time. |
- if (instance_) |
- instance_->Unhook(); |
- |
- mouse_hook_ = SetWindowsHookEx( |
- WH_MOUSE, &MouseHook, NULL, GetCurrentThreadId()); |
- if (mouse_hook_) { |
- instance_ = this; |
- // We don't care if setting the key hook succeeded. |
- key_hook_ = SetWindowsHookEx( |
- WH_KEYBOARD, &KeyHook, NULL, GetCurrentThreadId()); |
- } |
- if (instance_ != this) { |
- // Failed installation. Assume we got a mouse up in this case, otherwise |
- // we'll think all drags were canceled. |
- got_mouse_up_ = true; |
- } |
-} |
- |
-MoveLoopMouseWatcher::~MoveLoopMouseWatcher() { |
- Unhook(); |
-} |
- |
-void MoveLoopMouseWatcher::Unhook() { |
- if (instance_ != this) |
- return; |
- |
- DCHECK(mouse_hook_); |
- UnhookWindowsHookEx(mouse_hook_); |
- if (key_hook_) |
- UnhookWindowsHookEx(key_hook_); |
- key_hook_ = NULL; |
- mouse_hook_ = NULL; |
- instance_ = NULL; |
-} |
- |
-// static |
-LRESULT CALLBACK MoveLoopMouseWatcher::MouseHook(int n_code, |
- WPARAM w_param, |
- LPARAM l_param) { |
- DCHECK(instance_); |
- if (n_code == HC_ACTION && w_param == WM_LBUTTONUP) |
- instance_->got_mouse_up_ = true; |
- return CallNextHookEx(instance_->mouse_hook_, n_code, w_param, l_param); |
-} |
- |
-// static |
-LRESULT CALLBACK MoveLoopMouseWatcher::KeyHook(int n_code, |
- WPARAM w_param, |
- LPARAM l_param) { |
- if (n_code == HC_ACTION && w_param == VK_ESCAPE) { |
- if (base::win::GetVersion() >= base::win::VERSION_VISTA) { |
- int value = TRUE; |
- HRESULT result = DwmSetWindowAttribute( |
- instance_->host_->hwnd(), |
- DWMWA_TRANSITIONS_FORCEDISABLED, |
- &value, |
- sizeof(value)); |
- } |
- // Hide the window on escape, otherwise the window is visibly going to snap |
- // back to the original location before we close it. |
- // This behavior is specific to tab dragging, in that we generally wouldn't |
- // want this functionality if we have other consumers using this API. |
- instance_->host_->Hide(); |
- } |
- return CallNextHookEx(instance_->key_hook_, n_code, w_param, l_param); |
-} |
- |
-// Called from OnNCActivate. |
-BOOL CALLBACK EnumChildWindowsForRedraw(HWND hwnd, LPARAM lparam) { |
- DWORD process_id; |
- GetWindowThreadProcessId(hwnd, &process_id); |
- int flags = RDW_INVALIDATE | RDW_NOCHILDREN | RDW_FRAME; |
- if (process_id == GetCurrentProcessId()) |
- flags |= RDW_UPDATENOW; |
- RedrawWindow(hwnd, NULL, NULL, flags); |
- return TRUE; |
-} |
- |
-bool GetMonitorAndRects(const RECT& rect, |
- HMONITOR* monitor, |
- gfx::Rect* monitor_rect, |
- gfx::Rect* work_area) { |
- DCHECK(monitor); |
- DCHECK(monitor_rect); |
- DCHECK(work_area); |
- *monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTONULL); |
- if (!*monitor) |
- return false; |
- MONITORINFO monitor_info = { 0 }; |
- monitor_info.cbSize = sizeof(monitor_info); |
- GetMonitorInfo(*monitor, &monitor_info); |
- *monitor_rect = gfx::Rect(monitor_info.rcMonitor); |
- *work_area = gfx::Rect(monitor_info.rcWork); |
- return true; |
-} |
- |
-struct FindOwnedWindowsData { |
- HWND window; |
- std::vector<Widget*> owned_widgets; |
-}; |
- |
-BOOL CALLBACK FindOwnedWindowsCallback(HWND hwnd, LPARAM param) { |
- // TODO(beng): resolve wrt aura. |
-#if !defined(USE_AURA) |
- FindOwnedWindowsData* data = reinterpret_cast<FindOwnedWindowsData*>(param); |
- if (GetWindow(hwnd, GW_OWNER) == data->window) { |
- Widget* widget = Widget::GetWidgetForNativeView(hwnd); |
- if (widget) |
- data->owned_widgets.push_back(widget); |
- } |
-#endif |
- return TRUE; |
-} |
- |
-// Enables or disables the menu item for the specified command and menu. |
-void EnableMenuItemByCommand(HMENU menu, UINT command, bool enabled) { |
- UINT flags = MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_DISABLED | MF_GRAYED); |
- EnableMenuItem(menu, command, flags); |
-} |
- |
-// Callback used to notify child windows that the top level window received a |
-// DWMCompositionChanged message. |
-BOOL CALLBACK SendDwmCompositionChanged(HWND window, LPARAM param) { |
- SendMessage(window, WM_DWMCOMPOSITIONCHANGED, 0, 0); |
- return TRUE; |
-} |
- |
-// See comments in OnNCPaint() for details of this struct. |
-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; |
-} |
- |
-#if !defined(USE_AURA) |
- |
-// Get the source HWND of the specified message. Depending on the message, the |
-// source HWND is encoded in either the WPARAM or the LPARAM value. |
-HWND GetControlHWNDForMessage(UINT message, WPARAM w_param, LPARAM l_param) { |
- // Each of the following messages can be sent by a child HWND and must be |
- // forwarded to its associated NativeControlWin for handling. |
- switch (message) { |
- case WM_NOTIFY: |
- return reinterpret_cast<NMHDR*>(l_param)->hwndFrom; |
- case WM_COMMAND: |
- return reinterpret_cast<HWND>(l_param); |
- case WM_CONTEXTMENU: |
- return reinterpret_cast<HWND>(w_param); |
- case WM_CTLCOLORBTN: |
- case WM_CTLCOLORSTATIC: |
- return reinterpret_cast<HWND>(l_param); |
- } |
- return NULL; |
-} |
- |
-// Some messages may be sent to us by a child HWND. If this is the case, this |
-// function will forward those messages on to the object associated with the |
-// source HWND and return true, in which case the window procedure must not do |
-// any further processing of the message. If there is no associated |
-// ChildWindowMessageProcessor, the return value will be false and the WndProc |
-// can continue processing the message normally. |l_result| contains the result |
-// of the message processing by the control and must be returned by the WndProc |
-// if the return value is true. |
-bool ProcessChildWindowMessage(UINT message, |
- WPARAM w_param, |
- LPARAM l_param, |
- LRESULT* l_result) { |
- *l_result = 0; |
- |
- HWND control_hwnd = GetControlHWNDForMessage(message, w_param, l_param); |
- if (IsWindow(control_hwnd)) { |
- ChildWindowMessageProcessor* processor = |
- ChildWindowMessageProcessor::Get(control_hwnd); |
- if (processor) |
- return processor->ProcessMessage(message, w_param, l_param, l_result); |
- } |
- |
- return false; |
-} |
- |
-#endif |
- |
-// The thickness of an auto-hide taskbar in pixels. |
-const int kAutoHideTaskbarThicknessPx = 2; |
- |
-// The touch id to be used for touch events coming in from Windows Aura |
-// Desktop. |
-const int kDesktopChromeAuraTouchId = 9; |
- |
-} // namespace |
- |
-// 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 Window's lock and unlock functions as it is created and destroyed. |
-// See documentation in those methods for the technique used. |
-// |
-// The lock only has an effect if the window was visible upon lock creation, as |
-// it doesn't guard against direct visiblility changes, and multiple locks may |
-// exist simultaneously to handle certain nested Windows messages. |
-// |
-// 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 HWNDMessageHandler::ScopedRedrawLock { |
- public: |
- explicit ScopedRedrawLock(HWNDMessageHandler* owner) |
- : owner_(owner), |
- hwnd_(owner_->hwnd()), |
- was_visible_(owner_->IsVisible()), |
- cancel_unlock_(false), |
- force_(!(GetWindowLong(hwnd_, GWL_STYLE) & WS_CAPTION)) { |
- if (was_visible_ && ::IsWindow(hwnd_)) |
- owner_->LockUpdates(force_); |
- } |
- |
- ~ScopedRedrawLock() { |
- if (!cancel_unlock_ && was_visible_ && ::IsWindow(hwnd_)) |
- owner_->UnlockUpdates(force_); |
- } |
- |
- // Cancel the unlock operation, call this if the Widget is being destroyed. |
- void CancelUnlockOperation() { cancel_unlock_ = true; } |
- |
- private: |
- // The owner having its style changed. |
- HWNDMessageHandler* owner_; |
- // The owner's HWND, cached to avoid action after window destruction. |
- HWND hwnd_; |
- // Records the HWND visibility at the time of creation. |
- bool was_visible_; |
- // A flag indicating that the unlock operation was canceled. |
- bool cancel_unlock_; |
- // If true, perform the redraw lock regardless of Aero state. |
- bool force_; |
- |
- DISALLOW_COPY_AND_ASSIGN(ScopedRedrawLock); |
-}; |
- |
-//////////////////////////////////////////////////////////////////////////////// |
-// HWNDMessageHandler, public: |
- |
-HWNDMessageHandler::HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate) |
- : delegate_(delegate), |
- ALLOW_THIS_IN_INITIALIZER_LIST( |
- fullscreen_handler_(new FullscreenHandler)), |
- ALLOW_THIS_IN_INITIALIZER_LIST(close_widget_factory_(this)), |
- remove_standard_frame_(false), |
- use_system_default_icon_(false), |
- restore_focus_when_enabled_(false), |
- restored_enabled_(false), |
- previous_cursor_(NULL), |
- active_mouse_tracking_flags_(0), |
- is_right_mouse_pressed_on_caption_(false), |
- lock_updates_count_(0), |
- destroyed_(NULL), |
- ignore_window_pos_changes_(false), |
- ALLOW_THIS_IN_INITIALIZER_LIST(ignore_pos_changes_factory_(this)), |
- last_monitor_(NULL), |
- use_layered_buffer_(false), |
- layered_alpha_(255), |
- ALLOW_THIS_IN_INITIALIZER_LIST(paint_layered_window_factory_(this)), |
- can_update_layered_window_(true), |
- is_first_nccalc_(true) { |
-} |
- |
-HWNDMessageHandler::~HWNDMessageHandler() { |
- delegate_ = NULL; |
- if (destroyed_ != NULL) |
- *destroyed_ = true; |
- // Prevent calls back into this class via WNDPROC now that we've been |
- // destroyed. |
- ClearUserData(); |
-} |
- |
-void HWNDMessageHandler::Init(HWND parent, const gfx::Rect& bounds) { |
- TRACE_EVENT0("views", "HWNDMessageHandler::Init"); |
- GetMonitorAndRects(bounds.ToRECT(), &last_monitor_, &last_monitor_rect_, |
- &last_work_area_); |
- |
- // Create the window. |
- WindowImpl::Init(parent, bounds); |
-} |
- |
-void HWNDMessageHandler::InitModalType(ui::ModalType modal_type) { |
- if (modal_type == ui::MODAL_TYPE_NONE) |
- return; |
- // We implement modality by crawling up the hierarchy of windows starting |
- // at the owner, disabling all of them so that they don't receive input |
- // messages. |
- HWND start = ::GetWindow(hwnd(), GW_OWNER); |
- while (start) { |
- ::EnableWindow(start, FALSE); |
- start = ::GetParent(start); |
- } |
-} |
- |
-void HWNDMessageHandler::Close() { |
- if (!IsWindow(hwnd())) |
- return; // No need to do anything. |
- |
- // Let's hide ourselves right away. |
- Hide(); |
- |
- // Modal dialog windows disable their owner windows; re-enable them now so |
- // they can activate as foreground windows upon this window's destruction. |
- RestoreEnabledIfNecessary(); |
- |
- if (!close_widget_factory_.HasWeakPtrs()) { |
- // And we delay the close so that if we are called from an ATL callback, |
- // we don't destroy the window before the callback returned (as the caller |
- // may delete ourselves on destroy and the ATL callback would still |
- // dereference us when the callback returns). |
- MessageLoop::current()->PostTask( |
- FROM_HERE, |
- base::Bind(&HWNDMessageHandler::CloseNow, |
- close_widget_factory_.GetWeakPtr())); |
- } |
-} |
- |
-void HWNDMessageHandler::CloseNow() { |
- // We may already have been destroyed if the selection resulted in a tab |
- // switch which will have reactivated the browser window and closed us, so |
- // we need to check to see if we're still a window before trying to destroy |
- // ourself. |
- if (IsWindow(hwnd())) |
- DestroyWindow(hwnd()); |
-} |
- |
-gfx::Rect HWNDMessageHandler::GetWindowBoundsInScreen() const { |
- RECT r; |
- GetWindowRect(hwnd(), &r); |
- return gfx::Rect(r); |
-} |
- |
-gfx::Rect HWNDMessageHandler::GetClientAreaBoundsInScreen() const { |
- RECT r; |
- GetClientRect(hwnd(), &r); |
- POINT point = { r.left, r.top }; |
- ClientToScreen(hwnd(), &point); |
- return gfx::Rect(point.x, point.y, r.right - r.left, r.bottom - r.top); |
-} |
- |
-gfx::Rect HWNDMessageHandler::GetRestoredBounds() const { |
- // If we're in fullscreen mode, we've changed the normal bounds to the monitor |
- // rect, so return the saved bounds instead. |
- if (fullscreen_handler_->fullscreen()) |
- return fullscreen_handler_->GetRestoreBounds(); |
- |
- gfx::Rect bounds; |
- GetWindowPlacement(&bounds, NULL); |
- return bounds; |
-} |
- |
-void HWNDMessageHandler::GetWindowPlacement( |
- gfx::Rect* bounds, |
- ui::WindowShowState* show_state) const { |
- WINDOWPLACEMENT wp; |
- wp.length = sizeof(wp); |
- const bool succeeded = !!::GetWindowPlacement(hwnd(), &wp); |
- DCHECK(succeeded); |
- |
- if (bounds != NULL) { |
- if (wp.showCmd == SW_SHOWNORMAL) { |
- // GetWindowPlacement can return misleading position if a normalized |
- // window was resized using Aero Snap feature (see comment 9 in bug |
- // 36421). As a workaround, using GetWindowRect for normalized windows. |
- const bool succeeded = GetWindowRect(hwnd(), &wp.rcNormalPosition) != 0; |
- DCHECK(succeeded); |
- |
- *bounds = gfx::Rect(wp.rcNormalPosition); |
- } else { |
- MONITORINFO mi; |
- mi.cbSize = sizeof(mi); |
- const bool succeeded = GetMonitorInfo( |
- MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONEAREST), &mi) != 0; |
- DCHECK(succeeded); |
- |
- *bounds = gfx::Rect(wp.rcNormalPosition); |
- // Convert normal position from workarea coordinates to screen |
- // coordinates. |
- bounds->Offset(mi.rcWork.left - mi.rcMonitor.left, |
- mi.rcWork.top - mi.rcMonitor.top); |
- } |
- } |
- |
- if (show_state) { |
- if (wp.showCmd == SW_SHOWMAXIMIZED) |
- *show_state = ui::SHOW_STATE_MAXIMIZED; |
- else if (wp.showCmd == SW_SHOWMINIMIZED) |
- *show_state = ui::SHOW_STATE_MINIMIZED; |
- else |
- *show_state = ui::SHOW_STATE_NORMAL; |
- } |
-} |
- |
-void HWNDMessageHandler::SetBounds(const gfx::Rect& bounds) { |
- LONG style = GetWindowLong(hwnd(), GWL_STYLE); |
- if (style & WS_MAXIMIZE) |
- SetWindowLong(hwnd(), GWL_STYLE, style & ~WS_MAXIMIZE); |
- SetWindowPos(hwnd(), NULL, bounds.x(), bounds.y(), bounds.width(), |
- bounds.height(), SWP_NOACTIVATE | SWP_NOZORDER); |
-} |
- |
-void HWNDMessageHandler::SetSize(const gfx::Size& size) { |
- SetWindowPos(hwnd(), NULL, 0, 0, size.width(), size.height(), |
- SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE); |
-} |
- |
-void HWNDMessageHandler::CenterWindow(const gfx::Size& size) { |
- HWND parent = GetParent(hwnd()); |
- if (!IsWindow(hwnd())) |
- parent = ::GetWindow(hwnd(), GW_OWNER); |
- ui::CenterAndSizeWindow(parent, hwnd(), size); |
-} |
- |
-void HWNDMessageHandler::SetRegion(HRGN region) { |
- SetWindowRgn(hwnd(), region, TRUE); |
-} |
- |
-void HWNDMessageHandler::StackAbove(HWND other_hwnd) { |
- SetWindowPos(hwnd(), other_hwnd, 0, 0, 0, 0, |
- SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); |
-} |
- |
-void HWNDMessageHandler::StackAtTop() { |
- SetWindowPos(hwnd(), HWND_TOP, 0, 0, 0, 0, |
- SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); |
-} |
- |
-void HWNDMessageHandler::Show() { |
- if (IsWindow(hwnd())) |
- ShowWindowWithState(ui::SHOW_STATE_INACTIVE); |
-} |
- |
-void HWNDMessageHandler::ShowWindowWithState(ui::WindowShowState show_state) { |
- TRACE_EVENT0("views", "HWNDMessageHandler::ShowWindowWithState"); |
- DWORD native_show_state; |
- switch (show_state) { |
- case ui::SHOW_STATE_INACTIVE: |
- native_show_state = SW_SHOWNOACTIVATE; |
- break; |
- case ui::SHOW_STATE_MAXIMIZED: |
- native_show_state = SW_SHOWMAXIMIZED; |
- break; |
- case ui::SHOW_STATE_MINIMIZED: |
- native_show_state = SW_SHOWMINIMIZED; |
- break; |
- default: |
- native_show_state = delegate_->GetInitialShowState(); |
- break; |
- } |
- Show(native_show_state); |
-} |
- |
-void HWNDMessageHandler::Show(int show_state) { |
- ShowWindow(hwnd(), show_state); |
- // When launched from certain programs like bash and Windows Live Messenger, |
- // show_state is set to SW_HIDE, so we need to correct that condition. We |
- // don't just change show_state to SW_SHOWNORMAL because MSDN says we must |
- // always first call ShowWindow with the specified value from STARTUPINFO, |
- // otherwise all future ShowWindow calls will be ignored (!!#@@#!). Instead, |
- // we call ShowWindow again in this case. |
- if (show_state == SW_HIDE) { |
- show_state = SW_SHOWNORMAL; |
- ShowWindow(hwnd(), show_state); |
- } |
- |
- // We need to explicitly activate the window if we've been shown with a state |
- // that should activate, because if we're opened from a desktop shortcut while |
- // an existing window is already running it doesn't seem to be enough to use |
- // one of these flags to activate the window. |
- if (show_state == SW_SHOWNORMAL || show_state == SW_SHOWMAXIMIZED) |
- Activate(); |
- |
- if (!delegate_->HandleInitialFocus()) |
- SetInitialFocus(); |
-} |
- |
-void HWNDMessageHandler::ShowMaximizedWithBounds(const gfx::Rect& bounds) { |
- WINDOWPLACEMENT placement = { 0 }; |
- placement.length = sizeof(WINDOWPLACEMENT); |
- placement.showCmd = SW_SHOWMAXIMIZED; |
- placement.rcNormalPosition = bounds.ToRECT(); |
- SetWindowPlacement(hwnd(), &placement); |
-} |
- |
-void HWNDMessageHandler::Hide() { |
- if (IsWindow(hwnd())) { |
- // NOTE: Be careful not to activate any windows here (for example, calling |
- // ShowWindow(SW_HIDE) will automatically activate another window). This |
- // code can be called while a window is being deactivated, and activating |
- // another window will screw up the activation that is already in progress. |
- SetWindowPos(hwnd(), NULL, 0, 0, 0, 0, |
- SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | |
- SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER); |
- |
- if (!GetParent(hwnd())) |
- NotifyOwnedWindowsParentClosing(); |
- } |
-} |
- |
-void HWNDMessageHandler::Maximize() { |
- ExecuteSystemMenuCommand(SC_MAXIMIZE); |
-} |
- |
-void HWNDMessageHandler::Minimize() { |
- ExecuteSystemMenuCommand(SC_MINIMIZE); |
- delegate_->HandleNativeBlur(NULL); |
-} |
- |
-void HWNDMessageHandler::Restore() { |
- ExecuteSystemMenuCommand(SC_RESTORE); |
-} |
- |
-void HWNDMessageHandler::Activate() { |
- if (IsMinimized()) |
- ::ShowWindow(hwnd(), SW_RESTORE); |
- ::SetWindowPos(hwnd(), HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); |
- SetForegroundWindow(hwnd()); |
-} |
- |
-void HWNDMessageHandler::Deactivate() { |
- HWND next_hwnd = ::GetNextWindow(hwnd(), GW_HWNDNEXT); |
- if (next_hwnd) |
- ::SetForegroundWindow(next_hwnd); |
-} |
- |
-void HWNDMessageHandler::SetAlwaysOnTop(bool on_top) { |
- ::SetWindowPos(hwnd(), on_top ? HWND_TOPMOST : HWND_NOTOPMOST, |
- 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); |
-} |
- |
-bool HWNDMessageHandler::IsVisible() const { |
- return !!::IsWindowVisible(hwnd()); |
-} |
- |
-bool HWNDMessageHandler::IsActive() const { |
- return GetActiveWindow() == hwnd(); |
-} |
- |
-bool HWNDMessageHandler::IsMinimized() const { |
- return !!::IsIconic(hwnd()); |
-} |
- |
-bool HWNDMessageHandler::IsMaximized() const { |
- return !!::IsZoomed(hwnd()); |
-} |
- |
-bool HWNDMessageHandler::RunMoveLoop(const gfx::Vector2d& drag_offset) { |
- ReleaseCapture(); |
- MoveLoopMouseWatcher watcher(this); |
- SendMessage(hwnd(), WM_SYSCOMMAND, SC_MOVE | 0x0002, GetMessagePos()); |
- // Windows doesn't appear to offer a way to determine whether the user |
- // canceled the move or not. We assume if the user released the mouse it was |
- // successful. |
- return watcher.got_mouse_up(); |
-} |
- |
-void HWNDMessageHandler::EndMoveLoop() { |
- SendMessage(hwnd(), WM_CANCELMODE, 0, 0); |
-} |
- |
-void HWNDMessageHandler::SendFrameChanged() { |
- SetWindowPos(hwnd(), NULL, 0, 0, 0, 0, |
- SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOCOPYBITS | |
- SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOREPOSITION | |
- SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER); |
-} |
- |
-void HWNDMessageHandler::FlashFrame(bool flash) { |
- FLASHWINFO fwi; |
- fwi.cbSize = sizeof(fwi); |
- fwi.hwnd = hwnd(); |
- if (flash) { |
- fwi.dwFlags = FLASHW_ALL; |
- fwi.uCount = 4; |
- fwi.dwTimeout = 0; |
- } else { |
- fwi.dwFlags = FLASHW_STOP; |
- } |
- FlashWindowEx(&fwi); |
-} |
- |
-void HWNDMessageHandler::ClearNativeFocus() { |
- ::SetFocus(hwnd()); |
-} |
- |
-void HWNDMessageHandler::SetCapture() { |
- DCHECK(!HasCapture()); |
- ::SetCapture(hwnd()); |
-} |
- |
-void HWNDMessageHandler::ReleaseCapture() { |
- ::ReleaseCapture(); |
-} |
- |
-bool HWNDMessageHandler::HasCapture() const { |
- return ::GetCapture() == hwnd(); |
-} |
- |
-void HWNDMessageHandler::SetVisibilityChangedAnimationsEnabled(bool enabled) { |
- if (base::win::GetVersion() >= base::win::VERSION_VISTA) { |
- int dwm_value = enabled ? FALSE : TRUE; |
- DwmSetWindowAttribute( |
- hwnd(), DWMWA_TRANSITIONS_FORCEDISABLED, &dwm_value, sizeof(dwm_value)); |
- } |
-} |
- |
-void HWNDMessageHandler::SetTitle(const string16& title) { |
- SetWindowText(hwnd(), title.c_str()); |
-} |
- |
-void HWNDMessageHandler::SetCursor(HCURSOR cursor) { |
- if (cursor) { |
- previous_cursor_ = ::SetCursor(cursor); |
- } else if (previous_cursor_) { |
- ::SetCursor(previous_cursor_); |
- previous_cursor_ = NULL; |
- } |
-} |
- |
-void HWNDMessageHandler::FrameTypeChanged() { |
- // Called when the frame type could possibly be changing (theme change or |
- // DWM composition change). |
- if (base::win::GetVersion() >= base::win::VERSION_VISTA) { |
- // We need to toggle the rendering policy of the DWM/glass frame as we |
- // change from opaque to glass. "Non client rendering enabled" means that |
- // the DWM's glass non-client rendering is enabled, which is why |
- // DWMNCRP_ENABLED is used for the native frame case. _DISABLED means the |
- // DWM doesn't render glass, and so is used in the custom frame case. |
- DWMNCRENDERINGPOLICY policy = !delegate_->IsUsingCustomFrame() ? |
- DWMNCRP_ENABLED : DWMNCRP_DISABLED; |
- DwmSetWindowAttribute(hwnd(), DWMWA_NCRENDERING_POLICY, |
- &policy, sizeof(DWMNCRENDERINGPOLICY)); |
- } |
- |
- ResetWindowRegion(true); |
- |
- // The non-client view needs to update too. |
- delegate_->HandleFrameChanged(); |
- |
- // WM_DWMCOMPOSITIONCHANGED is only sent to top level windows, however we want |
- // to notify our children too, since we can have MDI child windows who need to |
- // update their appearance. |
- EnumChildWindows(hwnd(), &SendDwmCompositionChanged, NULL); |
-} |
- |
-void HWNDMessageHandler::SchedulePaintInRect(const gfx::Rect& rect) { |
- if (use_layered_buffer_) { |
- // We must update the back-buffer immediately, since Windows' handling of |
- // invalid rects is somewhat mysterious. |
- invalid_rect_.Union(rect); |
- |
- // In some situations, such as drag and drop, when Windows itself runs a |
- // nested message loop our message loop appears to be starved and we don't |
- // receive calls to DidProcessMessage(). This only seems to affect layered |
- // windows, so we schedule a redraw manually using a task, since those never |
- // seem to be starved. Also, wtf. |
- if (!paint_layered_window_factory_.HasWeakPtrs()) { |
- MessageLoop::current()->PostTask( |
- FROM_HERE, |
- base::Bind(&HWNDMessageHandler::RedrawLayeredWindowContents, |
- paint_layered_window_factory_.GetWeakPtr())); |
- } |
- } else { |
- // InvalidateRect() expects client coordinates. |
- RECT r = rect.ToRECT(); |
- InvalidateRect(hwnd(), &r, FALSE); |
- } |
-} |
- |
-void HWNDMessageHandler::SetOpacity(BYTE opacity) { |
- layered_alpha_ = opacity; |
-} |
- |
-void HWNDMessageHandler::SetWindowIcons(const gfx::ImageSkia& window_icon, |
- const gfx::ImageSkia& app_icon) { |
- if (!window_icon.isNull()) { |
- HICON windows_icon = IconUtil::CreateHICONFromSkBitmap( |
- *window_icon.bitmap()); |
- // We need to make sure to destroy the previous icon, otherwise we'll leak |
- // these GDI objects until we crash! |
- HICON old_icon = reinterpret_cast<HICON>( |
- SendMessage(hwnd(), WM_SETICON, ICON_SMALL, |
- reinterpret_cast<LPARAM>(windows_icon))); |
- if (old_icon) |
- DestroyIcon(old_icon); |
- } |
- if (!app_icon.isNull()) { |
- HICON windows_icon = IconUtil::CreateHICONFromSkBitmap(*app_icon.bitmap()); |
- HICON old_icon = reinterpret_cast<HICON>( |
- SendMessage(hwnd(), WM_SETICON, ICON_BIG, |
- reinterpret_cast<LPARAM>(windows_icon))); |
- if (old_icon) |
- DestroyIcon(old_icon); |
- } |
-} |
- |
-//////////////////////////////////////////////////////////////////////////////// |
-// HWNDMessageHandler, InputMethodDelegate implementation: |
- |
-void HWNDMessageHandler::DispatchKeyEventPostIME(const ui::KeyEvent& key) { |
- SetMsgHandled(delegate_->HandleKeyEvent(key)); |
-} |
- |
-//////////////////////////////////////////////////////////////////////////////// |
-// HWNDMessageHandler, ui::WindowImpl overrides: |
- |
-HICON HWNDMessageHandler::GetDefaultWindowIcon() const { |
- if (use_system_default_icon_) |
- return NULL; |
- return ViewsDelegate::views_delegate ? |
- ViewsDelegate::views_delegate->GetDefaultWindowIcon() : NULL; |
-} |
- |
-LRESULT HWNDMessageHandler::OnWndProc(UINT message, |
- WPARAM w_param, |
- LPARAM l_param) { |
- HWND window = hwnd(); |
- LRESULT result = 0; |
- |
- if (delegate_ && delegate_->PreHandleMSG(message, w_param, l_param, &result)) |
- return result; |
- |
-#if !defined(USE_AURA) |
- // First allow messages sent by child controls to be processed directly by |
- // their associated views. If such a view is present, it will handle the |
- // message *instead of* this NativeWidgetWin. |
- if (ProcessChildWindowMessage(message, w_param, l_param, &result)) |
- return result; |
-#endif |
- |
- // Otherwise we handle everything else. |
- if (!ProcessWindowMessage(window, message, w_param, l_param, result)) |
- result = DefWindowProc(window, message, w_param, l_param); |
- |
- // DefWindowProc() may have destroyed the window in a nested message loop. |
- if (!::IsWindow(window)) |
- return result; |
- |
- if (delegate_) |
- delegate_->PostHandleMSG(message, w_param, l_param); |
- if (message == WM_NCDESTROY) { |
- MessageLoopForUI::current()->RemoveObserver(this); |
- if (delegate_) |
- delegate_->HandleDestroyed(); |
- } |
- |
- // Only top level widget should store/restore focus. |
- if (message == WM_ACTIVATE && delegate_->CanSaveFocus()) |
- PostProcessActivateMessage(LOWORD(w_param)); |
- if (message == WM_ENABLE && restore_focus_when_enabled_) { |
- // This path should be executed only for top level as |
- // restore_focus_when_enabled_ is set in PostProcessActivateMessage. |
- DCHECK(delegate_->CanSaveFocus()); |
- restore_focus_when_enabled_ = false; |
- delegate_->RestoreFocusOnEnable(); |
- } |
- return result; |
-} |
- |
-//////////////////////////////////////////////////////////////////////////////// |
-// HWNDMessageHandler, MessageLoopForUI::Observer implementation: |
- |
-base::EventStatus HWNDMessageHandler::WillProcessEvent( |
- const base::NativeEvent& event) { |
- return base::EVENT_CONTINUE; |
-} |
- |
-void HWNDMessageHandler::DidProcessEvent(const base::NativeEvent& event) { |
- RedrawInvalidRect(); |
-} |
- |
-//////////////////////////////////////////////////////////////////////////////// |
-// HWNDMessageHandler, private: |
- |
-void HWNDMessageHandler::SetInitialFocus() { |
- if (!(GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_TRANSPARENT) && |
- !(GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)) { |
- // The window does not get keyboard messages unless we focus it. |
- SetFocus(hwnd()); |
- } |
-} |
- |
-void HWNDMessageHandler::PostProcessActivateMessage(int activation_state) { |
- DCHECK(delegate_->CanSaveFocus()); |
- if (WA_INACTIVE == activation_state) { |
- // We might get activated/inactivated without being enabled, so we need to |
- // clear restore_focus_when_enabled_. |
- restore_focus_when_enabled_ = false; |
- delegate_->SaveFocusOnDeactivate(); |
- } else { |
- // We must restore the focus after the message has been DefProc'ed as it |
- // does set the focus to the last focused HWND. |
- // Note that if the window is not enabled, we cannot restore the focus as |
- // calling ::SetFocus on a child of the non-enabled top-window would fail. |
- // This is the case when showing a modal dialog (such as 'open file', |
- // 'print'...) from a different thread. |
- // In that case we delay the focus restoration to when the window is enabled |
- // again. |
- if (!IsWindowEnabled(hwnd())) { |
- DCHECK(!restore_focus_when_enabled_); |
- restore_focus_when_enabled_ = true; |
- return; |
- } |
- delegate_->RestoreFocusOnActivate(); |
- } |
-} |
- |
-void HWNDMessageHandler::RestoreEnabledIfNecessary() { |
- if (delegate_->IsModal() && !restored_enabled_) { |
- restored_enabled_ = true; |
- // If we were run modally, we need to undo the disabled-ness we inflicted on |
- // the owner's parent hierarchy. |
- HWND start = ::GetWindow(hwnd(), GW_OWNER); |
- while (start) { |
- ::EnableWindow(start, TRUE); |
- start = ::GetParent(start); |
- } |
- } |
-} |
- |
-void HWNDMessageHandler::ExecuteSystemMenuCommand(int command) { |
- if (command) |
- SendMessage(hwnd(), WM_SYSCOMMAND, command, 0); |
-} |
- |
-void HWNDMessageHandler::TrackMouseEvents(DWORD mouse_tracking_flags) { |
- // Begin tracking mouse events for this HWND so that we get WM_MOUSELEAVE |
- // when the user moves the mouse outside this HWND's bounds. |
- if (active_mouse_tracking_flags_ == 0 || mouse_tracking_flags & TME_CANCEL) { |
- if (mouse_tracking_flags & TME_CANCEL) { |
- // We're about to cancel active mouse tracking, so empty out the stored |
- // state. |
- active_mouse_tracking_flags_ = 0; |
- } else { |
- active_mouse_tracking_flags_ = mouse_tracking_flags; |
- } |
- |
- TRACKMOUSEEVENT tme; |
- tme.cbSize = sizeof(tme); |
- tme.dwFlags = mouse_tracking_flags; |
- tme.hwndTrack = hwnd(); |
- tme.dwHoverTime = 0; |
- TrackMouseEvent(&tme); |
- } else if (mouse_tracking_flags != active_mouse_tracking_flags_) { |
- TrackMouseEvents(active_mouse_tracking_flags_ | TME_CANCEL); |
- TrackMouseEvents(mouse_tracking_flags); |
- } |
-} |
- |
-void HWNDMessageHandler::ClientAreaSizeChanged() { |
- RECT r = {0, 0, 0, 0}; |
- if (delegate_->WidgetSizeIsClientSize()) { |
- // TODO(beng): investigate whether this could be done |
- // from other branch of if-else. |
- if (!IsMinimized()) |
- GetClientRect(hwnd(), &r); |
- } else { |
- GetWindowRect(hwnd(), &r); |
- } |
- gfx::Size s(std::max(0, static_cast<int>(r.right - r.left)), |
- std::max(0, static_cast<int>(r.bottom - r.top))); |
- delegate_->HandleClientSizeChanged(s); |
- if (use_layered_buffer_) { |
- layered_window_contents_.reset( |
- new gfx::Canvas(s, ui::SCALE_FACTOR_100P, false)); |
- } |
-} |
- |
-gfx::Insets HWNDMessageHandler::GetClientAreaInsets() const { |
- gfx::Insets insets; |
- if (delegate_->GetClientAreaInsets(&insets)) |
- return insets; |
- DCHECK(insets.empty()); |
- |
- // Returning an empty Insets object causes the default handling in |
- // NativeWidgetWin::OnNCCalcSize() to be invoked. |
- if (!delegate_->IsWidgetWindow() || |
- (!delegate_->IsUsingCustomFrame() && !remove_standard_frame_)) { |
- return insets; |
- } |
- |
- if (IsMaximized()) { |
- // Windows automatically adds a standard width border to all sides when a |
- // window is maximized. |
- int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME); |
- return gfx::Insets(border_thickness, border_thickness, border_thickness, |
- border_thickness); |
- } |
- |
- // The hack below doesn't seem to be necessary when the standard frame is |
- // removed. |
- if (remove_standard_frame_) |
- return insets; |
- // This is weird, but highly essential. If we don't offset the bottom edge |
- // of the client rect, the window client area and window area will match, |
- // and when returning to glass rendering mode from non-glass, the client |
- // area will not paint black as transparent. This is because (and I don't |
- // know why) the client area goes from matching the window rect to being |
- // something else. If the client area is not the window rect in both |
- // modes, the blackness doesn't occur. Because of this, we need to tell |
- // the RootView to lay out to fit the window rect, rather than the client |
- // rect when using the opaque frame. |
- // Note: this is only required for non-fullscreen windows. Note that |
- // fullscreen windows are in restored state, not maximized. |
- return gfx::Insets(0, 0, fullscreen_handler_->fullscreen() ? 0 : 1, 0); |
-} |
- |
-void HWNDMessageHandler::ResetWindowRegion(bool force) { |
- // A native frame uses the native window region, and we don't want to mess |
- // with it. |
- if (!delegate_->IsUsingCustomFrame() || !delegate_->IsWidgetWindow()) { |
- if (force) |
- SetWindowRgn(hwnd(), NULL, TRUE); |
- return; |
- } |
- |
- // 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(hwnd(), current_rgn); |
- |
- CRect window_rect; |
- GetWindowRect(hwnd(), &window_rect); |
- HRGN new_region; |
- if (IsMaximized()) { |
- HMONITOR monitor = MonitorFromWindow(hwnd(), 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; |
- delegate_->GetWindowMask( |
- gfx::Size(window_rect.Width(), window_rect.Height()), &window_mask); |
- new_region = gfx::CreateHRGNFromSkPath(window_mask); |
- } |
- |
- if (current_rgn_result == ERROR || !EqualRgn(current_rgn, new_region)) { |
- // SetWindowRgn takes ownership of the HRGN created by CreateNativeRegion. |
- SetWindowRgn(hwnd(), new_region, TRUE); |
- } else { |
- DeleteObject(new_region); |
- } |
- |
- DeleteObject(current_rgn); |
-} |
- |
-LRESULT HWNDMessageHandler::DefWindowProcWithRedrawLock(UINT message, |
- WPARAM w_param, |
- LPARAM l_param) { |
- ScopedRedrawLock lock(this); |
- // The Widget and HWND can be destroyed in the call to DefWindowProc, so use |
- // the |destroyed_| flag to avoid unlocking (and crashing) after destruction. |
- bool destroyed = false; |
- destroyed_ = &destroyed; |
- LRESULT result = DefWindowProc(hwnd(), message, w_param, l_param); |
- if (destroyed) |
- lock.CancelUnlockOperation(); |
- else |
- destroyed_ = NULL; |
- return result; |
-} |
- |
-void HWNDMessageHandler::NotifyOwnedWindowsParentClosing() { |
- FindOwnedWindowsData data; |
- data.window = hwnd(); |
- EnumThreadWindows(GetCurrentThreadId(), FindOwnedWindowsCallback, |
- reinterpret_cast<LPARAM>(&data)); |
- for (size_t i = 0; i < data.owned_widgets.size(); ++i) |
- data.owned_widgets[i]->OnOwnerClosing(); |
-} |
- |
-void HWNDMessageHandler::LockUpdates(bool force) { |
- // We skip locked updates when Aero is on for two reasons: |
- // 1. Because it isn't necessary |
- // 2. Because toggling the WS_VISIBLE flag may occur while the GPU process is |
- // attempting to present a child window's backbuffer onscreen. When these |
- // two actions race with one another, the child window will either flicker |
- // or will simply stop updating entirely. |
- if ((force || !ui::win::IsAeroGlassEnabled()) && ++lock_updates_count_ == 1) { |
- SetWindowLong(hwnd(), GWL_STYLE, |
- GetWindowLong(hwnd(), GWL_STYLE) & ~WS_VISIBLE); |
- } |
-} |
- |
-void HWNDMessageHandler::UnlockUpdates(bool force) { |
- if ((force || !ui::win::IsAeroGlassEnabled()) && --lock_updates_count_ <= 0) { |
- SetWindowLong(hwnd(), GWL_STYLE, |
- GetWindowLong(hwnd(), GWL_STYLE) | WS_VISIBLE); |
- lock_updates_count_ = 0; |
- } |
-} |
- |
-void HWNDMessageHandler::RedrawInvalidRect() { |
- if (!use_layered_buffer_) { |
- RECT r = { 0, 0, 0, 0 }; |
- if (GetUpdateRect(hwnd(), &r, FALSE) && !IsRectEmpty(&r)) { |
- RedrawWindow(hwnd(), &r, NULL, |
- RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOCHILDREN); |
- } |
- } |
-} |
- |
-void HWNDMessageHandler::RedrawLayeredWindowContents() { |
- if (invalid_rect_.IsEmpty()) |
- return; |
- |
- // We need to clip to the dirty rect ourselves. |
- layered_window_contents_->sk_canvas()->save(SkCanvas::kClip_SaveFlag); |
- layered_window_contents_->ClipRect(invalid_rect_); |
- delegate_->PaintLayeredWindow(layered_window_contents_.get()); |
- layered_window_contents_->sk_canvas()->restore(); |
- |
- RECT wr; |
- GetWindowRect(hwnd(), &wr); |
- SIZE size = {wr.right - wr.left, wr.bottom - wr.top}; |
- POINT position = {wr.left, wr.top}; |
- HDC dib_dc = skia::BeginPlatformPaint(layered_window_contents_->sk_canvas()); |
- POINT zero = {0, 0}; |
- BLENDFUNCTION blend = {AC_SRC_OVER, 0, layered_alpha_, AC_SRC_ALPHA}; |
- UpdateLayeredWindow(hwnd(), NULL, &position, &size, dib_dc, &zero, |
- RGB(0xFF, 0xFF, 0xFF), &blend, ULW_ALPHA); |
- invalid_rect_.SetRect(0, 0, 0, 0); |
- skia::EndPlatformPaint(layered_window_contents_->sk_canvas()); |
-} |
- |
-// Message handlers ------------------------------------------------------------ |
- |
-void HWNDMessageHandler::OnActivateApp(BOOL active, DWORD thread_id) { |
- if (delegate_->IsWidgetWindow() && !active && |
- thread_id != GetCurrentThreadId()) { |
- delegate_->HandleAppDeactivated(); |
- // Also update the native frame if it is rendering the non-client area. |
- if (!remove_standard_frame_ && !delegate_->IsUsingCustomFrame()) |
- DefWindowProcWithRedrawLock(WM_NCACTIVATE, FALSE, 0); |
- } |
-} |
- |
-BOOL HWNDMessageHandler::OnAppCommand(HWND window, |
- short command, |
- WORD device, |
- int keystate) { |
- BOOL handled = !!delegate_->HandleAppCommand(command); |
- SetMsgHandled(handled); |
- // Make sure to return TRUE if the event was handled or in some cases the |
- // system will execute the default handler which can cause bugs like going |
- // forward or back two pages instead of one. |
- return handled; |
-} |
- |
-void HWNDMessageHandler::OnCancelMode() { |
- delegate_->HandleCancelMode(); |
- // Need default handling, otherwise capture and other things aren't canceled. |
- SetMsgHandled(FALSE); |
-} |
- |
-void HWNDMessageHandler::OnCaptureChanged(HWND window) { |
- delegate_->HandleCaptureLost(); |
-} |
- |
-void HWNDMessageHandler::OnClose() { |
- delegate_->HandleClose(); |
-} |
- |
-void HWNDMessageHandler::OnCommand(UINT notification_code, |
- int command, |
- HWND window) { |
- // If the notification code is > 1 it means it is control specific and we |
- // should ignore it. |
- if (notification_code > 1 || delegate_->HandleAppCommand(command)) |
- SetMsgHandled(FALSE); |
-} |
- |
-LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) { |
- use_layered_buffer_ = !!(window_ex_style() & WS_EX_LAYERED); |
- |
-#if defined(USE_AURA) |
- if (window_ex_style() & WS_EX_COMPOSITED) { |
- if (base::win::GetVersion() >= base::win::VERSION_VISTA) { |
- // This is part of the magic to emulate layered windows with Aura |
- // see the explanation elsewere when we set WS_EX_COMPOSITED style. |
- MARGINS margins = {-1,-1,-1,-1}; |
- DwmExtendFrameIntoClientArea(hwnd(), &margins); |
- } |
- } |
-#endif |
- |
- fullscreen_handler_->set_hwnd(hwnd()); |
- |
- // This message initializes the window so that focus border are shown for |
- // windows. |
- SendMessage(hwnd(), |
- WM_CHANGEUISTATE, |
- MAKELPARAM(UIS_CLEAR, UISF_HIDEFOCUS), |
- 0); |
- |
- // Bug 964884: detach the IME attached to this window. |
- // We should attach IMEs only when we need to input CJK strings. |
- ImmAssociateContextEx(hwnd(), NULL, 0); |
- |
- if (remove_standard_frame_) { |
- SetWindowLong(hwnd(), GWL_STYLE, |
- GetWindowLong(hwnd(), GWL_STYLE) & ~WS_CAPTION); |
- SendFrameChanged(); |
- } |
- |
- // Get access to a modifiable copy of the system menu. |
- GetSystemMenu(hwnd(), false); |
- |
- if (base::win::GetVersion() >= base::win::VERSION_WIN7) |
- RegisterTouchWindow(hwnd(), 0); |
- |
- // We need to allow the delegate to size its contents since the window may not |
- // receive a size notification when its initial bounds are specified at window |
- // creation time. |
- ClientAreaSizeChanged(); |
- |
- // We need to add ourselves as a message loop observer so that we can repaint |
- // aggressively if the contents of our window become invalid. Unfortunately |
- // WM_PAINT messages are starved and we get flickery redrawing when resizing |
- // if we do not do this. |
- MessageLoopForUI::current()->AddObserver(this); |
- |
- delegate_->HandleCreate(); |
- |
- // TODO(beng): move more of NWW::OnCreate here. |
- return 0; |
-} |
- |
-void HWNDMessageHandler::OnDestroy() { |
- delegate_->HandleDestroying(); |
-} |
- |
-void HWNDMessageHandler::OnDisplayChange(UINT bits_per_pixel, |
- const CSize& screen_size) { |
- delegate_->HandleDisplayChange(); |
-} |
- |
-LRESULT HWNDMessageHandler::OnDwmCompositionChanged(UINT msg, |
- WPARAM w_param, |
- LPARAM l_param) { |
- if (!delegate_->IsWidgetWindow()) { |
- SetMsgHandled(FALSE); |
- return 0; |
- } |
- // For some reason, we need to hide the window while we're changing the frame |
- // type only when we're changing it in response to WM_DWMCOMPOSITIONCHANGED. |
- // If we don't, the client area will be filled with black. I'm suspecting |
- // something skia-ey. |
- // Frame type toggling caused by the user (e.g. switching theme) doesn't seem |
- // to have this requirement. |
- FrameTypeChanged(); |
- return 0; |
-} |
- |
-void HWNDMessageHandler::OnEnterSizeMove() { |
- delegate_->HandleBeginWMSizeMove(); |
- SetMsgHandled(FALSE); |
-} |
- |
-LRESULT HWNDMessageHandler::OnEraseBkgnd(HDC dc) { |
- // Needed to prevent resize flicker. |
- return 1; |
-} |
- |
-void HWNDMessageHandler::OnExitSizeMove() { |
- delegate_->HandleEndWMSizeMove(); |
- SetMsgHandled(FALSE); |
-} |
- |
-void HWNDMessageHandler::OnGetMinMaxInfo(MINMAXINFO* minmax_info) { |
- gfx::Size min_window_size; |
- gfx::Size max_window_size; |
- delegate_->GetMinMaxSize(&min_window_size, &max_window_size); |
- |
- // Add the native frame border size to the minimum and maximum size if the |
- // view reports its size as the client size. |
- if (delegate_->WidgetSizeIsClientSize()) { |
- CRect client_rect, window_rect; |
- GetClientRect(hwnd(), &client_rect); |
- GetWindowRect(hwnd(), &window_rect); |
- window_rect -= client_rect; |
- min_window_size.Enlarge(window_rect.Width(), window_rect.Height()); |
- if (!max_window_size.IsEmpty()) |
- max_window_size.Enlarge(window_rect.Width(), window_rect.Height()); |
- } |
- minmax_info->ptMinTrackSize.x = min_window_size.width(); |
- minmax_info->ptMinTrackSize.y = min_window_size.height(); |
- if (max_window_size.width() || max_window_size.height()) { |
- if (!max_window_size.width()) |
- max_window_size.set_width(GetSystemMetrics(SM_CXMAXTRACK)); |
- if (!max_window_size.height()) |
- max_window_size.set_height(GetSystemMetrics(SM_CYMAXTRACK)); |
- minmax_info->ptMaxTrackSize.x = max_window_size.width(); |
- minmax_info->ptMaxTrackSize.y = max_window_size.height(); |
- } |
- SetMsgHandled(FALSE); |
-} |
- |
-LRESULT HWNDMessageHandler::OnGetObject(UINT message, |
- WPARAM w_param, |
- LPARAM l_param) { |
- LRESULT reference_result = static_cast<LRESULT>(0L); |
- |
- // Accessibility readers will send an OBJID_CLIENT message |
- if (OBJID_CLIENT == l_param) { |
- // Retrieve MSAA dispatch object for the root view. |
- base::win::ScopedComPtr<IAccessible> root( |
- delegate_->GetNativeViewAccessible()); |
- |
- // Create a reference that MSAA will marshall to the client. |
- reference_result = LresultFromObject(IID_IAccessible, w_param, |
- static_cast<IAccessible*>(root.Detach())); |
- } |
- |
- return reference_result; |
-} |
- |
-LRESULT HWNDMessageHandler::OnImeMessages(UINT message, |
- WPARAM w_param, |
- LPARAM l_param) { |
- LRESULT result = 0; |
- SetMsgHandled(delegate_->HandleIMEMessage( |
- message, w_param, l_param, &result)); |
- return result; |
-} |
- |
-void HWNDMessageHandler::OnInitMenu(HMENU menu) { |
- bool is_fullscreen = fullscreen_handler_->fullscreen(); |
- bool is_minimized = IsMinimized(); |
- bool is_maximized = IsMaximized(); |
- bool is_restored = !is_fullscreen && !is_minimized && !is_maximized; |
- |
- ScopedRedrawLock lock(this); |
- EnableMenuItemByCommand(menu, SC_RESTORE, is_minimized || is_maximized); |
- EnableMenuItemByCommand(menu, SC_MOVE, is_restored); |
- EnableMenuItemByCommand(menu, SC_SIZE, delegate_->CanResize() && is_restored); |
- EnableMenuItemByCommand(menu, SC_MAXIMIZE, delegate_->CanMaximize() && |
- !is_fullscreen && !is_maximized); |
- EnableMenuItemByCommand(menu, SC_MINIMIZE, delegate_->CanMaximize() && |
- !is_minimized); |
-} |
- |
-void HWNDMessageHandler::OnInputLangChange(DWORD character_set, |
- HKL input_language_id) { |
- delegate_->HandleInputLanguageChange(character_set, input_language_id); |
-} |
- |
-LRESULT HWNDMessageHandler::OnKeyEvent(UINT message, |
- WPARAM w_param, |
- LPARAM l_param) { |
- MSG msg = { hwnd(), message, w_param, l_param, GetMessageTime() }; |
- ui::KeyEvent key(msg, message == WM_CHAR); |
- if (!delegate_->HandleUntranslatedKeyEvent(key)) |
- DispatchKeyEventPostIME(key); |
- return 0; |
-} |
- |
-void HWNDMessageHandler::OnKillFocus(HWND focused_window) { |
- delegate_->HandleNativeBlur(focused_window); |
- SetMsgHandled(FALSE); |
-} |
- |
-LRESULT HWNDMessageHandler::OnMouseActivate(UINT message, |
- WPARAM w_param, |
- LPARAM l_param) { |
- // TODO(beng): resolve this with the GetWindowLong() check on the subsequent |
- // line. |
- if (delegate_->IsWidgetWindow()) |
- return delegate_->CanActivate() ? MA_ACTIVATE : MA_NOACTIVATEANDEAT; |
- if (GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE) |
- return MA_NOACTIVATE; |
- SetMsgHandled(FALSE); |
- return MA_ACTIVATE; |
-} |
- |
-LRESULT HWNDMessageHandler::OnMouseRange(UINT message, |
- WPARAM w_param, |
- LPARAM l_param) { |
-#if defined(USE_AURA) |
- // We handle touch events on Windows Aura. Ignore synthesized mouse messages |
- // from Windows. |
- if (!touch_ids_.empty() || ui::IsMouseEventFromTouch(message)) |
- return 0; |
-#endif |
- if (message == WM_RBUTTONUP && is_right_mouse_pressed_on_caption_) { |
- is_right_mouse_pressed_on_caption_ = false; |
- ReleaseCapture(); |
- // |point| is in window coordinates, but WM_NCHITTEST and TrackPopupMenu() |
- // expect screen coordinates. |
- CPoint screen_point(l_param); |
- MapWindowPoints(hwnd(), HWND_DESKTOP, &screen_point, 1); |
- w_param = SendMessage(hwnd(), WM_NCHITTEST, 0, |
- MAKELPARAM(screen_point.x, screen_point.y)); |
- if (w_param == HTCAPTION || w_param == HTSYSMENU) { |
- ui::ShowSystemMenu(hwnd(), screen_point.x, screen_point.y); |
- return 0; |
- } |
- } else if (message == WM_NCLBUTTONDOWN && delegate_->IsUsingCustomFrame()) { |
- switch (w_param) { |
- 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 |
- // 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. |
- // Combine |w_param| with common key state message flags. |
- w_param |= base::win::IsCtrlPressed() ? MK_CONTROL : 0; |
- w_param |= base::win::IsShiftPressed() ? MK_SHIFT : 0; |
- } |
- } |
- } else if (message == WM_NCRBUTTONDOWN && |
- (w_param == HTCAPTION || w_param == HTSYSMENU)) { |
- is_right_mouse_pressed_on_caption_ = true; |
- // We SetCapture() to ensure we only show the menu when the button |
- // down and up are both on the caption. Note: this causes the button up to |
- // be WM_RBUTTONUP instead of WM_NCRBUTTONUP. |
- SetCapture(); |
- } |
- |
- MSG msg = { hwnd(), message, w_param, l_param, GetMessageTime(), |
- { GET_X_LPARAM(l_param), GET_Y_LPARAM(l_param) } }; |
- ui::MouseEvent event(msg); |
- if (!touch_ids_.empty() || ui::IsMouseEventFromTouch(message)) |
- event.set_flags(event.flags() | ui::EF_FROM_TOUCH); |
- |
- if (!(event.flags() & ui::EF_IS_NON_CLIENT)) |
- delegate_->HandleTooltipMouseMove(message, w_param, l_param); |
- |
- if (event.type() == ui::ET_MOUSE_MOVED && !HasCapture()) { |
- // Windows only fires WM_MOUSELEAVE events if the application begins |
- // "tracking" mouse events for a given HWND during WM_MOUSEMOVE events. |
- // We need to call |TrackMouseEvents| to listen for WM_MOUSELEAVE. |
- TrackMouseEvents((message == WM_NCMOUSEMOVE) ? |
- TME_NONCLIENT | TME_LEAVE : TME_LEAVE); |
- } else if (event.type() == ui::ET_MOUSE_EXITED) { |
- // Reset our tracking flags so future mouse movement over this |
- // NativeWidgetWin results in a new tracking session. Fall through for |
- // OnMouseEvent. |
- active_mouse_tracking_flags_ = 0; |
- } else if (event.type() == ui::ET_MOUSEWHEEL) { |
- // Reroute the mouse wheel to the window under the pointer if applicable. |
- return (ui::RerouteMouseWheel(hwnd(), w_param, l_param) || |
- delegate_->HandleMouseEvent(ui::MouseWheelEvent(msg))) ? 0 : 1; |
- } |
- |
- bool handled = delegate_->HandleMouseEvent(event); |
- if (!handled && message == WM_NCLBUTTONDOWN && w_param != HTSYSMENU && |
- delegate_->IsUsingCustomFrame()) { |
- // TODO(msw): Eliminate undesired painting, or re-evaluate this workaround. |
- // DefWindowProc for WM_NCLBUTTONDOWN does weird non-client painting, so we |
- // need to call it inside a ScopedRedrawLock. This may cause other negative |
- // side-effects (ex/ stifling non-client mouse releases). |
- DefWindowProcWithRedrawLock(message, w_param, l_param); |
- handled = true; |
- } |
- |
- SetMsgHandled(handled); |
- return 0; |
-} |
- |
-void HWNDMessageHandler::OnMove(const CPoint& point) { |
- delegate_->HandleMove(); |
- SetMsgHandled(FALSE); |
-} |
- |
-void HWNDMessageHandler::OnMoving(UINT param, const RECT* new_bounds) { |
- delegate_->HandleMove(); |
-} |
- |
-LRESULT HWNDMessageHandler::OnNCActivate(BOOL active) { |
- if (delegate_->CanActivate()) |
- delegate_->HandleActivationChanged(!!active); |
- |
- if (!delegate_->IsWidgetWindow()) { |
- SetMsgHandled(FALSE); |
- return 0; |
- } |
- |
- if (!delegate_->CanActivate()) |
- return TRUE; |
- |
- // On activation, lift any prior restriction against rendering as inactive. |
- bool inactive_rendering_disabled = delegate_->IsInactiveRenderingDisabled(); |
- if (active && inactive_rendering_disabled) |
- delegate_->EnableInactiveRendering(); |
- |
- if (delegate_->IsUsingCustomFrame()) { |
- // TODO(beng, et al): Hack to redraw this window and child windows |
- // synchronously upon activation. Not all child windows are redrawing |
- // themselves leading to issues like http://crbug.com/74604 |
- // We redraw out-of-process HWNDs asynchronously to avoid hanging the |
- // whole app if a child HWND belonging to a hung plugin is encountered. |
- RedrawWindow(hwnd(), NULL, NULL, |
- RDW_NOCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW); |
- EnumChildWindows(hwnd(), EnumChildWindowsForRedraw, NULL); |
- } |
- |
- // The frame may need to redraw as a result of the activation change. |
- // We can get WM_NCACTIVATE before we're actually visible. If we're not |
- // visible, no need to paint. |
- if (IsVisible()) |
- delegate_->SchedulePaint(); |
- |
- // Avoid DefWindowProc non-client rendering over our custom frame on newer |
- // Windows versions only (breaks taskbar activation indication on XP/Vista). |
- if (delegate_->IsUsingCustomFrame() && |
- base::win::GetVersion() > base::win::VERSION_VISTA) { |
- SetMsgHandled(TRUE); |
- return TRUE; |
- } |
- |
- return DefWindowProcWithRedrawLock( |
- WM_NCACTIVATE, inactive_rendering_disabled || active, 0); |
-} |
- |
-LRESULT HWNDMessageHandler::OnNCCalcSize(BOOL mode, LPARAM l_param) { |
- // We only override the default handling if we need to specify a custom |
- // non-client edge width. Note that in most cases "no insets" means no |
- // custom width, but in fullscreen mode or when the NonClientFrameView |
- // requests it, we want a custom width of 0. |
- |
- // Let User32 handle the first nccalcsize for captioned windows |
- // so it updates its internal structures (specifically caption-present) |
- // Without this Tile & Cascade windows won't work. |
- // See http://code.google.com/p/chromium/issues/detail?id=900 |
- if (is_first_nccalc_) { |
- is_first_nccalc_ = false; |
- if (GetWindowLong(hwnd(), GWL_STYLE) & WS_CAPTION) { |
- SetMsgHandled(FALSE); |
- return 0; |
- } |
- } |
- |
- gfx::Insets insets = GetClientAreaInsets(); |
- if (insets.empty() && !fullscreen_handler_->fullscreen() && |
- !(mode && remove_standard_frame_)) { |
- SetMsgHandled(FALSE); |
- return 0; |
- } |
- |
- RECT* client_rect = mode ? |
- &(reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param)->rgrc[0]) : |
- reinterpret_cast<RECT*>(l_param); |
- client_rect->left += insets.left(); |
- client_rect->top += insets.top(); |
- client_rect->bottom -= insets.bottom(); |
- client_rect->right -= insets.right(); |
- if (IsMaximized()) { |
- // Find all auto-hide taskbars along the screen edges and adjust in by the |
- // thickness of the auto-hide taskbar on each such edge, so the window isn't |
- // treated as a "fullscreen app", which would cause the taskbars to |
- // disappear. |
- HMONITOR monitor = MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONULL); |
- if (!monitor) { |
- // We might end up here if the window was previously minimized and the |
- // user clicks on the taskbar button to restore it in the previously |
- // maximized position. In that case WM_NCCALCSIZE is sent before the |
- // window coordinates are restored to their previous values, so our |
- // (left,top) would probably be (-32000,-32000) like all minimized |
- // windows. So the above MonitorFromWindow call fails, but if we check |
- // the window rect given with WM_NCCALCSIZE (which is our previous |
- // restored window position) we will get the correct monitor handle. |
- monitor = MonitorFromRect(client_rect, MONITOR_DEFAULTTONULL); |
- if (!monitor) { |
- // This is probably an extreme case that we won't hit, but if we don't |
- // intersect any monitor, let us not adjust the client rect since our |
- // window will not be visible anyway. |
- return 0; |
- } |
- } |
- if (GetTopmostAutoHideTaskbarForEdge(ABE_LEFT, monitor)) |
- client_rect->left += kAutoHideTaskbarThicknessPx; |
- if (GetTopmostAutoHideTaskbarForEdge(ABE_TOP, monitor)) { |
- if (!delegate_->IsUsingCustomFrame()) { |
- // Tricky bit. Due to a bug in DwmDefWindowProc()'s handling of |
- // WM_NCHITTEST, having any nonclient area atop the window causes the |
- // caption buttons to draw onscreen but not respond to mouse |
- // hover/clicks. |
- // So for a taskbar at the screen top, we can't push the |
- // client_rect->top down; instead, we move the bottom up by one pixel, |
- // which is the smallest change we can make and still get a client area |
- // less than the screen size. This is visibly ugly, but there seems to |
- // be no better solution. |
- --client_rect->bottom; |
- } else { |
- client_rect->top += kAutoHideTaskbarThicknessPx; |
- } |
- } |
- if (GetTopmostAutoHideTaskbarForEdge(ABE_RIGHT, monitor)) |
- client_rect->right -= kAutoHideTaskbarThicknessPx; |
- if (GetTopmostAutoHideTaskbarForEdge(ABE_BOTTOM, monitor)) |
- client_rect->bottom -= kAutoHideTaskbarThicknessPx; |
- |
- // We cannot return WVR_REDRAW when there is nonclient area, or Windows |
- // exhibits bugs where client pixels and child HWNDs are mispositioned by |
- // the width/height of the upper-left nonclient area. |
- return 0; |
- } |
- |
- // If the window bounds change, we're going to relayout and repaint anyway. |
- // Returning WVR_REDRAW avoids an extra paint before that of the old client |
- // pixels in the (now wrong) location, and thus makes actions like resizing a |
- // window from the left edge look slightly less broken. |
- // We special case when left or top insets are 0, since these conditions |
- // actually require another repaint to correct the layout after glass gets |
- // turned on and off. |
- if (insets.left() == 0 || insets.top() == 0) |
- return 0; |
- return mode ? WVR_REDRAW : 0; |
-} |
- |
-LRESULT HWNDMessageHandler::OnNCHitTest(const CPoint& point) { |
- if (!delegate_->IsWidgetWindow()) { |
- SetMsgHandled(FALSE); |
- return 0; |
- } |
- |
- // If the DWM is rendering the window controls, we need to give the DWM's |
- // default window procedure first chance to handle hit testing. |
- if (!remove_standard_frame_ && !delegate_->IsUsingCustomFrame()) { |
- LRESULT result; |
- if (DwmDefWindowProc(hwnd(), WM_NCHITTEST, 0, |
- MAKELPARAM(point.x, point.y), &result)) { |
- return result; |
- } |
- } |
- |
- // First, give the NonClientView a chance to test the point to see if it |
- // provides any of the non-client area. |
- POINT temp = point; |
- MapWindowPoints(HWND_DESKTOP, hwnd(), &temp, 1); |
- int component = delegate_->GetNonClientComponent(gfx::Point(temp)); |
- if (component != HTNOWHERE) |
- return component; |
- |
- // Otherwise, we let Windows do all the native frame non-client handling for |
- // us. |
- SetMsgHandled(FALSE); |
- return 0; |
-} |
- |
-void HWNDMessageHandler::OnNCPaint(HRGN rgn) { |
- // We only do non-client painting if we're not using the native frame. |
- // It's required to avoid some native painting artifacts from appearing when |
- // the window is resized. |
- if (!delegate_->IsWidgetWindow() || !delegate_->IsUsingCustomFrame()) { |
- SetMsgHandled(FALSE); |
- return; |
- } |
- |
- // 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(hwnd(), &window_rect); |
- |
- gfx::Size root_view_size = delegate_->GetRootViewSize(); |
- if (gfx::Size(window_rect.Width(), window_rect.Height()) != root_view_size) { |
- // 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(hwnd()); |
- ClipState clip_state; |
- clip_state.x = window_rect.left; |
- clip_state.y = window_rect.top; |
- clip_state.parent = hwnd(); |
- clip_state.dc = dc; |
- EnumChildWindows(hwnd(), &ClipDCToChild, |
- reinterpret_cast<LPARAM>(&clip_state)); |
- |
- gfx::Rect old_paint_region = invalid_rect_; |
- if (!old_paint_region.IsEmpty()) { |
- // The root view has a region that needs to be painted. Include it in the |
- // region we're going to paint. |
- |
- CRect old_paint_region_crect = old_paint_region.ToRECT(); |
- CRect tmp = dirty_region; |
- UnionRect(&dirty_region, &tmp, &old_paint_region_crect); |
- } |
- |
- SchedulePaintInRect(gfx::Rect(dirty_region)); |
- |
- // gfx::CanvasSkiaPaint's 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. |
- if (!delegate_->HandlePaintAccelerated(gfx::Rect(dirty_region))) { |
- gfx::CanvasSkiaPaint canvas(dc, true, dirty_region.left, |
- dirty_region.top, dirty_region.Width(), |
- dirty_region.Height()); |
- delegate_->HandlePaint(&canvas); |
- } |
- |
- ReleaseDC(hwnd(), dc); |
- // When using a custom frame, we want to avoid calling DefWindowProc() since |
- // that may render artifacts. |
- SetMsgHandled(delegate_->IsUsingCustomFrame()); |
-} |
- |
-LRESULT HWNDMessageHandler::OnNCUAHDrawCaption(UINT message, |
- 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(delegate_->IsUsingCustomFrame()); |
- return 0; |
-} |
- |
-LRESULT HWNDMessageHandler::OnNCUAHDrawFrame(UINT message, |
- 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(delegate_->IsUsingCustomFrame()); |
- return 0; |
-} |
- |
-LRESULT HWNDMessageHandler::OnNotify(int w_param, NMHDR* l_param) { |
- LRESULT l_result = 0; |
- SetMsgHandled(delegate_->HandleTooltipNotify(w_param, l_param, &l_result)); |
- return l_result; |
-} |
- |
-void HWNDMessageHandler::OnPaint(HDC dc) { |
- RECT dirty_rect; |
- // Try to paint accelerated first. |
- if (GetUpdateRect(hwnd(), &dirty_rect, FALSE) && |
- !IsRectEmpty(&dirty_rect)) { |
- if (delegate_->HandlePaintAccelerated(gfx::Rect(dirty_rect))) { |
- ValidateRect(hwnd(), NULL); |
- } else { |
-#if defined(USE_AURA) |
- delegate_->HandlePaint(NULL); |
-#else |
- scoped_ptr<gfx::CanvasPaint> canvas( |
- gfx::CanvasPaint::CreateCanvasPaint(hwnd())); |
- delegate_->HandlePaint(canvas->AsCanvas()); |
-#endif |
- } |
- } else { |
- // TODO(msw): Find a better solution for this crbug.com/93530 workaround. |
- // Some scenarios otherwise fail to validate minimized app/popup windows. |
- ValidateRect(hwnd(), NULL); |
- } |
-} |
- |
-LRESULT HWNDMessageHandler::OnReflectedMessage(UINT message, |
- WPARAM w_param, |
- LPARAM l_param) { |
- SetMsgHandled(FALSE); |
- return 0; |
-} |
- |
-LRESULT HWNDMessageHandler::OnSetCursor(UINT message, |
- WPARAM w_param, |
- LPARAM l_param) { |
- const LRESULT result = DefWindowProcWithRedrawLock(message, w_param, l_param); |
- // Invalidate the window to paint over any outdated window regions asap, as |
- // using a RedrawLock for WM_SETCURSOR may show content through this window. |
- if (delegate_->IsUsingCustomFrame() && !ui::win::IsAeroGlassEnabled()) |
- InvalidateRect(hwnd(), NULL, FALSE); |
- return result; |
-} |
- |
-void HWNDMessageHandler::OnSetFocus(HWND last_focused_window) { |
- delegate_->HandleNativeFocus(last_focused_window); |
- SetMsgHandled(FALSE); |
-} |
- |
-LRESULT HWNDMessageHandler::OnSetIcon(UINT size_type, HICON new_icon) { |
- // Use a ScopedRedrawLock to avoid weird non-client painting. |
- return DefWindowProcWithRedrawLock(WM_SETICON, size_type, |
- reinterpret_cast<LPARAM>(new_icon)); |
-} |
- |
-LRESULT HWNDMessageHandler::OnSetText(const wchar_t* text) { |
- // Use a ScopedRedrawLock to avoid weird non-client painting. |
- return DefWindowProcWithRedrawLock(WM_SETTEXT, NULL, |
- reinterpret_cast<LPARAM>(text)); |
-} |
- |
-void HWNDMessageHandler::OnSettingChange(UINT flags, const wchar_t* section) { |
- if (!GetParent(hwnd()) && (flags == SPI_SETWORKAREA) && |
- !delegate_->WillProcessWorkAreaChange()) { |
- // Fire a dummy SetWindowPos() call, so we'll trip the code in |
- // OnWindowPosChanging() below that notices work area changes. |
- ::SetWindowPos(hwnd(), 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | |
- SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE | SWP_NOOWNERZORDER); |
- SetMsgHandled(TRUE); |
- } else { |
- if (flags == SPI_SETWORKAREA) |
- delegate_->HandleWorkAreaChanged(); |
- SetMsgHandled(FALSE); |
- } |
-} |
- |
-void HWNDMessageHandler::OnSize(UINT param, const CSize& size) { |
- RedrawWindow(hwnd(), NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN); |
- // ResetWindowRegion is going to trigger WM_NCPAINT. By doing it after we've |
- // invoked OnSize we ensure the RootView has been laid out. |
- ResetWindowRegion(false); |
-} |
- |
-void HWNDMessageHandler::OnSysCommand(UINT notification_code, |
- const CPoint& point) { |
- if (!delegate_->IsWidgetWindow()) |
- return; |
- |
- // 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; |
- // Ignore size/move/maximize in fullscreen mode. |
- if (fullscreen_handler_->fullscreen() && |
- (((notification_code & sc_mask) == SC_SIZE) || |
- ((notification_code & sc_mask) == SC_MOVE) || |
- ((notification_code & sc_mask) == SC_MAXIMIZE))) |
- return; |
- if (delegate_->IsUsingCustomFrame()) { |
- if ((notification_code & sc_mask) == SC_MINIMIZE || |
- (notification_code & sc_mask) == SC_MAXIMIZE || |
- (notification_code & sc_mask) == SC_RESTORE) { |
- delegate_->ResetWindowControls(); |
- } else if ((notification_code & sc_mask) == SC_MOVE || |
- (notification_code & sc_mask) == SC_SIZE) { |
- if (!IsVisible()) { |
- // Circumvent ScopedRedrawLocks and force visibility before entering a |
- // resize or move modal loop to get continuous sizing/moving feedback. |
- SetWindowLong(hwnd(), GWL_STYLE, |
- GetWindowLong(hwnd(), GWL_STYLE) | WS_VISIBLE); |
- } |
- } |
- } |
- |
- // Handle SC_KEYMENU, which means that the user has pressed the ALT |
- // key and released it, so we should focus the menu bar. |
- if ((notification_code & sc_mask) == SC_KEYMENU && point.x == 0) { |
- int modifiers = ui::EF_NONE; |
- if (base::win::IsShiftPressed()) |
- modifiers |= ui::EF_SHIFT_DOWN; |
- if (base::win::IsCtrlPressed()) |
- modifiers |= ui::EF_CONTROL_DOWN; |
- // Retrieve the status of shift and control keys to prevent consuming |
- // shift+alt keys, which are used by Windows to change input languages. |
- ui::Accelerator accelerator(ui::KeyboardCodeForWindowsKeyCode(VK_MENU), |
- modifiers); |
- delegate_->HandleAccelerator(accelerator); |
- return; |
- } |
- |
- // If the delegate can't handle it, the system implementation will be called. |
- if (!delegate_->HandleCommand(notification_code)) { |
- DefWindowProc(hwnd(), WM_SYSCOMMAND, notification_code, |
- MAKELPARAM(point.x, point.y)); |
- } |
-} |
- |
-void HWNDMessageHandler::OnThemeChanged() { |
- ui::NativeThemeWin::instance()->CloseHandles(); |
-} |
- |
-LRESULT HWNDMessageHandler::OnTouchEvent(UINT message, |
- WPARAM w_param, |
- LPARAM l_param) { |
- int num_points = LOWORD(w_param); |
- scoped_ptr<TOUCHINPUT[]> input(new TOUCHINPUT[num_points]); |
- if (GetTouchInputInfo(reinterpret_cast<HTOUCHINPUT>(l_param), |
- num_points, input.get(), sizeof(TOUCHINPUT))) { |
- for (int i = 0; i < num_points; ++i) { |
- ui::EventType touch_event_type = ui::ET_UNKNOWN; |
- |
- if (input[i].dwFlags & TOUCHEVENTF_DOWN) { |
- touch_ids_.insert(input[i].dwID); |
- touch_event_type = ui::ET_TOUCH_PRESSED; |
- } else if (input[i].dwFlags & TOUCHEVENTF_UP) { |
- touch_ids_.erase(input[i].dwID); |
- touch_event_type = ui::ET_TOUCH_RELEASED; |
- } else if (input[i].dwFlags & TOUCHEVENTF_MOVE) { |
- touch_event_type = ui::ET_TOUCH_MOVED; |
- } |
- // Handle touch events only on Aura for now. |
-#if defined(USE_AURA) |
- if (touch_event_type != ui::ET_UNKNOWN) { |
- POINT point; |
- point.x = TOUCH_COORD_TO_PIXEL(input[i].x); |
- point.y = TOUCH_COORD_TO_PIXEL(input[i].y); |
- |
- ScreenToClient(hwnd(), &point); |
- |
- ui::TouchEvent event( |
- touch_event_type, |
- gfx::Point(point.x, point.y), |
- kDesktopChromeAuraTouchId, |
- base::TimeDelta::FromMilliseconds(input[i].dwTime)); |
- delegate_->HandleTouchEvent(event); |
- } |
-#endif |
- } |
- } |
- CloseTouchInputHandle(reinterpret_cast<HTOUCHINPUT>(l_param)); |
- SetMsgHandled(FALSE); |
- return 0; |
-} |
- |
-void HWNDMessageHandler::OnWindowPosChanging(WINDOWPOS* window_pos) { |
- if (ignore_window_pos_changes_) { |
- // If somebody's trying to toggle our visibility, change the nonclient area, |
- // change our Z-order, or activate us, we should probably let it go through. |
- if (!(window_pos->flags & ((IsVisible() ? SWP_HIDEWINDOW : SWP_SHOWWINDOW) | |
- SWP_FRAMECHANGED)) && |
- (window_pos->flags & (SWP_NOZORDER | SWP_NOACTIVATE))) { |
- // Just sizing/moving the window; ignore. |
- window_pos->flags |= SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW; |
- window_pos->flags &= ~(SWP_SHOWWINDOW | SWP_HIDEWINDOW); |
- } |
- } else if (!GetParent(hwnd())) { |
- CRect window_rect; |
- HMONITOR monitor; |
- gfx::Rect monitor_rect, work_area; |
- if (GetWindowRect(hwnd(), &window_rect) && |
- GetMonitorAndRects(window_rect, &monitor, &monitor_rect, &work_area)) { |
- bool work_area_changed = (monitor_rect == last_monitor_rect_) && |
- (work_area != last_work_area_); |
- if (monitor && (monitor == last_monitor_) && |
- ((fullscreen_handler_->fullscreen() && |
- !fullscreen_handler_->metro_snap()) || |
- work_area_changed)) { |
- // A rect for the monitor we're on changed. Normally Windows notifies |
- // us about this (and thus we're reaching here due to the SetWindowPos() |
- // call in OnSettingChange() above), but with some software (e.g. |
- // nVidia's nView desktop manager) the work area can change asynchronous |
- // to any notification, and we're just sent a SetWindowPos() call with a |
- // new (frequently incorrect) position/size. In either case, the best |
- // response is to throw away the existing position/size information in |
- // |window_pos| and recalculate it based on the new work rect. |
- gfx::Rect new_window_rect; |
- if (fullscreen_handler_->fullscreen()) { |
- new_window_rect = monitor_rect; |
- } else if (IsMaximized()) { |
- new_window_rect = work_area; |
- int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME); |
- new_window_rect.Inset(-border_thickness, -border_thickness); |
- } else { |
- new_window_rect = gfx::Rect(window_rect); |
- new_window_rect.AdjustToFit(work_area); |
- } |
- window_pos->x = new_window_rect.x(); |
- window_pos->y = new_window_rect.y(); |
- window_pos->cx = new_window_rect.width(); |
- window_pos->cy = new_window_rect.height(); |
- // WARNING! Don't set SWP_FRAMECHANGED here, it breaks moving the child |
- // HWNDs for some reason. |
- window_pos->flags &= ~(SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW); |
- window_pos->flags |= SWP_NOCOPYBITS; |
- |
- // Now ignore all immediately-following SetWindowPos() changes. Windows |
- // likes to (incorrectly) recalculate what our position/size should be |
- // and send us further updates. |
- ignore_window_pos_changes_ = true; |
- DCHECK(!ignore_pos_changes_factory_.HasWeakPtrs()); |
- MessageLoop::current()->PostTask( |
- FROM_HERE, |
- base::Bind(&HWNDMessageHandler::StopIgnoringPosChanges, |
- ignore_pos_changes_factory_.GetWeakPtr())); |
- } |
- last_monitor_ = monitor; |
- last_monitor_rect_ = monitor_rect; |
- last_work_area_ = work_area; |
- } |
- } |
- |
- if (ScopedFullscreenVisibility::IsHiddenForFullscreen(hwnd())) { |
- // Prevent the window from being made visible if we've been asked to do so. |
- // See comment in header as to why we might want this. |
- window_pos->flags &= ~SWP_SHOWWINDOW; |
- } |
- |
- SetMsgHandled(FALSE); |
-} |
- |
-void HWNDMessageHandler::OnWindowPosChanged(WINDOWPOS* window_pos) { |
- if (DidClientAreaSizeChange(window_pos)) |
- ClientAreaSizeChanged(); |
- if (remove_standard_frame_ && window_pos->flags & SWP_FRAMECHANGED && |
- ui::win::IsAeroGlassEnabled()) { |
- MARGINS m = {10, 10, 10, 10}; |
- DwmExtendFrameIntoClientArea(hwnd(), &m); |
- } |
- if (window_pos->flags & SWP_SHOWWINDOW) |
- delegate_->HandleVisibilityChanged(true); |
- else if (window_pos->flags & SWP_HIDEWINDOW) |
- delegate_->HandleVisibilityChanged(false); |
- SetMsgHandled(FALSE); |
-} |
- |
-} // namespace views |
+// Copyright (c) 2012 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 "ui/views/win/hwnd_message_handler.h" |
+ |
+#include <dwmapi.h> |
+#include <shellapi.h> |
+ |
+#include "base/bind.h" |
+#include "base/debug/trace_event.h" |
+#include "base/win/windows_version.h" |
+#include "ui/base/events/event.h" |
+#include "ui/base/events/event_utils.h" |
+#include "ui/base/keycodes/keyboard_code_conversion_win.h" |
+#include "ui/base/win/hwnd_util.h" |
+#include "ui/base/win/mouse_wheel_util.h" |
+#include "ui/base/win/shell.h" |
+#include "ui/base/win/touch_input.h" |
+#include "ui/gfx/canvas.h" |
+#include "ui/gfx/canvas_skia_paint.h" |
+#include "ui/gfx/icon_util.h" |
+#include "ui/gfx/insets.h" |
+#include "ui/gfx/path.h" |
+#include "ui/gfx/path_win.h" |
+#include "ui/gfx/screen.h" |
+#include "ui/native_theme/native_theme_win.h" |
+#include "ui/views/views_delegate.h" |
+#include "ui/views/widget/monitor_win.h" |
+#include "ui/views/widget/native_widget_win.h" |
+#include "ui/views/widget/widget_hwnd_utils.h" |
+#include "ui/views/win/fullscreen_handler.h" |
+#include "ui/views/win/hwnd_message_handler_delegate.h" |
+#include "ui/views/win/scoped_fullscreen_visibility.h" |
+ |
+#if !defined(USE_AURA) |
+#include "ui/views/accessibility/native_view_accessibility_win.h" |
+#include "ui/views/widget/child_window_message_processor.h" |
+#endif |
+ |
+namespace views { |
+namespace { |
+ |
+// MoveLoopMouseWatcher is used to determine if the user canceled or completed a |
+// move. win32 doesn't appear to offer a way to determine the result of a move, |
+// so we install hooks to determine if we got a mouse up and assume the move |
+// completed. |
+class MoveLoopMouseWatcher { |
+ public: |
+ explicit MoveLoopMouseWatcher(HWNDMessageHandler* host); |
+ ~MoveLoopMouseWatcher(); |
+ |
+ // Returns true if the mouse is up, or if we couldn't install the hook. |
+ bool got_mouse_up() const { return got_mouse_up_; } |
+ |
+ private: |
+ // Instance that owns the hook. We only allow one instance to hook the mouse |
+ // at a time. |
+ static MoveLoopMouseWatcher* instance_; |
+ |
+ // Key and mouse callbacks from the hook. |
+ static LRESULT CALLBACK MouseHook(int n_code, WPARAM w_param, LPARAM l_param); |
+ static LRESULT CALLBACK KeyHook(int n_code, WPARAM w_param, LPARAM l_param); |
+ |
+ void Unhook(); |
+ |
+ // HWNDMessageHandler that created us. |
+ HWNDMessageHandler* host_; |
+ |
+ // Did we get a mouse up? |
+ bool got_mouse_up_; |
+ |
+ // Hook identifiers. |
+ HHOOK mouse_hook_; |
+ HHOOK key_hook_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(MoveLoopMouseWatcher); |
+}; |
+ |
+// static |
+MoveLoopMouseWatcher* MoveLoopMouseWatcher::instance_ = NULL; |
+ |
+MoveLoopMouseWatcher::MoveLoopMouseWatcher(HWNDMessageHandler* host) |
+ : host_(host), |
+ got_mouse_up_(false), |
+ mouse_hook_(NULL), |
+ key_hook_(NULL) { |
+ // Only one instance can be active at a time. |
+ if (instance_) |
+ instance_->Unhook(); |
+ |
+ mouse_hook_ = SetWindowsHookEx( |
+ WH_MOUSE, &MouseHook, NULL, GetCurrentThreadId()); |
+ if (mouse_hook_) { |
+ instance_ = this; |
+ // We don't care if setting the key hook succeeded. |
+ key_hook_ = SetWindowsHookEx( |
+ WH_KEYBOARD, &KeyHook, NULL, GetCurrentThreadId()); |
+ } |
+ if (instance_ != this) { |
+ // Failed installation. Assume we got a mouse up in this case, otherwise |
+ // we'll think all drags were canceled. |
+ got_mouse_up_ = true; |
+ } |
+} |
+ |
+MoveLoopMouseWatcher::~MoveLoopMouseWatcher() { |
+ Unhook(); |
+} |
+ |
+void MoveLoopMouseWatcher::Unhook() { |
+ if (instance_ != this) |
+ return; |
+ |
+ DCHECK(mouse_hook_); |
+ UnhookWindowsHookEx(mouse_hook_); |
+ if (key_hook_) |
+ UnhookWindowsHookEx(key_hook_); |
+ key_hook_ = NULL; |
+ mouse_hook_ = NULL; |
+ instance_ = NULL; |
+} |
+ |
+// static |
+LRESULT CALLBACK MoveLoopMouseWatcher::MouseHook(int n_code, |
+ WPARAM w_param, |
+ LPARAM l_param) { |
+ DCHECK(instance_); |
+ if (n_code == HC_ACTION && w_param == WM_LBUTTONUP) |
+ instance_->got_mouse_up_ = true; |
+ return CallNextHookEx(instance_->mouse_hook_, n_code, w_param, l_param); |
+} |
+ |
+// static |
+LRESULT CALLBACK MoveLoopMouseWatcher::KeyHook(int n_code, |
+ WPARAM w_param, |
+ LPARAM l_param) { |
+ if (n_code == HC_ACTION && w_param == VK_ESCAPE) { |
+ if (base::win::GetVersion() >= base::win::VERSION_VISTA) { |
+ int value = TRUE; |
+ HRESULT result = DwmSetWindowAttribute( |
+ instance_->host_->hwnd(), |
+ DWMWA_TRANSITIONS_FORCEDISABLED, |
+ &value, |
+ sizeof(value)); |
+ } |
+ // Hide the window on escape, otherwise the window is visibly going to snap |
+ // back to the original location before we close it. |
+ // This behavior is specific to tab dragging, in that we generally wouldn't |
+ // want this functionality if we have other consumers using this API. |
+ instance_->host_->Hide(); |
+ } |
+ return CallNextHookEx(instance_->key_hook_, n_code, w_param, l_param); |
+} |
+ |
+// Called from OnNCActivate. |
+BOOL CALLBACK EnumChildWindowsForRedraw(HWND hwnd, LPARAM lparam) { |
+ DWORD process_id; |
+ GetWindowThreadProcessId(hwnd, &process_id); |
+ int flags = RDW_INVALIDATE | RDW_NOCHILDREN | RDW_FRAME; |
+ if (process_id == GetCurrentProcessId()) |
+ flags |= RDW_UPDATENOW; |
+ RedrawWindow(hwnd, NULL, NULL, flags); |
+ return TRUE; |
+} |
+ |
+bool GetMonitorAndRects(const RECT& rect, |
+ HMONITOR* monitor, |
+ gfx::Rect* monitor_rect, |
+ gfx::Rect* work_area) { |
+ DCHECK(monitor); |
+ DCHECK(monitor_rect); |
+ DCHECK(work_area); |
+ *monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTONULL); |
+ if (!*monitor) |
+ return false; |
+ MONITORINFO monitor_info = { 0 }; |
+ monitor_info.cbSize = sizeof(monitor_info); |
+ GetMonitorInfo(*monitor, &monitor_info); |
+ *monitor_rect = gfx::Rect(monitor_info.rcMonitor); |
+ *work_area = gfx::Rect(monitor_info.rcWork); |
+ return true; |
+} |
+ |
+struct FindOwnedWindowsData { |
+ HWND window; |
+ std::vector<Widget*> owned_widgets; |
+}; |
+ |
+BOOL CALLBACK FindOwnedWindowsCallback(HWND hwnd, LPARAM param) { |
+ // TODO(beng): resolve wrt aura. |
+#if !defined(USE_AURA) |
+ FindOwnedWindowsData* data = reinterpret_cast<FindOwnedWindowsData*>(param); |
+ if (GetWindow(hwnd, GW_OWNER) == data->window) { |
+ Widget* widget = Widget::GetWidgetForNativeView(hwnd); |
+ if (widget) |
+ data->owned_widgets.push_back(widget); |
+ } |
+#endif |
+ return TRUE; |
+} |
+ |
+// Enables or disables the menu item for the specified command and menu. |
+void EnableMenuItemByCommand(HMENU menu, UINT command, bool enabled) { |
+ UINT flags = MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_DISABLED | MF_GRAYED); |
+ EnableMenuItem(menu, command, flags); |
+} |
+ |
+// Callback used to notify child windows that the top level window received a |
+// DWMCompositionChanged message. |
+BOOL CALLBACK SendDwmCompositionChanged(HWND window, LPARAM param) { |
+ SendMessage(window, WM_DWMCOMPOSITIONCHANGED, 0, 0); |
+ return TRUE; |
+} |
+ |
+// See comments in OnNCPaint() for details of this struct. |
+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; |
+} |
+ |
+#if !defined(USE_AURA) |
+ |
+// Get the source HWND of the specified message. Depending on the message, the |
+// source HWND is encoded in either the WPARAM or the LPARAM value. |
+HWND GetControlHWNDForMessage(UINT message, WPARAM w_param, LPARAM l_param) { |
+ // Each of the following messages can be sent by a child HWND and must be |
+ // forwarded to its associated NativeControlWin for handling. |
+ switch (message) { |
+ case WM_NOTIFY: |
+ return reinterpret_cast<NMHDR*>(l_param)->hwndFrom; |
+ case WM_COMMAND: |
+ return reinterpret_cast<HWND>(l_param); |
+ case WM_CONTEXTMENU: |
+ return reinterpret_cast<HWND>(w_param); |
+ case WM_CTLCOLORBTN: |
+ case WM_CTLCOLORSTATIC: |
+ return reinterpret_cast<HWND>(l_param); |
+ } |
+ return NULL; |
+} |
+ |
+// Some messages may be sent to us by a child HWND. If this is the case, this |
+// function will forward those messages on to the object associated with the |
+// source HWND and return true, in which case the window procedure must not do |
+// any further processing of the message. If there is no associated |
+// ChildWindowMessageProcessor, the return value will be false and the WndProc |
+// can continue processing the message normally. |l_result| contains the result |
+// of the message processing by the control and must be returned by the WndProc |
+// if the return value is true. |
+bool ProcessChildWindowMessage(UINT message, |
+ WPARAM w_param, |
+ LPARAM l_param, |
+ LRESULT* l_result) { |
+ *l_result = 0; |
+ |
+ HWND control_hwnd = GetControlHWNDForMessage(message, w_param, l_param); |
+ if (IsWindow(control_hwnd)) { |
+ ChildWindowMessageProcessor* processor = |
+ ChildWindowMessageProcessor::Get(control_hwnd); |
+ if (processor) |
+ return processor->ProcessMessage(message, w_param, l_param, l_result); |
+ } |
+ |
+ return false; |
+} |
+ |
+#endif |
+ |
+// The thickness of an auto-hide taskbar in pixels. |
+const int kAutoHideTaskbarThicknessPx = 2; |
+ |
+// The touch id to be used for touch events coming in from Windows Aura |
+// Desktop. |
+const int kDesktopChromeAuraTouchId = 9; |
+ |
+} // namespace |
+ |
+// 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 Window's lock and unlock functions as it is created and destroyed. |
+// See documentation in those methods for the technique used. |
+// |
+// The lock only has an effect if the window was visible upon lock creation, as |
+// it doesn't guard against direct visiblility changes, and multiple locks may |
+// exist simultaneously to handle certain nested Windows messages. |
+// |
+// 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 HWNDMessageHandler::ScopedRedrawLock { |
+ public: |
+ explicit ScopedRedrawLock(HWNDMessageHandler* owner) |
+ : owner_(owner), |
+ hwnd_(owner_->hwnd()), |
+ was_visible_(owner_->IsVisible()), |
+ cancel_unlock_(false), |
+ force_(!(GetWindowLong(hwnd_, GWL_STYLE) & WS_CAPTION)) { |
+ if (was_visible_ && ::IsWindow(hwnd_)) |
+ owner_->LockUpdates(force_); |
+ } |
+ |
+ ~ScopedRedrawLock() { |
+ if (!cancel_unlock_ && was_visible_ && ::IsWindow(hwnd_)) |
+ owner_->UnlockUpdates(force_); |
+ } |
+ |
+ // Cancel the unlock operation, call this if the Widget is being destroyed. |
+ void CancelUnlockOperation() { cancel_unlock_ = true; } |
+ |
+ private: |
+ // The owner having its style changed. |
+ HWNDMessageHandler* owner_; |
+ // The owner's HWND, cached to avoid action after window destruction. |
+ HWND hwnd_; |
+ // Records the HWND visibility at the time of creation. |
+ bool was_visible_; |
+ // A flag indicating that the unlock operation was canceled. |
+ bool cancel_unlock_; |
+ // If true, perform the redraw lock regardless of Aero state. |
+ bool force_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ScopedRedrawLock); |
+}; |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// HWNDMessageHandler, public: |
+ |
+HWNDMessageHandler::HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate) |
+ : delegate_(delegate), |
+ ALLOW_THIS_IN_INITIALIZER_LIST( |
+ fullscreen_handler_(new FullscreenHandler)), |
+ ALLOW_THIS_IN_INITIALIZER_LIST(close_widget_factory_(this)), |
+ remove_standard_frame_(false), |
+ use_system_default_icon_(false), |
+ restore_focus_when_enabled_(false), |
+ restored_enabled_(false), |
+ previous_cursor_(NULL), |
+ active_mouse_tracking_flags_(0), |
+ is_right_mouse_pressed_on_caption_(false), |
+ lock_updates_count_(0), |
+ destroyed_(NULL), |
+ ignore_window_pos_changes_(false), |
+ ALLOW_THIS_IN_INITIALIZER_LIST(ignore_pos_changes_factory_(this)), |
+ last_monitor_(NULL), |
+ use_layered_buffer_(false), |
+ layered_alpha_(255), |
+ ALLOW_THIS_IN_INITIALIZER_LIST(paint_layered_window_factory_(this)), |
+ can_update_layered_window_(true), |
+ is_first_nccalc_(true) { |
+} |
+ |
+HWNDMessageHandler::~HWNDMessageHandler() { |
+ delegate_ = NULL; |
+ if (destroyed_ != NULL) |
+ *destroyed_ = true; |
+ // Prevent calls back into this class via WNDPROC now that we've been |
+ // destroyed. |
+ ClearUserData(); |
+} |
+ |
+void HWNDMessageHandler::Init(HWND parent, const gfx::Rect& bounds) { |
+ TRACE_EVENT0("views", "HWNDMessageHandler::Init"); |
+ GetMonitorAndRects(bounds.ToRECT(), &last_monitor_, &last_monitor_rect_, |
+ &last_work_area_); |
+ |
+ // Create the window. |
+ WindowImpl::Init(parent, bounds); |
+} |
+ |
+void HWNDMessageHandler::InitModalType(ui::ModalType modal_type) { |
+ if (modal_type == ui::MODAL_TYPE_NONE) |
+ return; |
+ // We implement modality by crawling up the hierarchy of windows starting |
+ // at the owner, disabling all of them so that they don't receive input |
+ // messages. |
+ HWND start = ::GetWindow(hwnd(), GW_OWNER); |
+ while (start) { |
+ ::EnableWindow(start, FALSE); |
+ start = ::GetParent(start); |
+ } |
+} |
+ |
+void HWNDMessageHandler::Close() { |
+ if (!IsWindow(hwnd())) |
+ return; // No need to do anything. |
+ |
+ // Let's hide ourselves right away. |
+ Hide(); |
+ |
+ // Modal dialog windows disable their owner windows; re-enable them now so |
+ // they can activate as foreground windows upon this window's destruction. |
+ RestoreEnabledIfNecessary(); |
+ |
+ if (!close_widget_factory_.HasWeakPtrs()) { |
+ // And we delay the close so that if we are called from an ATL callback, |
+ // we don't destroy the window before the callback returned (as the caller |
+ // may delete ourselves on destroy and the ATL callback would still |
+ // dereference us when the callback returns). |
+ MessageLoop::current()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&HWNDMessageHandler::CloseNow, |
+ close_widget_factory_.GetWeakPtr())); |
+ } |
+} |
+ |
+void HWNDMessageHandler::CloseNow() { |
+ // We may already have been destroyed if the selection resulted in a tab |
+ // switch which will have reactivated the browser window and closed us, so |
+ // we need to check to see if we're still a window before trying to destroy |
+ // ourself. |
+ if (IsWindow(hwnd())) |
+ DestroyWindow(hwnd()); |
+} |
+ |
+gfx::Rect HWNDMessageHandler::GetWindowBoundsInScreen() const { |
+ RECT r; |
+ GetWindowRect(hwnd(), &r); |
+ return gfx::Rect(r); |
+} |
+ |
+gfx::Rect HWNDMessageHandler::GetClientAreaBoundsInScreen() const { |
+ RECT r; |
+ GetClientRect(hwnd(), &r); |
+ POINT point = { r.left, r.top }; |
+ ClientToScreen(hwnd(), &point); |
+ return gfx::Rect(point.x, point.y, r.right - r.left, r.bottom - r.top); |
+} |
+ |
+gfx::Rect HWNDMessageHandler::GetRestoredBounds() const { |
+ // If we're in fullscreen mode, we've changed the normal bounds to the monitor |
+ // rect, so return the saved bounds instead. |
+ if (fullscreen_handler_->fullscreen()) |
+ return fullscreen_handler_->GetRestoreBounds(); |
+ |
+ gfx::Rect bounds; |
+ GetWindowPlacement(&bounds, NULL); |
+ return bounds; |
+} |
+ |
+void HWNDMessageHandler::GetWindowPlacement( |
+ gfx::Rect* bounds, |
+ ui::WindowShowState* show_state) const { |
+ WINDOWPLACEMENT wp; |
+ wp.length = sizeof(wp); |
+ const bool succeeded = !!::GetWindowPlacement(hwnd(), &wp); |
+ DCHECK(succeeded); |
+ |
+ if (bounds != NULL) { |
+ if (wp.showCmd == SW_SHOWNORMAL) { |
+ // GetWindowPlacement can return misleading position if a normalized |
+ // window was resized using Aero Snap feature (see comment 9 in bug |
+ // 36421). As a workaround, using GetWindowRect for normalized windows. |
+ const bool succeeded = GetWindowRect(hwnd(), &wp.rcNormalPosition) != 0; |
+ DCHECK(succeeded); |
+ |
+ *bounds = gfx::Rect(wp.rcNormalPosition); |
+ } else { |
+ MONITORINFO mi; |
+ mi.cbSize = sizeof(mi); |
+ const bool succeeded = GetMonitorInfo( |
+ MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONEAREST), &mi) != 0; |
+ DCHECK(succeeded); |
+ |
+ *bounds = gfx::Rect(wp.rcNormalPosition); |
+ // Convert normal position from workarea coordinates to screen |
+ // coordinates. |
+ bounds->Offset(mi.rcWork.left - mi.rcMonitor.left, |
+ mi.rcWork.top - mi.rcMonitor.top); |
+ } |
+ } |
+ |
+ if (show_state) { |
+ if (wp.showCmd == SW_SHOWMAXIMIZED) |
+ *show_state = ui::SHOW_STATE_MAXIMIZED; |
+ else if (wp.showCmd == SW_SHOWMINIMIZED) |
+ *show_state = ui::SHOW_STATE_MINIMIZED; |
+ else |
+ *show_state = ui::SHOW_STATE_NORMAL; |
+ } |
+} |
+ |
+void HWNDMessageHandler::SetBounds(const gfx::Rect& bounds) { |
+ LONG style = GetWindowLong(hwnd(), GWL_STYLE); |
+ if (style & WS_MAXIMIZE) |
+ SetWindowLong(hwnd(), GWL_STYLE, style & ~WS_MAXIMIZE); |
+ SetWindowPos(hwnd(), NULL, bounds.x(), bounds.y(), bounds.width(), |
+ bounds.height(), SWP_NOACTIVATE | SWP_NOZORDER); |
+} |
+ |
+void HWNDMessageHandler::SetSize(const gfx::Size& size) { |
+ SetWindowPos(hwnd(), NULL, 0, 0, size.width(), size.height(), |
+ SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE); |
+} |
+ |
+void HWNDMessageHandler::CenterWindow(const gfx::Size& size) { |
+ HWND parent = GetParent(hwnd()); |
+ if (!IsWindow(hwnd())) |
+ parent = ::GetWindow(hwnd(), GW_OWNER); |
+ ui::CenterAndSizeWindow(parent, hwnd(), size); |
+} |
+ |
+void HWNDMessageHandler::SetRegion(HRGN region) { |
+ SetWindowRgn(hwnd(), region, TRUE); |
+} |
+ |
+void HWNDMessageHandler::StackAbove(HWND other_hwnd) { |
+ SetWindowPos(hwnd(), other_hwnd, 0, 0, 0, 0, |
+ SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); |
+} |
+ |
+void HWNDMessageHandler::StackAtTop() { |
+ SetWindowPos(hwnd(), HWND_TOP, 0, 0, 0, 0, |
+ SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); |
+} |
+ |
+void HWNDMessageHandler::Show() { |
+ if (IsWindow(hwnd())) |
+ ShowWindowWithState(ui::SHOW_STATE_INACTIVE); |
+} |
+ |
+void HWNDMessageHandler::ShowWindowWithState(ui::WindowShowState show_state) { |
+ TRACE_EVENT0("views", "HWNDMessageHandler::ShowWindowWithState"); |
+ DWORD native_show_state; |
+ switch (show_state) { |
+ case ui::SHOW_STATE_INACTIVE: |
+ native_show_state = SW_SHOWNOACTIVATE; |
+ break; |
+ case ui::SHOW_STATE_MAXIMIZED: |
+ native_show_state = SW_SHOWMAXIMIZED; |
+ break; |
+ case ui::SHOW_STATE_MINIMIZED: |
+ native_show_state = SW_SHOWMINIMIZED; |
+ break; |
+ default: |
+ native_show_state = delegate_->GetInitialShowState(); |
+ break; |
+ } |
+ Show(native_show_state); |
+} |
+ |
+void HWNDMessageHandler::Show(int show_state) { |
+ ShowWindow(hwnd(), show_state); |
+ // When launched from certain programs like bash and Windows Live Messenger, |
+ // show_state is set to SW_HIDE, so we need to correct that condition. We |
+ // don't just change show_state to SW_SHOWNORMAL because MSDN says we must |
+ // always first call ShowWindow with the specified value from STARTUPINFO, |
+ // otherwise all future ShowWindow calls will be ignored (!!#@@#!). Instead, |
+ // we call ShowWindow again in this case. |
+ if (show_state == SW_HIDE) { |
+ show_state = SW_SHOWNORMAL; |
+ ShowWindow(hwnd(), show_state); |
+ } |
+ |
+ // We need to explicitly activate the window if we've been shown with a state |
+ // that should activate, because if we're opened from a desktop shortcut while |
+ // an existing window is already running it doesn't seem to be enough to use |
+ // one of these flags to activate the window. |
+ if (show_state == SW_SHOWNORMAL || show_state == SW_SHOWMAXIMIZED) |
+ Activate(); |
+ |
+ if (!delegate_->HandleInitialFocus()) |
+ SetInitialFocus(); |
+} |
+ |
+void HWNDMessageHandler::ShowMaximizedWithBounds(const gfx::Rect& bounds) { |
+ WINDOWPLACEMENT placement = { 0 }; |
+ placement.length = sizeof(WINDOWPLACEMENT); |
+ placement.showCmd = SW_SHOWMAXIMIZED; |
+ placement.rcNormalPosition = bounds.ToRECT(); |
+ SetWindowPlacement(hwnd(), &placement); |
+} |
+ |
+void HWNDMessageHandler::Hide() { |
+ if (IsWindow(hwnd())) { |
+ // NOTE: Be careful not to activate any windows here (for example, calling |
+ // ShowWindow(SW_HIDE) will automatically activate another window). This |
+ // code can be called while a window is being deactivated, and activating |
+ // another window will screw up the activation that is already in progress. |
+ SetWindowPos(hwnd(), NULL, 0, 0, 0, 0, |
+ SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | |
+ SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER); |
+ |
+ if (!GetParent(hwnd())) |
+ NotifyOwnedWindowsParentClosing(); |
+ } |
+} |
+ |
+void HWNDMessageHandler::Maximize() { |
+ ExecuteSystemMenuCommand(SC_MAXIMIZE); |
+} |
+ |
+void HWNDMessageHandler::Minimize() { |
+ ExecuteSystemMenuCommand(SC_MINIMIZE); |
+ delegate_->HandleNativeBlur(NULL); |
+} |
+ |
+void HWNDMessageHandler::Restore() { |
+ ExecuteSystemMenuCommand(SC_RESTORE); |
+} |
+ |
+void HWNDMessageHandler::Activate() { |
+ if (IsMinimized()) |
+ ::ShowWindow(hwnd(), SW_RESTORE); |
+ ::SetWindowPos(hwnd(), HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); |
+ SetForegroundWindow(hwnd()); |
+} |
+ |
+void HWNDMessageHandler::Deactivate() { |
+ HWND next_hwnd = ::GetNextWindow(hwnd(), GW_HWNDNEXT); |
+ if (next_hwnd) |
+ ::SetForegroundWindow(next_hwnd); |
+} |
+ |
+void HWNDMessageHandler::SetAlwaysOnTop(bool on_top) { |
+ ::SetWindowPos(hwnd(), on_top ? HWND_TOPMOST : HWND_NOTOPMOST, |
+ 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); |
+} |
+ |
+bool HWNDMessageHandler::IsVisible() const { |
+ return !!::IsWindowVisible(hwnd()); |
+} |
+ |
+bool HWNDMessageHandler::IsActive() const { |
+ return GetActiveWindow() == hwnd(); |
+} |
+ |
+bool HWNDMessageHandler::IsMinimized() const { |
+ return !!::IsIconic(hwnd()); |
+} |
+ |
+bool HWNDMessageHandler::IsMaximized() const { |
+ return !!::IsZoomed(hwnd()); |
+} |
+ |
+bool HWNDMessageHandler::RunMoveLoop(const gfx::Vector2d& drag_offset) { |
+ ReleaseCapture(); |
+ MoveLoopMouseWatcher watcher(this); |
+ SendMessage(hwnd(), WM_SYSCOMMAND, SC_MOVE | 0x0002, GetMessagePos()); |
+ // Windows doesn't appear to offer a way to determine whether the user |
+ // canceled the move or not. We assume if the user released the mouse it was |
+ // successful. |
+ return watcher.got_mouse_up(); |
+} |
+ |
+void HWNDMessageHandler::EndMoveLoop() { |
+ SendMessage(hwnd(), WM_CANCELMODE, 0, 0); |
+} |
+ |
+void HWNDMessageHandler::SendFrameChanged() { |
+ SetWindowPos(hwnd(), NULL, 0, 0, 0, 0, |
+ SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOCOPYBITS | |
+ SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOREPOSITION | |
+ SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER); |
+} |
+ |
+void HWNDMessageHandler::FlashFrame(bool flash) { |
+ FLASHWINFO fwi; |
+ fwi.cbSize = sizeof(fwi); |
+ fwi.hwnd = hwnd(); |
+ if (flash) { |
+ fwi.dwFlags = FLASHW_ALL; |
+ fwi.uCount = 4; |
+ fwi.dwTimeout = 0; |
+ } else { |
+ fwi.dwFlags = FLASHW_STOP; |
+ } |
+ FlashWindowEx(&fwi); |
+} |
+ |
+void HWNDMessageHandler::ClearNativeFocus() { |
+ ::SetFocus(hwnd()); |
+} |
+ |
+void HWNDMessageHandler::SetCapture() { |
+ DCHECK(!HasCapture()); |
+ ::SetCapture(hwnd()); |
+} |
+ |
+void HWNDMessageHandler::ReleaseCapture() { |
+ ::ReleaseCapture(); |
+} |
+ |
+bool HWNDMessageHandler::HasCapture() const { |
+ return ::GetCapture() == hwnd(); |
+} |
+ |
+void HWNDMessageHandler::SetVisibilityChangedAnimationsEnabled(bool enabled) { |
+ if (base::win::GetVersion() >= base::win::VERSION_VISTA) { |
+ int dwm_value = enabled ? FALSE : TRUE; |
+ DwmSetWindowAttribute( |
+ hwnd(), DWMWA_TRANSITIONS_FORCEDISABLED, &dwm_value, sizeof(dwm_value)); |
+ } |
+} |
+ |
+void HWNDMessageHandler::SetTitle(const string16& title) { |
+ SetWindowText(hwnd(), title.c_str()); |
+} |
+ |
+void HWNDMessageHandler::SetCursor(HCURSOR cursor) { |
+ if (cursor) { |
+ previous_cursor_ = ::SetCursor(cursor); |
+ } else if (previous_cursor_) { |
+ ::SetCursor(previous_cursor_); |
+ previous_cursor_ = NULL; |
+ } |
+} |
+ |
+void HWNDMessageHandler::FrameTypeChanged() { |
+ // Called when the frame type could possibly be changing (theme change or |
+ // DWM composition change). |
+ if (base::win::GetVersion() >= base::win::VERSION_VISTA) { |
+ // We need to toggle the rendering policy of the DWM/glass frame as we |
+ // change from opaque to glass. "Non client rendering enabled" means that |
+ // the DWM's glass non-client rendering is enabled, which is why |
+ // DWMNCRP_ENABLED is used for the native frame case. _DISABLED means the |
+ // DWM doesn't render glass, and so is used in the custom frame case. |
+ DWMNCRENDERINGPOLICY policy = !delegate_->IsUsingCustomFrame() ? |
+ DWMNCRP_ENABLED : DWMNCRP_DISABLED; |
+ DwmSetWindowAttribute(hwnd(), DWMWA_NCRENDERING_POLICY, |
+ &policy, sizeof(DWMNCRENDERINGPOLICY)); |
+ } |
+ |
+ ResetWindowRegion(true); |
+ |
+ // The non-client view needs to update too. |
+ delegate_->HandleFrameChanged(); |
+ |
+ // WM_DWMCOMPOSITIONCHANGED is only sent to top level windows, however we want |
+ // to notify our children too, since we can have MDI child windows who need to |
+ // update their appearance. |
+ EnumChildWindows(hwnd(), &SendDwmCompositionChanged, NULL); |
+} |
+ |
+void HWNDMessageHandler::SchedulePaintInRect(const gfx::Rect& rect) { |
+ if (use_layered_buffer_) { |
+ // We must update the back-buffer immediately, since Windows' handling of |
+ // invalid rects is somewhat mysterious. |
+ invalid_rect_.Union(rect); |
+ |
+ // In some situations, such as drag and drop, when Windows itself runs a |
+ // nested message loop our message loop appears to be starved and we don't |
+ // receive calls to DidProcessMessage(). This only seems to affect layered |
+ // windows, so we schedule a redraw manually using a task, since those never |
+ // seem to be starved. Also, wtf. |
+ if (!paint_layered_window_factory_.HasWeakPtrs()) { |
+ MessageLoop::current()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&HWNDMessageHandler::RedrawLayeredWindowContents, |
+ paint_layered_window_factory_.GetWeakPtr())); |
+ } |
+ } else { |
+ // InvalidateRect() expects client coordinates. |
+ RECT r = rect.ToRECT(); |
+ InvalidateRect(hwnd(), &r, FALSE); |
+ } |
+} |
+ |
+void HWNDMessageHandler::SetOpacity(BYTE opacity) { |
+ layered_alpha_ = opacity; |
+} |
+ |
+void HWNDMessageHandler::SetWindowIcons(const gfx::ImageSkia& window_icon, |
+ const gfx::ImageSkia& app_icon) { |
+ if (!window_icon.isNull()) { |
+ HICON windows_icon = IconUtil::CreateHICONFromSkBitmap( |
+ *window_icon.bitmap()); |
+ // We need to make sure to destroy the previous icon, otherwise we'll leak |
+ // these GDI objects until we crash! |
+ HICON old_icon = reinterpret_cast<HICON>( |
+ SendMessage(hwnd(), WM_SETICON, ICON_SMALL, |
+ reinterpret_cast<LPARAM>(windows_icon))); |
+ if (old_icon) |
+ DestroyIcon(old_icon); |
+ } |
+ if (!app_icon.isNull()) { |
+ HICON windows_icon = IconUtil::CreateHICONFromSkBitmap(*app_icon.bitmap()); |
+ HICON old_icon = reinterpret_cast<HICON>( |
+ SendMessage(hwnd(), WM_SETICON, ICON_BIG, |
+ reinterpret_cast<LPARAM>(windows_icon))); |
+ if (old_icon) |
+ DestroyIcon(old_icon); |
+ } |
+} |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// HWNDMessageHandler, InputMethodDelegate implementation: |
+ |
+void HWNDMessageHandler::DispatchKeyEventPostIME(const ui::KeyEvent& key) { |
+ SetMsgHandled(delegate_->HandleKeyEvent(key)); |
+} |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// HWNDMessageHandler, ui::WindowImpl overrides: |
+ |
+HICON HWNDMessageHandler::GetDefaultWindowIcon() const { |
+ if (use_system_default_icon_) |
+ return NULL; |
+ return ViewsDelegate::views_delegate ? |
+ ViewsDelegate::views_delegate->GetDefaultWindowIcon() : NULL; |
+} |
+ |
+LRESULT HWNDMessageHandler::OnWndProc(UINT message, |
+ WPARAM w_param, |
+ LPARAM l_param) { |
+ HWND window = hwnd(); |
+ LRESULT result = 0; |
+ |
+ if (delegate_ && delegate_->PreHandleMSG(message, w_param, l_param, &result)) |
+ return result; |
+ |
+#if !defined(USE_AURA) |
+ // First allow messages sent by child controls to be processed directly by |
+ // their associated views. If such a view is present, it will handle the |
+ // message *instead of* this NativeWidgetWin. |
+ if (ProcessChildWindowMessage(message, w_param, l_param, &result)) |
+ return result; |
+#endif |
+ |
+ // Otherwise we handle everything else. |
+ if (!ProcessWindowMessage(window, message, w_param, l_param, result)) |
+ result = DefWindowProc(window, message, w_param, l_param); |
+ |
+ // DefWindowProc() may have destroyed the window in a nested message loop. |
+ if (!::IsWindow(window)) |
+ return result; |
+ |
+ if (delegate_) |
+ delegate_->PostHandleMSG(message, w_param, l_param); |
+ if (message == WM_NCDESTROY) { |
+ MessageLoopForUI::current()->RemoveObserver(this); |
+ if (delegate_) |
+ delegate_->HandleDestroyed(); |
+ } |
+ |
+ // Only top level widget should store/restore focus. |
+ if (message == WM_ACTIVATE && delegate_->CanSaveFocus()) |
+ PostProcessActivateMessage(LOWORD(w_param)); |
+ if (message == WM_ENABLE && restore_focus_when_enabled_) { |
+ // This path should be executed only for top level as |
+ // restore_focus_when_enabled_ is set in PostProcessActivateMessage. |
+ DCHECK(delegate_->CanSaveFocus()); |
+ restore_focus_when_enabled_ = false; |
+ delegate_->RestoreFocusOnEnable(); |
+ } |
+ return result; |
+} |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// HWNDMessageHandler, MessageLoopForUI::Observer implementation: |
+ |
+base::EventStatus HWNDMessageHandler::WillProcessEvent( |
+ const base::NativeEvent& event) { |
+ return base::EVENT_CONTINUE; |
+} |
+ |
+void HWNDMessageHandler::DidProcessEvent(const base::NativeEvent& event) { |
+ RedrawInvalidRect(); |
+} |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// HWNDMessageHandler, private: |
+ |
+void HWNDMessageHandler::SetInitialFocus() { |
+ if (!(GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_TRANSPARENT) && |
+ !(GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)) { |
+ // The window does not get keyboard messages unless we focus it. |
+ SetFocus(hwnd()); |
+ } |
+} |
+ |
+void HWNDMessageHandler::PostProcessActivateMessage(int activation_state) { |
+ DCHECK(delegate_->CanSaveFocus()); |
+ if (WA_INACTIVE == activation_state) { |
+ // We might get activated/inactivated without being enabled, so we need to |
+ // clear restore_focus_when_enabled_. |
+ restore_focus_when_enabled_ = false; |
+ delegate_->SaveFocusOnDeactivate(); |
+ } else { |
+ // We must restore the focus after the message has been DefProc'ed as it |
+ // does set the focus to the last focused HWND. |
+ // Note that if the window is not enabled, we cannot restore the focus as |
+ // calling ::SetFocus on a child of the non-enabled top-window would fail. |
+ // This is the case when showing a modal dialog (such as 'open file', |
+ // 'print'...) from a different thread. |
+ // In that case we delay the focus restoration to when the window is enabled |
+ // again. |
+ if (!IsWindowEnabled(hwnd())) { |
+ DCHECK(!restore_focus_when_enabled_); |
+ restore_focus_when_enabled_ = true; |
+ return; |
+ } |
+ delegate_->RestoreFocusOnActivate(); |
+ } |
+} |
+ |
+void HWNDMessageHandler::RestoreEnabledIfNecessary() { |
+ if (delegate_->IsModal() && !restored_enabled_) { |
+ restored_enabled_ = true; |
+ // If we were run modally, we need to undo the disabled-ness we inflicted on |
+ // the owner's parent hierarchy. |
+ HWND start = ::GetWindow(hwnd(), GW_OWNER); |
+ while (start) { |
+ ::EnableWindow(start, TRUE); |
+ start = ::GetParent(start); |
+ } |
+ } |
+} |
+ |
+void HWNDMessageHandler::ExecuteSystemMenuCommand(int command) { |
+ if (command) |
+ SendMessage(hwnd(), WM_SYSCOMMAND, command, 0); |
+} |
+ |
+void HWNDMessageHandler::TrackMouseEvents(DWORD mouse_tracking_flags) { |
+ // Begin tracking mouse events for this HWND so that we get WM_MOUSELEAVE |
+ // when the user moves the mouse outside this HWND's bounds. |
+ if (active_mouse_tracking_flags_ == 0 || mouse_tracking_flags & TME_CANCEL) { |
+ if (mouse_tracking_flags & TME_CANCEL) { |
+ // We're about to cancel active mouse tracking, so empty out the stored |
+ // state. |
+ active_mouse_tracking_flags_ = 0; |
+ } else { |
+ active_mouse_tracking_flags_ = mouse_tracking_flags; |
+ } |
+ |
+ TRACKMOUSEEVENT tme; |
+ tme.cbSize = sizeof(tme); |
+ tme.dwFlags = mouse_tracking_flags; |
+ tme.hwndTrack = hwnd(); |
+ tme.dwHoverTime = 0; |
+ TrackMouseEvent(&tme); |
+ } else if (mouse_tracking_flags != active_mouse_tracking_flags_) { |
+ TrackMouseEvents(active_mouse_tracking_flags_ | TME_CANCEL); |
+ TrackMouseEvents(mouse_tracking_flags); |
+ } |
+} |
+ |
+void HWNDMessageHandler::ClientAreaSizeChanged() { |
+ RECT r = {0, 0, 0, 0}; |
+ if (delegate_->WidgetSizeIsClientSize()) { |
+ // TODO(beng): investigate whether this could be done |
+ // from other branch of if-else. |
+ if (!IsMinimized()) |
+ GetClientRect(hwnd(), &r); |
+ } else { |
+ GetWindowRect(hwnd(), &r); |
+ } |
+ gfx::Size s(std::max(0, static_cast<int>(r.right - r.left)), |
+ std::max(0, static_cast<int>(r.bottom - r.top))); |
+ delegate_->HandleClientSizeChanged(s); |
+ if (use_layered_buffer_) { |
+ layered_window_contents_.reset( |
+ new gfx::Canvas(s, ui::SCALE_FACTOR_100P, false)); |
+ } |
+} |
+ |
+gfx::Insets HWNDMessageHandler::GetClientAreaInsets() const { |
+ gfx::Insets insets; |
+ if (delegate_->GetClientAreaInsets(&insets)) |
+ return insets; |
+ DCHECK(insets.empty()); |
+ |
+ // Returning an empty Insets object causes the default handling in |
+ // NativeWidgetWin::OnNCCalcSize() to be invoked. |
+ if (!delegate_->IsWidgetWindow() || |
+ (!delegate_->IsUsingCustomFrame() && !remove_standard_frame_)) { |
+ return insets; |
+ } |
+ |
+ if (IsMaximized()) { |
+ // Windows automatically adds a standard width border to all sides when a |
+ // window is maximized. |
+ int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME); |
+ return gfx::Insets(border_thickness, border_thickness, border_thickness, |
+ border_thickness); |
+ } |
+ |
+ // The hack below doesn't seem to be necessary when the standard frame is |
+ // removed. |
+ if (remove_standard_frame_) |
+ return insets; |
+ // This is weird, but highly essential. If we don't offset the bottom edge |
+ // of the client rect, the window client area and window area will match, |
+ // and when returning to glass rendering mode from non-glass, the client |
+ // area will not paint black as transparent. This is because (and I don't |
+ // know why) the client area goes from matching the window rect to being |
+ // something else. If the client area is not the window rect in both |
+ // modes, the blackness doesn't occur. Because of this, we need to tell |
+ // the RootView to lay out to fit the window rect, rather than the client |
+ // rect when using the opaque frame. |
+ // Note: this is only required for non-fullscreen windows. Note that |
+ // fullscreen windows are in restored state, not maximized. |
+ return gfx::Insets(0, 0, fullscreen_handler_->fullscreen() ? 0 : 1, 0); |
+} |
+ |
+void HWNDMessageHandler::ResetWindowRegion(bool force) { |
+ // A native frame uses the native window region, and we don't want to mess |
+ // with it. |
+ if (!delegate_->IsUsingCustomFrame() || !delegate_->IsWidgetWindow()) { |
+ if (force) |
+ SetWindowRgn(hwnd(), NULL, TRUE); |
+ return; |
+ } |
+ |
+ // 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(hwnd(), current_rgn); |
+ |
+ CRect window_rect; |
+ GetWindowRect(hwnd(), &window_rect); |
+ HRGN new_region; |
+ if (IsMaximized()) { |
+ HMONITOR monitor = MonitorFromWindow(hwnd(), 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; |
+ delegate_->GetWindowMask( |
+ gfx::Size(window_rect.Width(), window_rect.Height()), &window_mask); |
+ new_region = gfx::CreateHRGNFromSkPath(window_mask); |
+ } |
+ |
+ if (current_rgn_result == ERROR || !EqualRgn(current_rgn, new_region)) { |
+ // SetWindowRgn takes ownership of the HRGN created by CreateNativeRegion. |
+ SetWindowRgn(hwnd(), new_region, TRUE); |
+ } else { |
+ DeleteObject(new_region); |
+ } |
+ |
+ DeleteObject(current_rgn); |
+} |
+ |
+LRESULT HWNDMessageHandler::DefWindowProcWithRedrawLock(UINT message, |
+ WPARAM w_param, |
+ LPARAM l_param) { |
+ ScopedRedrawLock lock(this); |
+ // The Widget and HWND can be destroyed in the call to DefWindowProc, so use |
+ // the |destroyed_| flag to avoid unlocking (and crashing) after destruction. |
+ bool destroyed = false; |
+ destroyed_ = &destroyed; |
+ LRESULT result = DefWindowProc(hwnd(), message, w_param, l_param); |
+ if (destroyed) |
+ lock.CancelUnlockOperation(); |
+ else |
+ destroyed_ = NULL; |
+ return result; |
+} |
+ |
+void HWNDMessageHandler::NotifyOwnedWindowsParentClosing() { |
+ FindOwnedWindowsData data; |
+ data.window = hwnd(); |
+ EnumThreadWindows(GetCurrentThreadId(), FindOwnedWindowsCallback, |
+ reinterpret_cast<LPARAM>(&data)); |
+ for (size_t i = 0; i < data.owned_widgets.size(); ++i) |
+ data.owned_widgets[i]->OnOwnerClosing(); |
+} |
+ |
+void HWNDMessageHandler::LockUpdates(bool force) { |
+ // We skip locked updates when Aero is on for two reasons: |
+ // 1. Because it isn't necessary |
+ // 2. Because toggling the WS_VISIBLE flag may occur while the GPU process is |
+ // attempting to present a child window's backbuffer onscreen. When these |
+ // two actions race with one another, the child window will either flicker |
+ // or will simply stop updating entirely. |
+ if ((force || !ui::win::IsAeroGlassEnabled()) && ++lock_updates_count_ == 1) { |
+ SetWindowLong(hwnd(), GWL_STYLE, |
+ GetWindowLong(hwnd(), GWL_STYLE) & ~WS_VISIBLE); |
+ } |
+} |
+ |
+void HWNDMessageHandler::UnlockUpdates(bool force) { |
+ if ((force || !ui::win::IsAeroGlassEnabled()) && --lock_updates_count_ <= 0) { |
+ SetWindowLong(hwnd(), GWL_STYLE, |
+ GetWindowLong(hwnd(), GWL_STYLE) | WS_VISIBLE); |
+ lock_updates_count_ = 0; |
+ } |
+} |
+ |
+void HWNDMessageHandler::RedrawInvalidRect() { |
+ if (!use_layered_buffer_) { |
+ RECT r = { 0, 0, 0, 0 }; |
+ if (GetUpdateRect(hwnd(), &r, FALSE) && !IsRectEmpty(&r)) { |
+ RedrawWindow(hwnd(), &r, NULL, |
+ RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOCHILDREN); |
+ } |
+ } |
+} |
+ |
+void HWNDMessageHandler::RedrawLayeredWindowContents() { |
+ if (invalid_rect_.IsEmpty()) |
+ return; |
+ |
+ // We need to clip to the dirty rect ourselves. |
+ layered_window_contents_->sk_canvas()->save(SkCanvas::kClip_SaveFlag); |
+ layered_window_contents_->ClipRect(invalid_rect_); |
+ delegate_->PaintLayeredWindow(layered_window_contents_.get()); |
+ layered_window_contents_->sk_canvas()->restore(); |
+ |
+ RECT wr; |
+ GetWindowRect(hwnd(), &wr); |
+ SIZE size = {wr.right - wr.left, wr.bottom - wr.top}; |
+ POINT position = {wr.left, wr.top}; |
+ HDC dib_dc = skia::BeginPlatformPaint(layered_window_contents_->sk_canvas()); |
+ POINT zero = {0, 0}; |
+ BLENDFUNCTION blend = {AC_SRC_OVER, 0, layered_alpha_, AC_SRC_ALPHA}; |
+ UpdateLayeredWindow(hwnd(), NULL, &position, &size, dib_dc, &zero, |
+ RGB(0xFF, 0xFF, 0xFF), &blend, ULW_ALPHA); |
+ invalid_rect_.SetRect(0, 0, 0, 0); |
+ skia::EndPlatformPaint(layered_window_contents_->sk_canvas()); |
+} |
+ |
+// Message handlers ------------------------------------------------------------ |
+ |
+void HWNDMessageHandler::OnActivateApp(BOOL active, DWORD thread_id) { |
+ if (delegate_->IsWidgetWindow() && !active && |
+ thread_id != GetCurrentThreadId()) { |
+ delegate_->HandleAppDeactivated(); |
+ // Also update the native frame if it is rendering the non-client area. |
+ if (!remove_standard_frame_ && !delegate_->IsUsingCustomFrame()) |
+ DefWindowProcWithRedrawLock(WM_NCACTIVATE, FALSE, 0); |
+ } |
+} |
+ |
+BOOL HWNDMessageHandler::OnAppCommand(HWND window, |
+ short command, |
+ WORD device, |
+ int keystate) { |
+ BOOL handled = !!delegate_->HandleAppCommand(command); |
+ SetMsgHandled(handled); |
+ // Make sure to return TRUE if the event was handled or in some cases the |
+ // system will execute the default handler which can cause bugs like going |
+ // forward or back two pages instead of one. |
+ return handled; |
+} |
+ |
+void HWNDMessageHandler::OnCancelMode() { |
+ delegate_->HandleCancelMode(); |
+ // Need default handling, otherwise capture and other things aren't canceled. |
+ SetMsgHandled(FALSE); |
+} |
+ |
+void HWNDMessageHandler::OnCaptureChanged(HWND window) { |
+ delegate_->HandleCaptureLost(); |
+} |
+ |
+void HWNDMessageHandler::OnClose() { |
+ delegate_->HandleClose(); |
+} |
+ |
+void HWNDMessageHandler::OnCommand(UINT notification_code, |
+ int command, |
+ HWND window) { |
+ // If the notification code is > 1 it means it is control specific and we |
+ // should ignore it. |
+ if (notification_code > 1 || delegate_->HandleAppCommand(command)) |
+ SetMsgHandled(FALSE); |
+} |
+ |
+LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) { |
+ use_layered_buffer_ = !!(window_ex_style() & WS_EX_LAYERED); |
+ |
+#if defined(USE_AURA) |
+ if (window_ex_style() & WS_EX_COMPOSITED) { |
+ if (base::win::GetVersion() >= base::win::VERSION_VISTA) { |
+ // This is part of the magic to emulate layered windows with Aura |
+ // see the explanation elsewere when we set WS_EX_COMPOSITED style. |
+ MARGINS margins = {-1,-1,-1,-1}; |
+ DwmExtendFrameIntoClientArea(hwnd(), &margins); |
+ } |
+ } |
+#endif |
+ |
+ fullscreen_handler_->set_hwnd(hwnd()); |
+ |
+ // This message initializes the window so that focus border are shown for |
+ // windows. |
+ SendMessage(hwnd(), |
+ WM_CHANGEUISTATE, |
+ MAKELPARAM(UIS_CLEAR, UISF_HIDEFOCUS), |
+ 0); |
+ |
+ // Bug 964884: detach the IME attached to this window. |
+ // We should attach IMEs only when we need to input CJK strings. |
+ ImmAssociateContextEx(hwnd(), NULL, 0); |
+ |
+ if (remove_standard_frame_) { |
+ SetWindowLong(hwnd(), GWL_STYLE, |
+ GetWindowLong(hwnd(), GWL_STYLE) & ~WS_CAPTION); |
+ SendFrameChanged(); |
+ } |
+ |
+ // Get access to a modifiable copy of the system menu. |
+ GetSystemMenu(hwnd(), false); |
+ |
+ if (base::win::GetVersion() >= base::win::VERSION_WIN7) |
+ RegisterTouchWindow(hwnd(), 0); |
+ |
+ // We need to allow the delegate to size its contents since the window may not |
+ // receive a size notification when its initial bounds are specified at window |
+ // creation time. |
+ ClientAreaSizeChanged(); |
+ |
+ // We need to add ourselves as a message loop observer so that we can repaint |
+ // aggressively if the contents of our window become invalid. Unfortunately |
+ // WM_PAINT messages are starved and we get flickery redrawing when resizing |
+ // if we do not do this. |
+ MessageLoopForUI::current()->AddObserver(this); |
+ |
+ delegate_->HandleCreate(); |
+ |
+ // TODO(beng): move more of NWW::OnCreate here. |
+ return 0; |
+} |
+ |
+void HWNDMessageHandler::OnDestroy() { |
+ delegate_->HandleDestroying(); |
+} |
+ |
+void HWNDMessageHandler::OnDisplayChange(UINT bits_per_pixel, |
+ const CSize& screen_size) { |
+ delegate_->HandleDisplayChange(); |
+} |
+ |
+LRESULT HWNDMessageHandler::OnDwmCompositionChanged(UINT msg, |
+ WPARAM w_param, |
+ LPARAM l_param) { |
+ if (!delegate_->IsWidgetWindow()) { |
+ SetMsgHandled(FALSE); |
+ return 0; |
+ } |
+ // For some reason, we need to hide the window while we're changing the frame |
+ // type only when we're changing it in response to WM_DWMCOMPOSITIONCHANGED. |
+ // If we don't, the client area will be filled with black. I'm suspecting |
+ // something skia-ey. |
+ // Frame type toggling caused by the user (e.g. switching theme) doesn't seem |
+ // to have this requirement. |
+ FrameTypeChanged(); |
+ return 0; |
+} |
+ |
+void HWNDMessageHandler::OnEnterSizeMove() { |
+ delegate_->HandleBeginWMSizeMove(); |
+ SetMsgHandled(FALSE); |
+} |
+ |
+LRESULT HWNDMessageHandler::OnEraseBkgnd(HDC dc) { |
+ // Needed to prevent resize flicker. |
+ return 1; |
+} |
+ |
+void HWNDMessageHandler::OnExitSizeMove() { |
+ delegate_->HandleEndWMSizeMove(); |
+ SetMsgHandled(FALSE); |
+} |
+ |
+void HWNDMessageHandler::OnGetMinMaxInfo(MINMAXINFO* minmax_info) { |
+ gfx::Size min_window_size; |
+ gfx::Size max_window_size; |
+ delegate_->GetMinMaxSize(&min_window_size, &max_window_size); |
+ |
+ // Add the native frame border size to the minimum and maximum size if the |
+ // view reports its size as the client size. |
+ if (delegate_->WidgetSizeIsClientSize()) { |
+ CRect client_rect, window_rect; |
+ GetClientRect(hwnd(), &client_rect); |
+ GetWindowRect(hwnd(), &window_rect); |
+ window_rect -= client_rect; |
+ min_window_size.Enlarge(window_rect.Width(), window_rect.Height()); |
+ if (!max_window_size.IsEmpty()) |
+ max_window_size.Enlarge(window_rect.Width(), window_rect.Height()); |
+ } |
+ minmax_info->ptMinTrackSize.x = min_window_size.width(); |
+ minmax_info->ptMinTrackSize.y = min_window_size.height(); |
+ if (max_window_size.width() || max_window_size.height()) { |
+ if (!max_window_size.width()) |
+ max_window_size.set_width(GetSystemMetrics(SM_CXMAXTRACK)); |
+ if (!max_window_size.height()) |
+ max_window_size.set_height(GetSystemMetrics(SM_CYMAXTRACK)); |
+ minmax_info->ptMaxTrackSize.x = max_window_size.width(); |
+ minmax_info->ptMaxTrackSize.y = max_window_size.height(); |
+ } |
+ SetMsgHandled(FALSE); |
+} |
+ |
+LRESULT HWNDMessageHandler::OnGetObject(UINT message, |
+ WPARAM w_param, |
+ LPARAM l_param) { |
+ LRESULT reference_result = static_cast<LRESULT>(0L); |
+ |
+ // Accessibility readers will send an OBJID_CLIENT message |
+ if (OBJID_CLIENT == l_param) { |
+ // Retrieve MSAA dispatch object for the root view. |
+ base::win::ScopedComPtr<IAccessible> root( |
+ delegate_->GetNativeViewAccessible()); |
+ |
+ // Create a reference that MSAA will marshall to the client. |
+ reference_result = LresultFromObject(IID_IAccessible, w_param, |
+ static_cast<IAccessible*>(root.Detach())); |
+ } |
+ |
+ return reference_result; |
+} |
+ |
+LRESULT HWNDMessageHandler::OnImeMessages(UINT message, |
+ WPARAM w_param, |
+ LPARAM l_param) { |
+ LRESULT result = 0; |
+ SetMsgHandled(delegate_->HandleIMEMessage( |
+ message, w_param, l_param, &result)); |
+ return result; |
+} |
+ |
+void HWNDMessageHandler::OnInitMenu(HMENU menu) { |
+ bool is_fullscreen = fullscreen_handler_->fullscreen(); |
+ bool is_minimized = IsMinimized(); |
+ bool is_maximized = IsMaximized(); |
+ bool is_restored = !is_fullscreen && !is_minimized && !is_maximized; |
+ |
+ ScopedRedrawLock lock(this); |
+ EnableMenuItemByCommand(menu, SC_RESTORE, is_minimized || is_maximized); |
+ EnableMenuItemByCommand(menu, SC_MOVE, is_restored); |
+ EnableMenuItemByCommand(menu, SC_SIZE, delegate_->CanResize() && is_restored); |
+ EnableMenuItemByCommand(menu, SC_MAXIMIZE, delegate_->CanMaximize() && |
+ !is_fullscreen && !is_maximized); |
+ EnableMenuItemByCommand(menu, SC_MINIMIZE, delegate_->CanMaximize() && |
+ !is_minimized); |
+} |
+ |
+void HWNDMessageHandler::OnInputLangChange(DWORD character_set, |
+ HKL input_language_id) { |
+ delegate_->HandleInputLanguageChange(character_set, input_language_id); |
+} |
+ |
+LRESULT HWNDMessageHandler::OnKeyEvent(UINT message, |
+ WPARAM w_param, |
+ LPARAM l_param) { |
+ MSG msg = { hwnd(), message, w_param, l_param, GetMessageTime() }; |
+ ui::KeyEvent key(msg, message == WM_CHAR); |
+ if (!delegate_->HandleUntranslatedKeyEvent(key)) |
+ DispatchKeyEventPostIME(key); |
+ return 0; |
+} |
+ |
+void HWNDMessageHandler::OnKillFocus(HWND focused_window) { |
+ delegate_->HandleNativeBlur(focused_window); |
+ SetMsgHandled(FALSE); |
+} |
+ |
+LRESULT HWNDMessageHandler::OnMouseActivate(UINT message, |
+ WPARAM w_param, |
+ LPARAM l_param) { |
+ // TODO(beng): resolve this with the GetWindowLong() check on the subsequent |
+ // line. |
+ if (delegate_->IsWidgetWindow()) |
+ return delegate_->CanActivate() ? MA_ACTIVATE : MA_NOACTIVATEANDEAT; |
+ if (GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE) |
+ return MA_NOACTIVATE; |
+ SetMsgHandled(FALSE); |
+ return MA_ACTIVATE; |
+} |
+ |
+LRESULT HWNDMessageHandler::OnMouseRange(UINT message, |
+ WPARAM w_param, |
+ LPARAM l_param) { |
+#if defined(USE_AURA) |
+ // We handle touch events on Windows Aura. Ignore synthesized mouse messages |
+ // from Windows. |
+ if (!touch_ids_.empty() || ui::IsMouseEventFromTouch(message)) |
+ return 0; |
+#endif |
+ if (message == WM_RBUTTONUP && is_right_mouse_pressed_on_caption_) { |
+ is_right_mouse_pressed_on_caption_ = false; |
+ ReleaseCapture(); |
+ // |point| is in window coordinates, but WM_NCHITTEST and TrackPopupMenu() |
+ // expect screen coordinates. |
+ CPoint screen_point(l_param); |
+ MapWindowPoints(hwnd(), HWND_DESKTOP, &screen_point, 1); |
+ w_param = SendMessage(hwnd(), WM_NCHITTEST, 0, |
+ MAKELPARAM(screen_point.x, screen_point.y)); |
+ if (w_param == HTCAPTION || w_param == HTSYSMENU) { |
+ ui::ShowSystemMenu(hwnd(), screen_point.x, screen_point.y); |
+ return 0; |
+ } |
+ } else if (message == WM_NCLBUTTONDOWN && delegate_->IsUsingCustomFrame()) { |
+ switch (w_param) { |
+ 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 |
+ // 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. |
+ // Combine |w_param| with common key state message flags. |
+ w_param |= base::win::IsCtrlPressed() ? MK_CONTROL : 0; |
+ w_param |= base::win::IsShiftPressed() ? MK_SHIFT : 0; |
+ } |
+ } |
+ } else if (message == WM_NCRBUTTONDOWN && |
+ (w_param == HTCAPTION || w_param == HTSYSMENU)) { |
+ is_right_mouse_pressed_on_caption_ = true; |
+ // We SetCapture() to ensure we only show the menu when the button |
+ // down and up are both on the caption. Note: this causes the button up to |
+ // be WM_RBUTTONUP instead of WM_NCRBUTTONUP. |
+ SetCapture(); |
+ } |
+ |
+ MSG msg = { hwnd(), message, w_param, l_param, GetMessageTime(), |
+ { GET_X_LPARAM(l_param), GET_Y_LPARAM(l_param) } }; |
+ ui::MouseEvent event(msg); |
+ if (!touch_ids_.empty() || ui::IsMouseEventFromTouch(message)) |
+ event.set_flags(event.flags() | ui::EF_FROM_TOUCH); |
+ |
+ if (!(event.flags() & ui::EF_IS_NON_CLIENT)) |
+ delegate_->HandleTooltipMouseMove(message, w_param, l_param); |
+ |
+ if (event.type() == ui::ET_MOUSE_MOVED && !HasCapture()) { |
+ // Windows only fires WM_MOUSELEAVE events if the application begins |
+ // "tracking" mouse events for a given HWND during WM_MOUSEMOVE events. |
+ // We need to call |TrackMouseEvents| to listen for WM_MOUSELEAVE. |
+ TrackMouseEvents((message == WM_NCMOUSEMOVE) ? |
+ TME_NONCLIENT | TME_LEAVE : TME_LEAVE); |
+ } else if (event.type() == ui::ET_MOUSE_EXITED) { |
+ // Reset our tracking flags so future mouse movement over this |
+ // NativeWidgetWin results in a new tracking session. Fall through for |
+ // OnMouseEvent. |
+ active_mouse_tracking_flags_ = 0; |
+ } else if (event.type() == ui::ET_MOUSEWHEEL) { |
+ // Reroute the mouse wheel to the window under the pointer if applicable. |
+ return (ui::RerouteMouseWheel(hwnd(), w_param, l_param) || |
+ delegate_->HandleMouseEvent(ui::MouseWheelEvent(msg))) ? 0 : 1; |
+ } |
+ |
+ bool handled = delegate_->HandleMouseEvent(event); |
+ if (!handled && message == WM_NCLBUTTONDOWN && w_param != HTSYSMENU && |
+ delegate_->IsUsingCustomFrame()) { |
+ // TODO(msw): Eliminate undesired painting, or re-evaluate this workaround. |
+ // DefWindowProc for WM_NCLBUTTONDOWN does weird non-client painting, so we |
+ // need to call it inside a ScopedRedrawLock. This may cause other negative |
+ // side-effects (ex/ stifling non-client mouse releases). |
+ DefWindowProcWithRedrawLock(message, w_param, l_param); |
+ handled = true; |
+ } |
+ |
+ SetMsgHandled(handled); |
+ return 0; |
+} |
+ |
+void HWNDMessageHandler::OnMove(const CPoint& point) { |
+ delegate_->HandleMove(); |
+ SetMsgHandled(FALSE); |
+} |
+ |
+void HWNDMessageHandler::OnMoving(UINT param, const RECT* new_bounds) { |
+ delegate_->HandleMove(); |
+} |
+ |
+LRESULT HWNDMessageHandler::OnNCActivate(BOOL active) { |
+ if (delegate_->CanActivate()) |
+ delegate_->HandleActivationChanged(!!active); |
+ |
+ if (!delegate_->IsWidgetWindow()) { |
+ SetMsgHandled(FALSE); |
+ return 0; |
+ } |
+ |
+ if (!delegate_->CanActivate()) |
+ return TRUE; |
+ |
+ // On activation, lift any prior restriction against rendering as inactive. |
+ bool inactive_rendering_disabled = delegate_->IsInactiveRenderingDisabled(); |
+ if (active && inactive_rendering_disabled) |
+ delegate_->EnableInactiveRendering(); |
+ |
+ if (delegate_->IsUsingCustomFrame()) { |
+ // TODO(beng, et al): Hack to redraw this window and child windows |
+ // synchronously upon activation. Not all child windows are redrawing |
+ // themselves leading to issues like http://crbug.com/74604 |
+ // We redraw out-of-process HWNDs asynchronously to avoid hanging the |
+ // whole app if a child HWND belonging to a hung plugin is encountered. |
+ RedrawWindow(hwnd(), NULL, NULL, |
+ RDW_NOCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW); |
+ EnumChildWindows(hwnd(), EnumChildWindowsForRedraw, NULL); |
+ } |
+ |
+ // The frame may need to redraw as a result of the activation change. |
+ // We can get WM_NCACTIVATE before we're actually visible. If we're not |
+ // visible, no need to paint. |
+ if (IsVisible()) |
+ delegate_->SchedulePaint(); |
+ |
+ // Avoid DefWindowProc non-client rendering over our custom frame on newer |
+ // Windows versions only (breaks taskbar activation indication on XP/Vista). |
+ if (delegate_->IsUsingCustomFrame() && |
+ base::win::GetVersion() > base::win::VERSION_VISTA) { |
+ SetMsgHandled(TRUE); |
+ return TRUE; |
+ } |
+ |
+ return DefWindowProcWithRedrawLock( |
+ WM_NCACTIVATE, inactive_rendering_disabled || active, 0); |
+} |
+ |
+LRESULT HWNDMessageHandler::OnNCCalcSize(BOOL mode, LPARAM l_param) { |
+ // We only override the default handling if we need to specify a custom |
+ // non-client edge width. Note that in most cases "no insets" means no |
+ // custom width, but in fullscreen mode or when the NonClientFrameView |
+ // requests it, we want a custom width of 0. |
+ |
+ // Let User32 handle the first nccalcsize for captioned windows |
+ // so it updates its internal structures (specifically caption-present) |
+ // Without this Tile & Cascade windows won't work. |
+ // See http://code.google.com/p/chromium/issues/detail?id=900 |
+ if (is_first_nccalc_) { |
+ is_first_nccalc_ = false; |
+ if (GetWindowLong(hwnd(), GWL_STYLE) & WS_CAPTION) { |
+ SetMsgHandled(FALSE); |
+ return 0; |
+ } |
+ } |
+ |
+ gfx::Insets insets = GetClientAreaInsets(); |
+ if (insets.empty() && !fullscreen_handler_->fullscreen() && |
+ !(mode && remove_standard_frame_)) { |
+ SetMsgHandled(FALSE); |
+ return 0; |
+ } |
+ |
+ RECT* client_rect = mode ? |
+ &(reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param)->rgrc[0]) : |
+ reinterpret_cast<RECT*>(l_param); |
+ client_rect->left += insets.left(); |
+ client_rect->top += insets.top(); |
+ client_rect->bottom -= insets.bottom(); |
+ client_rect->right -= insets.right(); |
+ if (IsMaximized()) { |
+ // Find all auto-hide taskbars along the screen edges and adjust in by the |
+ // thickness of the auto-hide taskbar on each such edge, so the window isn't |
+ // treated as a "fullscreen app", which would cause the taskbars to |
+ // disappear. |
+ HMONITOR monitor = MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONULL); |
+ if (!monitor) { |
+ // We might end up here if the window was previously minimized and the |
+ // user clicks on the taskbar button to restore it in the previously |
+ // maximized position. In that case WM_NCCALCSIZE is sent before the |
+ // window coordinates are restored to their previous values, so our |
+ // (left,top) would probably be (-32000,-32000) like all minimized |
+ // windows. So the above MonitorFromWindow call fails, but if we check |
+ // the window rect given with WM_NCCALCSIZE (which is our previous |
+ // restored window position) we will get the correct monitor handle. |
+ monitor = MonitorFromRect(client_rect, MONITOR_DEFAULTTONULL); |
+ if (!monitor) { |
+ // This is probably an extreme case that we won't hit, but if we don't |
+ // intersect any monitor, let us not adjust the client rect since our |
+ // window will not be visible anyway. |
+ return 0; |
+ } |
+ } |
+ if (GetTopmostAutoHideTaskbarForEdge(ABE_LEFT, monitor)) |
+ client_rect->left += kAutoHideTaskbarThicknessPx; |
+ if (GetTopmostAutoHideTaskbarForEdge(ABE_TOP, monitor)) { |
+ if (!delegate_->IsUsingCustomFrame()) { |
+ // Tricky bit. Due to a bug in DwmDefWindowProc()'s handling of |
+ // WM_NCHITTEST, having any nonclient area atop the window causes the |
+ // caption buttons to draw onscreen but not respond to mouse |
+ // hover/clicks. |
+ // So for a taskbar at the screen top, we can't push the |
+ // client_rect->top down; instead, we move the bottom up by one pixel, |
+ // which is the smallest change we can make and still get a client area |
+ // less than the screen size. This is visibly ugly, but there seems to |
+ // be no better solution. |
+ --client_rect->bottom; |
+ } else { |
+ client_rect->top += kAutoHideTaskbarThicknessPx; |
+ } |
+ } |
+ if (GetTopmostAutoHideTaskbarForEdge(ABE_RIGHT, monitor)) |
+ client_rect->right -= kAutoHideTaskbarThicknessPx; |
+ if (GetTopmostAutoHideTaskbarForEdge(ABE_BOTTOM, monitor)) |
+ client_rect->bottom -= kAutoHideTaskbarThicknessPx; |
+ |
+ // We cannot return WVR_REDRAW when there is nonclient area, or Windows |
+ // exhibits bugs where client pixels and child HWNDs are mispositioned by |
+ // the width/height of the upper-left nonclient area. |
+ return 0; |
+ } |
+ |
+ // If the window bounds change, we're going to relayout and repaint anyway. |
+ // Returning WVR_REDRAW avoids an extra paint before that of the old client |
+ // pixels in the (now wrong) location, and thus makes actions like resizing a |
+ // window from the left edge look slightly less broken. |
+ // We special case when left or top insets are 0, since these conditions |
+ // actually require another repaint to correct the layout after glass gets |
+ // turned on and off. |
+ if (insets.left() == 0 || insets.top() == 0) |
+ return 0; |
+ return mode ? WVR_REDRAW : 0; |
+} |
+ |
+LRESULT HWNDMessageHandler::OnNCHitTest(const CPoint& point) { |
+ if (!delegate_->IsWidgetWindow()) { |
+ SetMsgHandled(FALSE); |
+ return 0; |
+ } |
+ |
+ // If the DWM is rendering the window controls, we need to give the DWM's |
+ // default window procedure first chance to handle hit testing. |
+ if (!remove_standard_frame_ && !delegate_->IsUsingCustomFrame()) { |
+ LRESULT result; |
+ if (DwmDefWindowProc(hwnd(), WM_NCHITTEST, 0, |
+ MAKELPARAM(point.x, point.y), &result)) { |
+ return result; |
+ } |
+ } |
+ |
+ // First, give the NonClientView a chance to test the point to see if it |
+ // provides any of the non-client area. |
+ POINT temp = point; |
+ MapWindowPoints(HWND_DESKTOP, hwnd(), &temp, 1); |
+ int component = delegate_->GetNonClientComponent(gfx::Point(temp)); |
+ if (component != HTNOWHERE) |
+ return component; |
+ |
+ // Otherwise, we let Windows do all the native frame non-client handling for |
+ // us. |
+ SetMsgHandled(FALSE); |
+ return 0; |
+} |
+ |
+void HWNDMessageHandler::OnNCPaint(HRGN rgn) { |
+ // We only do non-client painting if we're not using the native frame. |
+ // It's required to avoid some native painting artifacts from appearing when |
+ // the window is resized. |
+ if (!delegate_->IsWidgetWindow() || !delegate_->IsUsingCustomFrame()) { |
+ SetMsgHandled(FALSE); |
+ return; |
+ } |
+ |
+ // 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(hwnd(), &window_rect); |
+ |
+ gfx::Size root_view_size = delegate_->GetRootViewSize(); |
+ if (gfx::Size(window_rect.Width(), window_rect.Height()) != root_view_size) { |
+ // 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(hwnd()); |
+ ClipState clip_state; |
+ clip_state.x = window_rect.left; |
+ clip_state.y = window_rect.top; |
+ clip_state.parent = hwnd(); |
+ clip_state.dc = dc; |
+ EnumChildWindows(hwnd(), &ClipDCToChild, |
+ reinterpret_cast<LPARAM>(&clip_state)); |
+ |
+ gfx::Rect old_paint_region = invalid_rect_; |
+ if (!old_paint_region.IsEmpty()) { |
+ // The root view has a region that needs to be painted. Include it in the |
+ // region we're going to paint. |
+ |
+ CRect old_paint_region_crect = old_paint_region.ToRECT(); |
+ CRect tmp = dirty_region; |
+ UnionRect(&dirty_region, &tmp, &old_paint_region_crect); |
+ } |
+ |
+ SchedulePaintInRect(gfx::Rect(dirty_region)); |
+ |
+ // gfx::CanvasSkiaPaint's 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. |
+ if (!delegate_->HandlePaintAccelerated(gfx::Rect(dirty_region))) { |
+ gfx::CanvasSkiaPaint canvas(dc, true, dirty_region.left, |
+ dirty_region.top, dirty_region.Width(), |
+ dirty_region.Height()); |
+ delegate_->HandlePaint(&canvas); |
+ } |
+ |
+ ReleaseDC(hwnd(), dc); |
+ // When using a custom frame, we want to avoid calling DefWindowProc() since |
+ // that may render artifacts. |
+ SetMsgHandled(delegate_->IsUsingCustomFrame()); |
+} |
+ |
+LRESULT HWNDMessageHandler::OnNCUAHDrawCaption(UINT message, |
+ 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(delegate_->IsUsingCustomFrame()); |
+ return 0; |
+} |
+ |
+LRESULT HWNDMessageHandler::OnNCUAHDrawFrame(UINT message, |
+ 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(delegate_->IsUsingCustomFrame()); |
+ return 0; |
+} |
+ |
+LRESULT HWNDMessageHandler::OnNotify(int w_param, NMHDR* l_param) { |
+ LRESULT l_result = 0; |
+ SetMsgHandled(delegate_->HandleTooltipNotify(w_param, l_param, &l_result)); |
+ return l_result; |
+} |
+ |
+void HWNDMessageHandler::OnPaint(HDC dc) { |
+ RECT dirty_rect; |
+ // Try to paint accelerated first. |
+ if (GetUpdateRect(hwnd(), &dirty_rect, FALSE) && |
+ !IsRectEmpty(&dirty_rect)) { |
+ if (delegate_->HandlePaintAccelerated(gfx::Rect(dirty_rect))) { |
+ ValidateRect(hwnd(), NULL); |
+ } else { |
+#if defined(USE_AURA) |
+ delegate_->HandlePaint(NULL); |
+#else |
+ scoped_ptr<gfx::CanvasPaint> canvas( |
+ gfx::CanvasPaint::CreateCanvasPaint(hwnd())); |
+ delegate_->HandlePaint(canvas->AsCanvas()); |
+#endif |
+ } |
+ } else { |
+ // TODO(msw): Find a better solution for this crbug.com/93530 workaround. |
+ // Some scenarios otherwise fail to validate minimized app/popup windows. |
+ ValidateRect(hwnd(), NULL); |
+ } |
+} |
+ |
+LRESULT HWNDMessageHandler::OnReflectedMessage(UINT message, |
+ WPARAM w_param, |
+ LPARAM l_param) { |
+ SetMsgHandled(FALSE); |
+ return 0; |
+} |
+ |
+LRESULT HWNDMessageHandler::OnSetCursor(UINT message, |
+ WPARAM w_param, |
+ LPARAM l_param) { |
+ const LRESULT result = DefWindowProcWithRedrawLock(message, w_param, l_param); |
+ // Invalidate the window to paint over any outdated window regions asap, as |
+ // using a RedrawLock for WM_SETCURSOR may show content through this window. |
+ if (delegate_->IsUsingCustomFrame() && !ui::win::IsAeroGlassEnabled()) |
+ InvalidateRect(hwnd(), NULL, FALSE); |
+ return result; |
+} |
+ |
+void HWNDMessageHandler::OnSetFocus(HWND last_focused_window) { |
+ delegate_->HandleNativeFocus(last_focused_window); |
+ SetMsgHandled(FALSE); |
+} |
+ |
+LRESULT HWNDMessageHandler::OnSetIcon(UINT size_type, HICON new_icon) { |
+ // Use a ScopedRedrawLock to avoid weird non-client painting. |
+ return DefWindowProcWithRedrawLock(WM_SETICON, size_type, |
+ reinterpret_cast<LPARAM>(new_icon)); |
+} |
+ |
+LRESULT HWNDMessageHandler::OnSetText(const wchar_t* text) { |
+ // Use a ScopedRedrawLock to avoid weird non-client painting. |
+ return DefWindowProcWithRedrawLock(WM_SETTEXT, NULL, |
+ reinterpret_cast<LPARAM>(text)); |
+} |
+ |
+void HWNDMessageHandler::OnSettingChange(UINT flags, const wchar_t* section) { |
+ if (!GetParent(hwnd()) && (flags == SPI_SETWORKAREA) && |
+ !delegate_->WillProcessWorkAreaChange()) { |
+ // Fire a dummy SetWindowPos() call, so we'll trip the code in |
+ // OnWindowPosChanging() below that notices work area changes. |
+ ::SetWindowPos(hwnd(), 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | |
+ SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE | SWP_NOOWNERZORDER); |
+ SetMsgHandled(TRUE); |
+ } else { |
+ if (flags == SPI_SETWORKAREA) |
+ delegate_->HandleWorkAreaChanged(); |
+ SetMsgHandled(FALSE); |
+ } |
+} |
+ |
+void HWNDMessageHandler::OnSize(UINT param, const CSize& size) { |
+ RedrawWindow(hwnd(), NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN); |
+ // ResetWindowRegion is going to trigger WM_NCPAINT. By doing it after we've |
+ // invoked OnSize we ensure the RootView has been laid out. |
+ ResetWindowRegion(false); |
+} |
+ |
+void HWNDMessageHandler::OnSysCommand(UINT notification_code, |
+ const CPoint& point) { |
+ if (!delegate_->IsWidgetWindow()) |
+ return; |
+ |
+ // 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; |
+ // Ignore size/move/maximize in fullscreen mode. |
+ if (fullscreen_handler_->fullscreen() && |
+ (((notification_code & sc_mask) == SC_SIZE) || |
+ ((notification_code & sc_mask) == SC_MOVE) || |
+ ((notification_code & sc_mask) == SC_MAXIMIZE))) |
+ return; |
+ if (delegate_->IsUsingCustomFrame()) { |
+ if ((notification_code & sc_mask) == SC_MINIMIZE || |
+ (notification_code & sc_mask) == SC_MAXIMIZE || |
+ (notification_code & sc_mask) == SC_RESTORE) { |
+ delegate_->ResetWindowControls(); |
+ } else if ((notification_code & sc_mask) == SC_MOVE || |
+ (notification_code & sc_mask) == SC_SIZE) { |
+ if (!IsVisible()) { |
+ // Circumvent ScopedRedrawLocks and force visibility before entering a |
+ // resize or move modal loop to get continuous sizing/moving feedback. |
+ SetWindowLong(hwnd(), GWL_STYLE, |
+ GetWindowLong(hwnd(), GWL_STYLE) | WS_VISIBLE); |
+ } |
+ } |
+ } |
+ |
+ // Handle SC_KEYMENU, which means that the user has pressed the ALT |
+ // key and released it, so we should focus the menu bar. |
+ if ((notification_code & sc_mask) == SC_KEYMENU && point.x == 0) { |
+ int modifiers = ui::EF_NONE; |
+ if (base::win::IsShiftPressed()) |
+ modifiers |= ui::EF_SHIFT_DOWN; |
+ if (base::win::IsCtrlPressed()) |
+ modifiers |= ui::EF_CONTROL_DOWN; |
+ // Retrieve the status of shift and control keys to prevent consuming |
+ // shift+alt keys, which are used by Windows to change input languages. |
+ ui::Accelerator accelerator(ui::KeyboardCodeForWindowsKeyCode(VK_MENU), |
+ modifiers); |
+ delegate_->HandleAccelerator(accelerator); |
+ return; |
+ } |
+ |
+ // If the delegate can't handle it, the system implementation will be called. |
+ if (!delegate_->HandleCommand(notification_code)) { |
+ DefWindowProc(hwnd(), WM_SYSCOMMAND, notification_code, |
+ MAKELPARAM(point.x, point.y)); |
+ } |
+} |
+ |
+void HWNDMessageHandler::OnThemeChanged() { |
+ ui::NativeThemeWin::instance()->CloseHandles(); |
+} |
+ |
+LRESULT HWNDMessageHandler::OnTouchEvent(UINT message, |
+ WPARAM w_param, |
+ LPARAM l_param) { |
+ int num_points = LOWORD(w_param); |
+ scoped_ptr<TOUCHINPUT[]> input(new TOUCHINPUT[num_points]); |
+ if (GetTouchInputInfoWrapper(reinterpret_cast<HTOUCHINPUT>(l_param), |
+ num_points, input.get(), sizeof(TOUCHINPUT))) { |
+ for (int i = 0; i < num_points; ++i) { |
+ ui::EventType touch_event_type = ui::ET_UNKNOWN; |
+ |
+ if (input[i].dwFlags & TOUCHEVENTF_DOWN) { |
+ touch_ids_.insert(input[i].dwID); |
+ touch_event_type = ui::ET_TOUCH_PRESSED; |
+ } else if (input[i].dwFlags & TOUCHEVENTF_UP) { |
+ touch_ids_.erase(input[i].dwID); |
+ touch_event_type = ui::ET_TOUCH_RELEASED; |
+ } else if (input[i].dwFlags & TOUCHEVENTF_MOVE) { |
+ touch_event_type = ui::ET_TOUCH_MOVED; |
+ } |
+ // Handle touch events only on Aura for now. |
+#if defined(USE_AURA) |
+ if (touch_event_type != ui::ET_UNKNOWN) { |
+ POINT point; |
+ point.x = TOUCH_COORD_TO_PIXEL(input[i].x); |
+ point.y = TOUCH_COORD_TO_PIXEL(input[i].y); |
+ |
+ ScreenToClient(hwnd(), &point); |
+ |
+ ui::TouchEvent event( |
+ touch_event_type, |
+ gfx::Point(point.x, point.y), |
+ kDesktopChromeAuraTouchId, |
+ base::TimeDelta::FromMilliseconds(input[i].dwTime)); |
+ delegate_->HandleTouchEvent(event); |
+ } |
+#endif |
+ } |
+ } |
+ CloseTouchInputHandle(reinterpret_cast<HTOUCHINPUT>(l_param)); |
+ SetMsgHandled(FALSE); |
+ return 0; |
+} |
+ |
+void HWNDMessageHandler::OnWindowPosChanging(WINDOWPOS* window_pos) { |
+ if (ignore_window_pos_changes_) { |
+ // If somebody's trying to toggle our visibility, change the nonclient area, |
+ // change our Z-order, or activate us, we should probably let it go through. |
+ if (!(window_pos->flags & ((IsVisible() ? SWP_HIDEWINDOW : SWP_SHOWWINDOW) | |
+ SWP_FRAMECHANGED)) && |
+ (window_pos->flags & (SWP_NOZORDER | SWP_NOACTIVATE))) { |
+ // Just sizing/moving the window; ignore. |
+ window_pos->flags |= SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW; |
+ window_pos->flags &= ~(SWP_SHOWWINDOW | SWP_HIDEWINDOW); |
+ } |
+ } else if (!GetParent(hwnd())) { |
+ CRect window_rect; |
+ HMONITOR monitor; |
+ gfx::Rect monitor_rect, work_area; |
+ if (GetWindowRect(hwnd(), &window_rect) && |
+ GetMonitorAndRects(window_rect, &monitor, &monitor_rect, &work_area)) { |
+ bool work_area_changed = (monitor_rect == last_monitor_rect_) && |
+ (work_area != last_work_area_); |
+ if (monitor && (monitor == last_monitor_) && |
+ ((fullscreen_handler_->fullscreen() && |
+ !fullscreen_handler_->metro_snap()) || |
+ work_area_changed)) { |
+ // A rect for the monitor we're on changed. Normally Windows notifies |
+ // us about this (and thus we're reaching here due to the SetWindowPos() |
+ // call in OnSettingChange() above), but with some software (e.g. |
+ // nVidia's nView desktop manager) the work area can change asynchronous |
+ // to any notification, and we're just sent a SetWindowPos() call with a |
+ // new (frequently incorrect) position/size. In either case, the best |
+ // response is to throw away the existing position/size information in |
+ // |window_pos| and recalculate it based on the new work rect. |
+ gfx::Rect new_window_rect; |
+ if (fullscreen_handler_->fullscreen()) { |
+ new_window_rect = monitor_rect; |
+ } else if (IsMaximized()) { |
+ new_window_rect = work_area; |
+ int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME); |
+ new_window_rect.Inset(-border_thickness, -border_thickness); |
+ } else { |
+ new_window_rect = gfx::Rect(window_rect); |
+ new_window_rect.AdjustToFit(work_area); |
+ } |
+ window_pos->x = new_window_rect.x(); |
+ window_pos->y = new_window_rect.y(); |
+ window_pos->cx = new_window_rect.width(); |
+ window_pos->cy = new_window_rect.height(); |
+ // WARNING! Don't set SWP_FRAMECHANGED here, it breaks moving the child |
+ // HWNDs for some reason. |
+ window_pos->flags &= ~(SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW); |
+ window_pos->flags |= SWP_NOCOPYBITS; |
+ |
+ // Now ignore all immediately-following SetWindowPos() changes. Windows |
+ // likes to (incorrectly) recalculate what our position/size should be |
+ // and send us further updates. |
+ ignore_window_pos_changes_ = true; |
+ DCHECK(!ignore_pos_changes_factory_.HasWeakPtrs()); |
+ MessageLoop::current()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&HWNDMessageHandler::StopIgnoringPosChanges, |
+ ignore_pos_changes_factory_.GetWeakPtr())); |
+ } |
+ last_monitor_ = monitor; |
+ last_monitor_rect_ = monitor_rect; |
+ last_work_area_ = work_area; |
+ } |
+ } |
+ |
+ if (ScopedFullscreenVisibility::IsHiddenForFullscreen(hwnd())) { |
+ // Prevent the window from being made visible if we've been asked to do so. |
+ // See comment in header as to why we might want this. |
+ window_pos->flags &= ~SWP_SHOWWINDOW; |
+ } |
+ |
+ SetMsgHandled(FALSE); |
+} |
+ |
+void HWNDMessageHandler::OnWindowPosChanged(WINDOWPOS* window_pos) { |
+ if (DidClientAreaSizeChange(window_pos)) |
+ ClientAreaSizeChanged(); |
+ if (remove_standard_frame_ && window_pos->flags & SWP_FRAMECHANGED && |
+ ui::win::IsAeroGlassEnabled()) { |
+ MARGINS m = {10, 10, 10, 10}; |
+ DwmExtendFrameIntoClientArea(hwnd(), &m); |
+ } |
+ if (window_pos->flags & SWP_SHOWWINDOW) |
+ delegate_->HandleVisibilityChanged(true); |
+ else if (window_pos->flags & SWP_HIDEWINDOW) |
+ delegate_->HandleVisibilityChanged(false); |
+ SetMsgHandled(FALSE); |
+} |
+ |
+} // namespace views |