diff options
author | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-14 20:53:42 +0000 |
---|---|---|
committer | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-14 20:53:42 +0000 |
commit | e2df0cd2aa14016fc5d10f964b94aafe5cac8246 (patch) | |
tree | 56a30c3fb05782f9ce698e6c4a4abdb5b8033a84 | |
parent | fbbaad5f3cc19ff3cc06bcadb5390064d1a4b077 (diff) | |
download | chromium_src-e2df0cd2aa14016fc5d10f964b94aafe5cac8246.zip chromium_src-e2df0cd2aa14016fc5d10f964b94aafe5cac8246.tar.gz chromium_src-e2df0cd2aa14016fc5d10f964b94aafe5cac8246.tar.bz2 |
fix bustage'
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@74851 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | views/widget/widget_win.cc | 1405 | ||||
-rw-r--r-- | views/widget/widget_win.h | 612 | ||||
-rw-r--r-- | views/widget/widget_win_unittest.cc | 82 |
3 files changed, 2099 insertions, 0 deletions
diff --git a/views/widget/widget_win.cc b/views/widget/widget_win.cc new file mode 100644 index 0000000..162d69f --- /dev/null +++ b/views/widget/widget_win.cc @@ -0,0 +1,1405 @@ +// Copyright (c) 2011 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 "views/widget/widget_win.h" + +#include <dwmapi.h> + +#include "base/string_util.h" +#include "base/win/windows_version.h" +#include "ui/base/keycodes/keyboard_code_conversion_win.h" +#include "ui/base/l10n/l10n_util_win.h" +#include "ui/base/system_monitor/system_monitor.h" +#include "ui/base/theme_provider.h" +#include "ui/base/view_prop.h" +#include "ui/base/win/hwnd_util.h" +#include "ui/gfx/canvas_skia.h" +#include "ui/gfx/native_theme_win.h" +#include "ui/gfx/path.h" +#include "views/accessibility/view_accessibility.h" +#include "views/controls/native_control_win.h" +#include "views/focus/accelerator_handler.h" +#include "views/focus/focus_util_win.h" +#include "views/views_delegate.h" +#include "views/widget/aero_tooltip_manager.h" +#include "views/widget/child_window_message_processor.h" +#include "views/widget/default_theme_provider.h" +#include "views/widget/drop_target_win.h" +#include "views/widget/root_view.h" +#include "views/widget/widget_delegate.h" +#include "views/widget/widget_utils.h" +#include "views/window/window_win.h" + +#pragma comment(lib, "dwmapi.lib") + +using ui::ViewProp; + +namespace { + +// Returns whether the specified window is the current active window. +bool IsWindowActive(HWND hwnd) { + WINDOWINFO info; + return ::GetWindowInfo(hwnd, &info) && + ((info.dwWindowStatus & WS_ACTIVECAPTION) != 0); +} + +} // namespace + +namespace views { + +// Property used to link the HWND to its RootView. +static const char* const kRootViewWindowProperty = "__ROOT_VIEW__"; + +// Links the HWND to it's Widget (as a Widget, not a WidgetWin). +static const char* const kWidgetKey = "__VIEWS_WIDGET__"; + +// static +bool WidgetWin::screen_reader_active_ = false; + +// A custom MSAA object id used to determine if a screen reader is actively +// listening for MSAA events. +#define OBJID_CUSTOM 1 + +RootView* GetRootViewForHWND(HWND hwnd) { + return reinterpret_cast<RootView*>( + ViewProp::GetValue(hwnd, kRootViewWindowProperty)); +} + +/////////////////////////////////////////////////////////////////////////////// +// WidgetWin, public + +WidgetWin::WidgetWin() + : close_widget_factory_(this), + active_mouse_tracking_flags_(0), + has_capture_(false), + use_layered_buffer_(true), + layered_alpha_(255), + delete_on_destroy_(true), + can_update_layered_window_(true), + last_mouse_event_was_move_(false), + is_mouse_down_(false), + is_window_(false), + restore_focus_when_enabled_(false), + delegate_(NULL), + accessibility_view_events_index_(-1), + accessibility_view_events_(kMaxAccessibilityViewEvents) { +} + +WidgetWin::~WidgetWin() { +} + +// static +WidgetWin* WidgetWin::GetWidget(HWND hwnd) { + // TODO(jcivelli): http://crbug.com/44499 We need a way to test that hwnd is + // associated with a WidgetWin (it might be a pure + // WindowImpl). + if (!WindowImpl::IsWindowImpl(hwnd)) + return NULL; + return reinterpret_cast<WidgetWin*>(ui::GetWindowUserData(hwnd)); +} + +// static +WidgetWin* WidgetWin::GetRootWidget(HWND hwnd) { + // First, check if the top-level window is a Widget. + HWND root = ::GetAncestor(hwnd, GA_ROOT); + if (!root) + return NULL; + + WidgetWin* widget = WidgetWin::GetWidget(root); + if (widget) + return widget; + + // Second, try to locate the last Widget window in the parent hierarchy. + HWND parent_hwnd = hwnd; + WidgetWin* parent_widget; + do { + parent_widget = WidgetWin::GetWidget(parent_hwnd); + if (parent_widget) { + widget = parent_widget; + parent_hwnd = ::GetAncestor(parent_hwnd, GA_PARENT); + } + } while (parent_hwnd != NULL && parent_widget != NULL); + + return widget; +} + +// static +bool WidgetWin::IsAeroGlassEnabled() { + if (base::win::GetVersion() < base::win::VERSION_VISTA) + return false; + // If composition is not enabled, we behave like on XP. + BOOL enabled = FALSE; + return SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled; +} + +void WidgetWin::SetUseLayeredBuffer(bool use_layered_buffer) { + if (use_layered_buffer_ == use_layered_buffer) + return; + + use_layered_buffer_ = use_layered_buffer; + if (!hwnd()) + return; + + if (use_layered_buffer_) + LayoutRootView(); + else + contents_.reset(NULL); +} + +View* WidgetWin::GetAccessibilityViewEventAt(int id) { + // Convert from MSAA child id. + id = -(id + 1); + DCHECK(id >= 0 && id < kMaxAccessibilityViewEvents); + return accessibility_view_events_[id]; +} + +int WidgetWin::AddAccessibilityViewEvent(View* view) { + accessibility_view_events_index_ = + (accessibility_view_events_index_ + 1) % kMaxAccessibilityViewEvents; + accessibility_view_events_[accessibility_view_events_index_] = view; + + // Convert to MSAA child id. + return -(accessibility_view_events_index_ + 1); +} + +void WidgetWin::ClearAccessibilityViewEvent(View* view) { + for (std::vector<View*>::iterator it = accessibility_view_events_.begin(); + it != accessibility_view_events_.end(); + ++it) { + if (*it == view) + *it = NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Widget implementation: + +void WidgetWin::Init(gfx::NativeView parent, const gfx::Rect& bounds) { + // Force creation of the RootView; otherwise, we may get a WM_SIZE after the + // window is created and before the root view is set up. + GetRootView(); + + // Create the window. + WindowImpl::Init(parent, bounds); + + // Attempt to detect screen readers by sending an event with our custom id. + if (!IsAccessibleWidget()) + NotifyWinEvent(EVENT_SYSTEM_ALERT, hwnd(), OBJID_CUSTOM, CHILDID_SELF); + + // See if the style has been overridden. + opaque_ = !(window_ex_style() & WS_EX_TRANSPARENT); + use_layered_buffer_ = (use_layered_buffer_ && + !!(window_ex_style() & WS_EX_LAYERED)); + + default_theme_provider_.reset(new DefaultThemeProvider()); + + props_.push_back(SetWindowSupportsRerouteMouseWheel(hwnd())); + + drop_target_ = new DropTargetWin(root_view_.get()); + + if ((window_style() & WS_CHILD) == 0 || + (WidgetWin::GetRootWidget(parent) == NULL && + parent != GetDesktopWindow())) { + // Top-level widgets and child widgets who do not have a top-level widget + // ancestor get a FocusManager. Child widgets parented to the desktop do not + // get a FocusManager because parenting to the desktop is the technique used + // to intentionally exclude a widget from the FocusManager hierarchy. + focus_manager_.reset(new FocusManager(this)); + } + + // Sets the RootView as a property, so the automation can introspect windows. + SetNativeWindowProperty(kRootViewWindowProperty, root_view_.get()); + + MessageLoopForUI::current()->AddObserver(this); + + // Windows special DWM window frame requires a special tooltip manager so + // that window controls in Chrome windows don't flicker when you move your + // mouse over them. See comment in aero_tooltip_manager.h. + if (GetThemeProvider()->ShouldUseNativeFrame()) { + tooltip_manager_.reset(new AeroTooltipManager(this)); + } else { + tooltip_manager_.reset(new TooltipManagerWin(this)); + } + + // 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); +} + +void WidgetWin::InitWithWidget(Widget* parent, const gfx::Rect& bounds) { + Init(parent->GetNativeView(), bounds); +} + +WidgetDelegate* WidgetWin::GetWidgetDelegate() { + return delegate_; +} + +void WidgetWin::SetWidgetDelegate(WidgetDelegate* delegate) { + delegate_ = delegate; +} + +void WidgetWin::SetContentsView(View* view) { + root_view_->SetContentsView(view); +} + +void WidgetWin::GetBounds(gfx::Rect* out, bool including_frame) const { + CRect crect; + if (including_frame) { + GetWindowRect(&crect); + *out = gfx::Rect(crect); + return; + } + + GetClientRect(&crect); + POINT p = {0, 0}; + ClientToScreen(hwnd(), &p); + out->SetRect(crect.left + p.x, crect.top + p.y, + crect.Width(), crect.Height()); +} + +void WidgetWin::SetBounds(const gfx::Rect& bounds) { + LONG style = GetWindowLong(GWL_STYLE); + if (style & WS_MAXIMIZE) + SetWindowLong(GWL_STYLE, style & ~WS_MAXIMIZE); + SetWindowPos(NULL, bounds.x(), bounds.y(), bounds.width(), bounds.height(), + SWP_NOACTIVATE | SWP_NOZORDER); +} + +void WidgetWin::MoveAbove(Widget* other) { + gfx::Rect bounds; + GetBounds(&bounds, false); + SetWindowPos(other->GetNativeView(), bounds.x(), bounds.y(), + bounds.width(), bounds.height(), SWP_NOACTIVATE); +} + +void WidgetWin::SetShape(gfx::NativeRegion region) { + SetWindowRgn(region, TRUE); +} + +void WidgetWin::Close() { + if (!IsWindow()) + return; // No need to do anything. + + // Let's hide ourselves right away. + Hide(); + + if (close_widget_factory_.empty()) { + // 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, + close_widget_factory_.NewRunnableMethod( + &WidgetWin::CloseNow)); + } +} + +void WidgetWin::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()) + DestroyWindow(hwnd()); +} + +void WidgetWin::Show() { + if (IsWindow()) + ShowWindow(SW_SHOWNOACTIVATE); +} + +void WidgetWin::Hide() { + if (IsWindow()) { + // 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(NULL, 0, 0, 0, 0, + SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | + SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER); + } +} + +gfx::NativeView WidgetWin::GetNativeView() const { + return WindowImpl::hwnd(); +} + +static BOOL CALLBACK EnumChildProcForRedraw(HWND hwnd, LPARAM lparam) { + DWORD process_id; + GetWindowThreadProcessId(hwnd, &process_id); + gfx::Rect invalid_rect = *reinterpret_cast<gfx::Rect*>(lparam); + + RECT window_rect; + GetWindowRect(hwnd, &window_rect); + invalid_rect.Offset(-window_rect.left, -window_rect.top); + + int flags = RDW_INVALIDATE | RDW_NOCHILDREN | RDW_FRAME; + if (process_id == GetCurrentProcessId()) + flags |= RDW_UPDATENOW; + RedrawWindow(hwnd, &invalid_rect.ToRECT(), NULL, flags); + return TRUE; +} + +void WidgetWin::PaintNow(const gfx::Rect& update_rect) { + if (use_layered_buffer_) { + PaintLayeredWindow(); + } else if (root_view_->NeedsPainting(false) && IsWindow()) { + if (!opaque_ && GetParent()) { + // We're transparent. Need to force painting to occur from our parent. + CRect parent_update_rect = update_rect.ToRECT(); + POINT location_in_parent = { 0, 0 }; + ClientToScreen(hwnd(), &location_in_parent); + ScreenToClient(GetParent(), &location_in_parent); + parent_update_rect.OffsetRect(location_in_parent); + RedrawWindow(GetParent(), parent_update_rect, NULL, + RDW_UPDATENOW | RDW_INVALIDATE | RDW_ALLCHILDREN); + } else { + // Paint child windows that are in a different process asynchronously. + // This prevents a hang in other processes from blocking this process. + + // Calculate the invalid rect in screen coordinates before the first + // RedrawWindow call to the parent HWND, since that will empty update_rect + // (which comes from a member variable) in the OnPaint call. + CRect screen_rect_temp; + GetWindowRect(&screen_rect_temp); + gfx::Rect screen_rect(screen_rect_temp); + gfx::Rect invalid_screen_rect = update_rect; + invalid_screen_rect.Offset(screen_rect.x(), screen_rect.y()); + + RedrawWindow(hwnd(), &update_rect.ToRECT(), NULL, + RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOCHILDREN); + + LPARAM lparam = reinterpret_cast<LPARAM>(&invalid_screen_rect); + EnumChildWindows(hwnd(), EnumChildProcForRedraw, lparam); + } + // As we were created with a style of WS_CLIPCHILDREN redraw requests may + // result in an empty paint rect in WM_PAINT (this'll happen if a + // child HWND completely contains the update _rect). In such a scenario + // RootView would never get a ProcessPaint and always think it needs to + // be painted (leading to a steady stream of RedrawWindow requests on every + // event). For this reason we tell RootView it doesn't need to paint + // here. + root_view_->ClearPaintRect(); + } +} + +void WidgetWin::SetOpacity(unsigned char opacity) { + layered_alpha_ = static_cast<BYTE>(opacity); +} + +void WidgetWin::SetAlwaysOnTop(bool on_top) { + if (on_top) + set_window_ex_style(window_ex_style() | WS_EX_TOPMOST); + else + set_window_ex_style(window_ex_style() & ~WS_EX_TOPMOST); +} + +RootView* WidgetWin::GetRootView() { + if (!root_view_.get()) { + // First time the root view is being asked for, create it now. + root_view_.reset(CreateRootView()); + } + return root_view_.get(); +} + +Widget* WidgetWin::GetRootWidget() const { + return GetRootWidget(hwnd()); +} + +bool WidgetWin::IsVisible() const { + return !!::IsWindowVisible(hwnd()); +} + +bool WidgetWin::IsActive() const { + return IsWindowActive(hwnd()); +} + +bool WidgetWin::IsAccessibleWidget() const { + return screen_reader_active_; +} + +TooltipManager* WidgetWin::GetTooltipManager() { + return tooltip_manager_.get(); +} + +void WidgetWin::GenerateMousePressedForView(View* view, + const gfx::Point& point) { + gfx::Point point_in_widget(point); + View::ConvertPointToWidget(view, &point_in_widget); + root_view_->SetMouseHandler(view); + ProcessMousePressed(point_in_widget.ToPOINT(), MK_LBUTTON, false, false); +} + +bool WidgetWin::GetAccelerator(int cmd_id, ui::Accelerator* accelerator) { + return false; +} + +Window* WidgetWin::GetWindow() { + return GetWindowImpl(hwnd()); +} + +const Window* WidgetWin::GetWindow() const { + return GetWindowImpl(hwnd()); +} + +void WidgetWin::SetNativeWindowProperty(const char* name, void* value) { + // Remove the existing property (if any). + for (ViewProps::iterator i = props_.begin(); i != props_.end(); ++i) { + if ((*i)->Key() == name) { + props_.erase(i); + break; + } + } + + if (value) + props_.push_back(new ViewProp(hwnd(), name, value)); +} + +void* WidgetWin::GetNativeWindowProperty(const char* name) { + return ViewProp::GetValue(hwnd(), name); +} + +ThemeProvider* WidgetWin::GetThemeProvider() const { + return GetWidgetThemeProvider(this); +} + +ThemeProvider* WidgetWin::GetDefaultThemeProvider() const { + return default_theme_provider_.get(); +} + +FocusManager* WidgetWin::GetFocusManager() { + if (focus_manager_.get()) + return focus_manager_.get(); + + WidgetWin* widget = static_cast<WidgetWin*>(GetRootWidget()); + if (widget && widget != this) { + // WidgetWin subclasses may override GetFocusManager(), for example for + // dealing with cases where the widget has been unparented. + return widget->GetFocusManager(); + } + return NULL; +} + +void WidgetWin::ViewHierarchyChanged(bool is_add, View *parent, + View *child) { + if (drop_target_.get()) + drop_target_->ResetTargetViewIfEquals(child); + + if (!is_add) + ClearAccessibilityViewEvent(child); +} + + +bool WidgetWin::ContainsNativeView(gfx::NativeView native_view) { + if (hwnd() == native_view) + return true; + + // Traverse the set of parents of the given view to determine if native_view + // is a descendant of this window. + HWND parent_window = ::GetParent(native_view); + HWND previous_child = native_view; + while (parent_window && parent_window != previous_child) { + if (hwnd() == parent_window) + return true; + previous_child = parent_window; + parent_window = ::GetParent(parent_window); + } + + // A views::NativeViewHost may contain the given native view, without it being + // an ancestor of hwnd(), so traverse the views::View hierarchy looking for + // such views. + return GetRootView()->ContainsNativeView(native_view); +} + +//////////////////////////////////////////////////////////////////////////////// +// MessageLoop::Observer + +void WidgetWin::WillProcessMessage(const MSG& msg) { +} + +void WidgetWin::DidProcessMessage(const MSG& msg) { + if (root_view_->NeedsPainting(true)) + PaintNow(root_view_->GetScheduledPaintRect()); +} + +//////////////////////////////////////////////////////////////////////////////// +// FocusTraversable + +FocusSearch* WidgetWin::GetFocusSearch() { + return root_view_->GetFocusSearch(); +} + +FocusTraversable* WidgetWin::GetFocusTraversableParent() { + // We are a proxy to the root view, so we should be bypassed when traversing + // up and as a result this should not be called. + NOTREACHED(); + return NULL; +} + +void WidgetWin::SetFocusTraversableParent(FocusTraversable* parent) { + root_view_->SetFocusTraversableParent(parent); +} + +View* WidgetWin::GetFocusTraversableParentView() { + // We are a proxy to the root view, so we should be bypassed when traversing + // up and as a result this should not be called. + NOTREACHED(); + return NULL; +} + +void WidgetWin::SetFocusTraversableParentView(View* parent_view) { + root_view_->SetFocusTraversableParentView(parent_view); +} + +/////////////////////////////////////////////////////////////////////////////// +// Message handlers + +void WidgetWin::OnActivate(UINT action, BOOL minimized, HWND window) { + SetMsgHandled(FALSE); +} + +void WidgetWin::OnActivateApp(BOOL active, DWORD thread_id) { + SetMsgHandled(FALSE); +} + +LRESULT WidgetWin::OnAppCommand(HWND window, short app_command, WORD device, + int keystate) { + SetMsgHandled(FALSE); + return 0; +} + +void WidgetWin::OnCancelMode() { +} + +void WidgetWin::OnCaptureChanged(HWND hwnd) { + if (has_capture_) { + if (is_mouse_down_) + root_view_->ProcessMouseDragCanceled(); + is_mouse_down_ = false; + has_capture_ = false; + } +} + +void WidgetWin::OnClose() { + Close(); +} + +void WidgetWin::OnCommand(UINT notification_code, int command_id, HWND window) { + SetMsgHandled(FALSE); +} + +LRESULT WidgetWin::OnCreate(CREATESTRUCT* create_struct) { + // Widget::GetWidgetFromNativeView expects the contents of this property + // to be of type Widget, so the cast is necessary. + SetNativeWindowProperty(kWidgetKey, static_cast<Widget*>(this)); + return 0; +} + +void WidgetWin::OnDestroy() { + if (drop_target_.get()) { + RevokeDragDrop(hwnd()); + drop_target_ = NULL; + } + + props_.reset(); +} + +void WidgetWin::OnDisplayChange(UINT bits_per_pixel, CSize screen_size) { + if (GetWidgetDelegate()) + GetWidgetDelegate()->DisplayChanged(); +} + +LRESULT WidgetWin::OnDwmCompositionChanged(UINT msg, + WPARAM w_param, + LPARAM l_param) { + SetMsgHandled(FALSE); + return 0; +} + +void WidgetWin::OnEndSession(BOOL ending, UINT logoff) { + SetMsgHandled(FALSE); +} + +void WidgetWin::OnEnterSizeMove() { + SetMsgHandled(FALSE); +} + +LRESULT WidgetWin::OnEraseBkgnd(HDC dc) { + // This is needed for magical win32 flicker ju-ju + return 1; +} + +void WidgetWin::OnExitMenuLoop(BOOL is_track_popup_menu) { + SetMsgHandled(FALSE); +} + +void WidgetWin::OnExitSizeMove() { + SetMsgHandled(FALSE); +} + +LRESULT WidgetWin::OnGetObject(UINT uMsg, 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( + ViewAccessibility::GetAccessibleForView(GetRootView())); + + // Create a reference that MSAA will marshall to the client. + reference_result = LresultFromObject(IID_IAccessible, w_param, + static_cast<IAccessible*>(root.Detach())); + } + + if (OBJID_CUSTOM == l_param) { + // An MSAA client requestes our custom id. Assume that we have detected an + // active windows screen reader. + OnScreenReaderDetected(); + + // Return with failure. + return static_cast<LRESULT>(0L); + } + + return reference_result; +} + +void WidgetWin::OnGetMinMaxInfo(MINMAXINFO* minmax_info) { + SetMsgHandled(FALSE); +} + +void WidgetWin::OnHScroll(int scroll_type, short position, HWND scrollbar) { + SetMsgHandled(FALSE); +} + +void WidgetWin::OnInitMenu(HMENU menu) { + SetMsgHandled(FALSE); +} + +void WidgetWin::OnInitMenuPopup(HMENU menu, + UINT position, + BOOL is_system_menu) { + SetMsgHandled(FALSE); +} + +LRESULT WidgetWin::OnKeyDown(UINT message, WPARAM w_param, LPARAM l_param) { + RootView* root_view = GetFocusedViewRootView(); + if (!root_view) + root_view = root_view_.get(); + + MSG msg; + MakeMSG(&msg, message, w_param, l_param); + SetMsgHandled(root_view->ProcessKeyEvent(KeyEvent(msg))); + return 0; +} + +LRESULT WidgetWin::OnKeyUp(UINT message, WPARAM w_param, LPARAM l_param) { + RootView* root_view = GetFocusedViewRootView(); + if (!root_view) + root_view = root_view_.get(); + + MSG msg; + MakeMSG(&msg, message, w_param, l_param); + SetMsgHandled(root_view->ProcessKeyEvent(KeyEvent(msg))); + return 0; +} + +void WidgetWin::OnKillFocus(HWND focused_window) { + GetFocusManager()->GetWidgetFocusManager()->OnWidgetFocusEvent( + this->GetNativeView(), + focused_window); + SetMsgHandled(FALSE); +} + +// TODO(pkasting): ORing the pressed/released button into the flags is _wrong_. +// It makes it impossible to tell which button was modified when multiple +// buttons are/were held down. We need to instead put the modified button into +// a separate member on the MouseEvent, then audit all consumers of MouseEvents +// to fix them to use the resulting values correctly. + +void WidgetWin::OnLButtonDown(UINT flags, const CPoint& point) { + ProcessMousePressed(point, flags | MK_LBUTTON, false, false); +} + +void WidgetWin::OnLButtonUp(UINT flags, const CPoint& point) { + ProcessMouseReleased(point, flags | MK_LBUTTON); +} + +void WidgetWin::OnLButtonDblClk(UINT flags, const CPoint& point) { + ProcessMousePressed(point, flags | MK_LBUTTON, true, false); +} + +void WidgetWin::OnMButtonDown(UINT flags, const CPoint& point) { + ProcessMousePressed(point, flags | MK_MBUTTON, false, false); +} + +void WidgetWin::OnMButtonUp(UINT flags, const CPoint& point) { + ProcessMouseReleased(point, flags | MK_MBUTTON); +} + +void WidgetWin::OnMButtonDblClk(UINT flags, const CPoint& point) { + ProcessMousePressed(point, flags | MK_MBUTTON, true, false); +} + +LRESULT WidgetWin::OnMouseActivate(HWND window, UINT hittest_code, + UINT message) { + SetMsgHandled(FALSE); + return MA_ACTIVATE; +} + +void WidgetWin::OnMouseMove(UINT flags, const CPoint& point) { + ProcessMouseMoved(point, flags, false); +} + +LRESULT WidgetWin::OnMouseLeave(UINT message, WPARAM w_param, LPARAM l_param) { + tooltip_manager_->OnMouseLeave(); + ProcessMouseExited(); + return 0; +} + +LRESULT WidgetWin::OnMouseWheel(UINT message, WPARAM w_param, LPARAM l_param) { + // Reroute the mouse-wheel to the window under the mouse pointer if + // applicable. + if (message == WM_MOUSEWHEEL && + views::RerouteMouseWheel(hwnd(), w_param, l_param)) { + return 0; + } + + int flags = GET_KEYSTATE_WPARAM(w_param); + short distance = GET_WHEEL_DELTA_WPARAM(w_param); + int x = GET_X_LPARAM(l_param); + int y = GET_Y_LPARAM(l_param); + MouseWheelEvent e(distance, x, y, Event::ConvertWindowsFlags(flags)); + return root_view_->ProcessMouseWheelEvent(e) ? 0 : 1; +} + +void WidgetWin::OnMove(const CPoint& point) { + SetMsgHandled(FALSE); +} + +void WidgetWin::OnMoving(UINT param, const LPRECT new_bounds) { +} + +LRESULT WidgetWin::OnMouseRange(UINT msg, WPARAM w_param, LPARAM l_param) { + tooltip_manager_->OnMouse(msg, w_param, l_param); + SetMsgHandled(FALSE); + return 0; +} + +LRESULT WidgetWin::OnNCActivate(BOOL active) { + SetMsgHandled(FALSE); + return 0; +} + +LRESULT WidgetWin::OnNCCalcSize(BOOL w_param, LPARAM l_param) { + SetMsgHandled(FALSE); + return 0; +} + +LRESULT WidgetWin::OnNCHitTest(const CPoint& pt) { + SetMsgHandled(FALSE); + return 0; +} + +void WidgetWin::OnNCLButtonDblClk(UINT flags, const CPoint& point) { + SetMsgHandled(ProcessMousePressed(point, flags | MK_LBUTTON, true, true)); +} + +void WidgetWin::OnNCLButtonDown(UINT flags, const CPoint& point) { + SetMsgHandled(ProcessMousePressed(point, flags | MK_LBUTTON, false, true)); +} + +void WidgetWin::OnNCLButtonUp(UINT flags, const CPoint& point) { + SetMsgHandled(FALSE); +} + +void WidgetWin::OnNCMButtonDblClk(UINT flags, const CPoint& point) { + SetMsgHandled(ProcessMousePressed(point, flags | MK_MBUTTON, true, true)); +} + +void WidgetWin::OnNCMButtonDown(UINT flags, const CPoint& point) { + SetMsgHandled(ProcessMousePressed(point, flags | MK_MBUTTON, false, true)); +} + +void WidgetWin::OnNCMButtonUp(UINT flags, const CPoint& point) { + SetMsgHandled(FALSE); +} + +LRESULT WidgetWin::OnNCMouseLeave(UINT uMsg, WPARAM w_param, LPARAM l_param) { + ProcessMouseExited(); + return 0; +} + +LRESULT WidgetWin::OnNCMouseMove(UINT flags, const CPoint& point) { + // NC points are in screen coordinates. + CPoint temp = point; + MapWindowPoints(HWND_DESKTOP, hwnd(), &temp, 1); + ProcessMouseMoved(temp, 0, true); + + // We need to process this message to stop Windows from drawing the window + // controls as the mouse moves over the title bar area when the window is + // maximized. + return 0; +} + +void WidgetWin::OnNCPaint(HRGN rgn) { + SetMsgHandled(FALSE); +} + +void WidgetWin::OnNCRButtonDblClk(UINT flags, const CPoint& point) { + SetMsgHandled(ProcessMousePressed(point, flags | MK_RBUTTON, true, true)); +} + +void WidgetWin::OnNCRButtonDown(UINT flags, const CPoint& point) { + SetMsgHandled(ProcessMousePressed(point, flags | MK_RBUTTON, false, true)); +} + +void WidgetWin::OnNCRButtonUp(UINT flags, const CPoint& point) { + SetMsgHandled(FALSE); +} + +LRESULT WidgetWin::OnNCUAHDrawCaption(UINT msg, + WPARAM w_param, + LPARAM l_param) { + SetMsgHandled(FALSE); + return 0; +} + +LRESULT WidgetWin::OnNCUAHDrawFrame(UINT msg, WPARAM w_param, LPARAM l_param) { + SetMsgHandled(FALSE); + return 0; +} + +LRESULT WidgetWin::OnNotify(int w_param, NMHDR* l_param) { + // We can be sent this message before the tooltip manager is created, if a + // subclass overrides OnCreate and creates some kind of Windows control there + // that sends WM_NOTIFY messages. + if (tooltip_manager_.get()) { + bool handled; + LRESULT result = tooltip_manager_->OnNotify(w_param, l_param, &handled); + SetMsgHandled(handled); + return result; + } + SetMsgHandled(FALSE); + return 0; +} + +void WidgetWin::OnPaint(HDC dc) { + root_view_->OnPaint(hwnd()); +} + +LRESULT WidgetWin::OnPowerBroadcast(DWORD power_event, DWORD data) { + ui::SystemMonitor* monitor = ui::SystemMonitor::Get(); + if (monitor) + monitor->ProcessWmPowerBroadcastMessage(power_event); + SetMsgHandled(FALSE); + return 0; +} + +void WidgetWin::OnRButtonDown(UINT flags, const CPoint& point) { + ProcessMousePressed(point, flags | MK_RBUTTON, false, false); +} + +void WidgetWin::OnRButtonUp(UINT flags, const CPoint& point) { + ProcessMouseReleased(point, flags | MK_RBUTTON); +} + +void WidgetWin::OnRButtonDblClk(UINT flags, const CPoint& point) { + ProcessMousePressed(point, flags | MK_RBUTTON, true, false); +} + +LRESULT WidgetWin::OnReflectedMessage(UINT msg, + WPARAM w_param, + LPARAM l_param) { + SetMsgHandled(FALSE); + return 0; +} + +void WidgetWin::OnSetFocus(HWND focused_window) { + GetFocusManager()->GetWidgetFocusManager()->OnWidgetFocusEvent( + focused_window, + this->GetNativeView()); + SetMsgHandled(FALSE); +} + +LRESULT WidgetWin::OnSetIcon(UINT size_type, HICON new_icon) { + SetMsgHandled(FALSE); + return 0; +} + +LRESULT WidgetWin::OnSetText(const wchar_t* text) { + SetMsgHandled(FALSE); + return 0; +} + +void WidgetWin::OnSettingChange(UINT flags, const wchar_t* section) { + if (flags == SPI_SETWORKAREA && GetWidgetDelegate()) + GetWidgetDelegate()->WorkAreaChanged(); + SetMsgHandled(FALSE); +} + +void WidgetWin::OnSize(UINT param, const CSize& size) { + LayoutRootView(); +} + +void WidgetWin::OnSysCommand(UINT notification_code, CPoint click) { +} + +void WidgetWin::OnThemeChanged() { + // Notify NativeTheme. + gfx::NativeTheme::instance()->CloseHandles(); +} + +void WidgetWin::OnFinalMessage(HWND window) { + if (delete_on_destroy_) + delete this; +} + +void WidgetWin::OnVScroll(int scroll_type, short position, HWND scrollbar) { + SetMsgHandled(FALSE); +} + +void WidgetWin::OnWindowPosChanging(WINDOWPOS* window_pos) { + SetMsgHandled(FALSE); +} + +void WidgetWin::OnWindowPosChanged(WINDOWPOS* window_pos) { + SetMsgHandled(FALSE); +} + +gfx::Size WidgetWin::GetRootViewSize() const { + CRect rect; + if (use_layered_buffer_) + GetWindowRect(&rect); + else + GetClientRect(&rect); + + return gfx::Size(rect.Width(), rect.Height()); +} + +/////////////////////////////////////////////////////////////////////////////// +// WidgetWin, protected: + +void WidgetWin::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); + } +} + +bool WidgetWin::ProcessMousePressed(const CPoint& point, + UINT flags, + bool dbl_click, + bool non_client) { + last_mouse_event_was_move_ = false; + // Windows gives screen coordinates for nonclient events, while the RootView + // expects window coordinates; convert if necessary. + gfx::Point converted_point(point); + if (non_client) + View::ConvertPointToView(NULL, root_view_.get(), &converted_point); + MouseEvent mouse_pressed(ui::ET_MOUSE_PRESSED, + converted_point.x(), + converted_point.y(), + (dbl_click ? ui::EF_IS_DOUBLE_CLICK : 0) | + (non_client ? ui::EF_IS_NON_CLIENT : 0) | + Event::ConvertWindowsFlags(flags)); + if (root_view_->OnMousePressed(mouse_pressed)) { + is_mouse_down_ = true; + if (!has_capture_) { + SetCapture(); + has_capture_ = true; + } + return true; + } + return false; +} + +void WidgetWin::ProcessMouseDragged(const CPoint& point, UINT flags) { + last_mouse_event_was_move_ = false; + MouseEvent mouse_drag(ui::ET_MOUSE_DRAGGED, + point.x, + point.y, + Event::ConvertWindowsFlags(flags)); + root_view_->OnMouseDragged(mouse_drag); +} + +void WidgetWin::ProcessMouseReleased(const CPoint& point, UINT flags) { + last_mouse_event_was_move_ = false; + MouseEvent mouse_up(ui::ET_MOUSE_RELEASED, + point.x, + point.y, + Event::ConvertWindowsFlags(flags)); + // Release the capture first, that way we don't get confused if + // OnMouseReleased blocks. + if (has_capture_ && ReleaseCaptureOnMouseReleased()) { + has_capture_ = false; + ReleaseCapture(); + } + is_mouse_down_ = false; + root_view_->OnMouseReleased(mouse_up, false); +} + +void WidgetWin::ProcessMouseMoved(const CPoint &point, UINT flags, + bool is_nonclient) { + // 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. + if (!has_capture_) + TrackMouseEvents(is_nonclient ? TME_NONCLIENT | TME_LEAVE : TME_LEAVE); + if (has_capture_ && is_mouse_down_) { + ProcessMouseDragged(point, flags); + } else { + gfx::Point screen_loc(point); + View::ConvertPointToScreen(root_view_.get(), &screen_loc); + if (last_mouse_event_was_move_ && last_mouse_move_x_ == screen_loc.x() && + last_mouse_move_y_ == screen_loc.y()) { + // Don't generate a mouse event for the same location as the last. + return; + } + last_mouse_move_x_ = screen_loc.x(); + last_mouse_move_y_ = screen_loc.y(); + last_mouse_event_was_move_ = true; + MouseEvent mouse_move(ui::ET_MOUSE_MOVED, + point.x, + point.y, + Event::ConvertWindowsFlags(flags)); + root_view_->OnMouseMoved(mouse_move); + } +} + +void WidgetWin::ProcessMouseExited() { + last_mouse_event_was_move_ = false; + root_view_->ProcessOnMouseExited(); + // Reset our tracking flag so that future mouse movement over this WidgetWin + // results in a new tracking session. + active_mouse_tracking_flags_ = 0; +} + +void WidgetWin::LayoutRootView() { + gfx::Size size(GetRootViewSize()); + + if (use_layered_buffer_) + SizeContents(size); + + // Resizing changes the size of the view hierarchy and thus forces a + // complete relayout. + root_view_->SetBounds(0, 0, size.width(), size.height()); + root_view_->SchedulePaint(); + + if (use_layered_buffer_) + PaintNow(gfx::Rect(0, 0, size.width(), size.height())); +} + +void WidgetWin::OnScreenReaderDetected() { + screen_reader_active_ = true; +} + +bool WidgetWin::ReleaseCaptureOnMouseReleased() { + return true; +} + +RootView* WidgetWin::CreateRootView() { + return new RootView(this); +} + +/////////////////////////////////////////////////////////////////////////////// +// WidgetWin, private: + +// static +Window* WidgetWin::GetWindowImpl(HWND hwnd) { + // NOTE: we can't use GetAncestor here as constrained windows are a Window, + // but not a top level window. + HWND parent = hwnd; + while (parent) { + WidgetWin* widget = + reinterpret_cast<WidgetWin*>(ui::GetWindowUserData(parent)); + if (widget && widget->is_window_) + return static_cast<WindowWin*>(widget); + parent = ::GetParent(parent); + } + return NULL; +} + +void WidgetWin::SizeContents(const gfx::Size& window_size) { + contents_.reset(new gfx::CanvasSkia(window_size.width(), + window_size.height(), + false)); +} + +void WidgetWin::PaintLayeredWindow() { + // Painting monkeys with our cliprect, so we need to save it so that the + // call to UpdateLayeredWindow updates the entire window, not just the + // cliprect. + contents_->save(SkCanvas::kClip_SaveFlag); + gfx::Rect dirty_rect = root_view_->GetScheduledPaintRect(); + contents_->ClipRectInt(dirty_rect.x(), dirty_rect.y(), dirty_rect.width(), + dirty_rect.height()); + root_view_->ProcessPaint(contents_.get()); + contents_->restore(); + + UpdateWindowFromContents(contents_->getTopPlatformDevice().getBitmapDC()); +} + +void WidgetWin::UpdateWindowFromContents(HDC dib_dc) { + DCHECK(use_layered_buffer_); + if (can_update_layered_window_) { + CRect wr; + GetWindowRect(&wr); + CSize size(wr.right - wr.left, wr.bottom - wr.top); + CPoint zero_origin(0, 0); + CPoint window_position = wr.TopLeft(); + + BLENDFUNCTION blend = {AC_SRC_OVER, 0, layered_alpha_, AC_SRC_ALPHA}; + UpdateLayeredWindow( + hwnd(), NULL, &window_position, &size, dib_dc, &zero_origin, + RGB(0xFF, 0xFF, 0xFF), &blend, ULW_ALPHA); + } +} + +RootView* WidgetWin::GetFocusedViewRootView() { + FocusManager* focus_manager = GetFocusManager(); + if (!focus_manager) { + NOTREACHED(); + return NULL; + } + View* focused_view = focus_manager->GetFocusedView(); + if (!focused_view) + return NULL; + return focused_view->GetRootView(); +} + +// 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. +static 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; +} + +HICON WidgetWin::GetDefaultWindowIcon() const { + if (ViewsDelegate::views_delegate) + return ViewsDelegate::views_delegate->GetDefaultWindowIcon(); + 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. +static 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; +} + +LRESULT WidgetWin::OnWndProc(UINT message, WPARAM w_param, LPARAM l_param) { + HWND window = hwnd(); + LRESULT result = 0; + + // 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 WidgetWin. + if (ProcessChildWindowMessage(message, w_param, l_param, &result)) + return result; + + // Otherwise we handle everything else. + if (!ProcessWindowMessage(window, message, w_param, l_param, result)) + result = DefWindowProc(window, message, w_param, l_param); + if (message == WM_NCDESTROY) { + MessageLoopForUI::current()->RemoveObserver(this); + OnFinalMessage(window); + } + if (message == WM_ACTIVATE) + PostProcessActivateMessage(this, LOWORD(w_param)); + if (message == WM_ENABLE && restore_focus_when_enabled_) { + restore_focus_when_enabled_ = false; + focus_manager_->RestoreFocusedView(); + } + return result; +} + +// static +void WidgetWin::PostProcessActivateMessage(WidgetWin* widget, + int activation_state) { + if (!widget->focus_manager_.get()) { + NOTREACHED(); + return; + } + if (WA_INACTIVE == activation_state) { + // We might get activated/inactivated without being enabled, so we need to + // clear restore_focus_when_enabled_. + widget->restore_focus_when_enabled_ = false; + widget->focus_manager_->StoreFocusedView(); + } 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(widget->GetNativeView())) { + DCHECK(!widget->restore_focus_when_enabled_); + widget->restore_focus_when_enabled_ = true; + return; + } + widget->focus_manager_->RestoreFocusedView(); + } +} + +void WidgetWin::MakeMSG(MSG* msg, UINT message, WPARAM w_param, + LPARAM l_param) const { + msg->hwnd = hwnd(); + msg->message = message; + msg->wParam = w_param; + msg->lParam = l_param; + msg->time = 0; + msg->pt.x = msg->pt.y = 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// Widget, public: + +// static +Widget* Widget::CreatePopupWidget(TransparencyParam transparent, + EventsParam accept_events, + DeleteParam delete_on_destroy, + MirroringParam mirror_in_rtl) { + WidgetWin* popup = new WidgetWin; + DWORD ex_style = WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE; + if (mirror_in_rtl == MirrorOriginInRTL) + ex_style |= l10n_util::GetExtendedTooltipStyles(); + if (transparent == Transparent) + ex_style |= WS_EX_LAYERED; + if (accept_events != AcceptEvents) + ex_style |= WS_EX_TRANSPARENT; + popup->set_window_style(WS_POPUP); + popup->set_window_ex_style(ex_style); + popup->set_delete_on_destroy(delete_on_destroy == DeleteOnDestroy); + return popup; +} + +static BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM l_param) { + RootView* root_view = GetRootViewForHWND(hwnd); + if (root_view) { + *reinterpret_cast<RootView**>(l_param) = root_view; + return FALSE; // Stop enumerating. + } + return TRUE; // Keep enumerating. +} + +// static +RootView* Widget::FindRootView(HWND hwnd) { + RootView* root_view = GetRootViewForHWND(hwnd); + if (root_view) + return root_view; + + // Enumerate all children and check if they have a RootView. + EnumChildWindows(hwnd, EnumChildProc, reinterpret_cast<LPARAM>(&root_view)); + + return root_view; +} + +// Enumerate child windows as they could have RootView distinct from +// the HWND's root view. +BOOL CALLBACK EnumAllRootViewsChildProc(HWND hwnd, LPARAM l_param) { + RootView* root_view = GetRootViewForHWND(hwnd); + if (root_view) { + std::set<RootView*>* root_views_set = + reinterpret_cast<std::set<RootView*>*>(l_param); + root_views_set->insert(root_view); + } + return TRUE; // Keep enumerating. +} + +void Widget::FindAllRootViews(HWND window, + std::vector<RootView*>* root_views) { + RootView* root_view = GetRootViewForHWND(window); + std::set<RootView*> root_views_set; + if (root_view) + root_views_set.insert(root_view); + // Enumerate all children and check if they have a RootView. + EnumChildWindows(window, EnumAllRootViewsChildProc, + reinterpret_cast<LPARAM>(&root_views_set)); + root_views->clear(); + root_views->reserve(root_views_set.size()); + for (std::set<RootView*>::iterator it = root_views_set.begin(); + it != root_views_set.end(); + ++it) + root_views->push_back(*it); +} + +//////////////////////////////////////////////////////////////////////////////// +// Widget, public: + +// static +Widget* Widget::GetWidgetFromNativeView(gfx::NativeView native_view) { + return IsWindow(native_view) ? + reinterpret_cast<Widget*>(ViewProp::GetValue(native_view, kWidgetKey)) : + NULL; +} + +// static +Widget* Widget::GetWidgetFromNativeWindow(gfx::NativeWindow native_window) { + return Widget::GetWidgetFromNativeView(native_window); +} + +// static +void Widget::NotifyLocaleChanged() { + NOTIMPLEMENTED(); +} + +} // namespace views diff --git a/views/widget/widget_win.h b/views/widget/widget_win.h new file mode 100644 index 0000000..c93db90 --- /dev/null +++ b/views/widget/widget_win.h @@ -0,0 +1,612 @@ +// Copyright (c) 2011 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. + +#ifndef VIEWS_WIDGET_WIDGET_WIN_H_ +#define VIEWS_WIDGET_WIDGET_WIN_H_ +#pragma once + +#include <atlbase.h> +#include <atlapp.h> +#include <atlcrack.h> +#include <atlmisc.h> + +#include <string> +#include <vector> + +#include "base/message_loop.h" +#include "base/scoped_ptr.h" +#include "base/scoped_vector.h" +#include "base/win/scoped_comptr.h" +#include "ui/base/win/window_impl.h" +#include "views/focus/focus_manager.h" +#include "views/layout/layout_manager.h" +#include "views/widget/widget.h" + +namespace ui { +class ViewProp; +} + +namespace gfx { +class CanvasSkia; +class Rect; +} + +namespace views { + +class DefaultThemeProvider; +class DropTargetWin; +class FocusSearch; +class RootView; +class TooltipManagerWin; +class Window; + +RootView* GetRootViewForHWND(HWND hwnd); + +// A Windows message reflected from other windows. This message is sent +// with the following arguments: +// hWnd - Target window +// uMsg - kReflectedMessage +// wParam - Should be 0 +// lParam - Pointer to MSG struct containing the original message. +const int kReflectedMessage = WM_APP + 3; + +// These two messages aren't defined in winuser.h, but they are sent to windows +// with captions. They appear to paint the window caption and frame. +// Unfortunately if you override the standard non-client rendering as we do +// with CustomFrameWindow, sometimes Windows (not deterministically +// reproducibly but definitely frequently) will send these messages to the +// window and paint the standard caption/title over the top of the custom one. +// So we need to handle these messages in CustomFrameWindow to prevent this +// from happening. +const int WM_NCUAHDRAWCAPTION = 0xAE; +const int WM_NCUAHDRAWFRAME = 0xAF; + +/////////////////////////////////////////////////////////////////////////////// +// +// WidgetWin +// A Widget for a views hierarchy used to represent anything that can be +// contained within an HWND, e.g. a control, a window, etc. Specializations +// suitable for specific tasks, e.g. top level window, are derived from this. +// +// This Widget contains a RootView which owns the hierarchy of views within it. +// As long as views are part of this tree, they will be deleted automatically +// when the RootView is destroyed. If you remove a view from the tree, you are +// then responsible for cleaning up after it. +// +/////////////////////////////////////////////////////////////////////////////// +class WidgetWin : public ui::WindowImpl, + public Widget, + public MessageLoopForUI::Observer, + public FocusTraversable { + public: + WidgetWin(); + virtual ~WidgetWin(); + + // Returns the Widget associated with the specified HWND (if any). + static WidgetWin* GetWidget(HWND hwnd); + + // Returns the root Widget associated with the specified HWND (if any). + static WidgetWin* GetRootWidget(HWND hwnd); + + // Returns true if we are on Windows Vista or greater and composition is + // enabled. + static bool IsAeroGlassEnabled(); + + void set_delete_on_destroy(bool delete_on_destroy) { + delete_on_destroy_ = delete_on_destroy; + } + + // See description of use_layered_buffer_ for details. + void SetUseLayeredBuffer(bool use_layered_buffer); + + // Disable Layered Window updates by setting to false. + void set_can_update_layered_window(bool can_update_layered_window) { + can_update_layered_window_ = can_update_layered_window; + } + + // Obtain the view event with the given MSAA child id. Used in + // ViewAccessibility::get_accChild to support requests for children of + // windowless controls. May return NULL (see ViewHierarchyChanged). + View* GetAccessibilityViewEventAt(int id); + + // Add a view that has recently fired an accessibility event. Returns a MSAA + // child id which is generated by: -(index of view in vector + 1) which + // guarantees a negative child id. This distinguishes the view from + // positive MSAA child id's which are direct leaf children of views that have + // associated hWnd's (e.g. WidgetWin). + int AddAccessibilityViewEvent(View* view); + + // Clear a view that has recently been removed on a hierarchy change. + void ClearAccessibilityViewEvent(View* view); + + BEGIN_MSG_MAP_EX(WidgetWin) + // Range handlers must go first! + MESSAGE_RANGE_HANDLER_EX(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseRange) + MESSAGE_RANGE_HANDLER_EX(WM_NCMOUSEMOVE, WM_NCMOUSEMOVE, OnMouseRange) + + // Reflected message handler + MESSAGE_HANDLER_EX(kReflectedMessage, OnReflectedMessage) + + // CustomFrameWindow hacks + MESSAGE_HANDLER_EX(WM_NCUAHDRAWCAPTION, OnNCUAHDrawCaption) + MESSAGE_HANDLER_EX(WM_NCUAHDRAWFRAME, OnNCUAHDrawFrame) + + // Vista and newer + MESSAGE_HANDLER_EX(WM_DWMCOMPOSITIONCHANGED, OnDwmCompositionChanged) + + // Non-atlcrack.h handlers + MESSAGE_HANDLER_EX(WM_GETOBJECT, OnGetObject) + MESSAGE_HANDLER_EX(WM_NCMOUSELEAVE, OnNCMouseLeave) + MESSAGE_HANDLER_EX(WM_MOUSELEAVE, OnMouseLeave) + MESSAGE_HANDLER_EX(WM_MOUSEWHEEL, OnMouseWheel) + + // Key events. + MESSAGE_HANDLER_EX(WM_KEYDOWN, OnKeyDown) + MESSAGE_HANDLER_EX(WM_KEYUP, OnKeyUp) + MESSAGE_HANDLER_EX(WM_SYSKEYDOWN, OnKeyDown); + MESSAGE_HANDLER_EX(WM_SYSKEYUP, OnKeyUp); + + // This list is in _ALPHABETICAL_ order! OR I WILL HURT YOU. + MSG_WM_ACTIVATE(OnActivate) + MSG_WM_ACTIVATEAPP(OnActivateApp) + MSG_WM_APPCOMMAND(OnAppCommand) + MSG_WM_CANCELMODE(OnCancelMode) + MSG_WM_CAPTURECHANGED(OnCaptureChanged) + MSG_WM_CLOSE(OnClose) + MSG_WM_COMMAND(OnCommand) + MSG_WM_CREATE(OnCreate) + MSG_WM_DESTROY(OnDestroy) + MSG_WM_DISPLAYCHANGE(OnDisplayChange) + MSG_WM_ERASEBKGND(OnEraseBkgnd) + MSG_WM_ENDSESSION(OnEndSession) + MSG_WM_ENTERSIZEMOVE(OnEnterSizeMove) + MSG_WM_EXITMENULOOP(OnExitMenuLoop) + MSG_WM_EXITSIZEMOVE(OnExitSizeMove) + MSG_WM_GETMINMAXINFO(OnGetMinMaxInfo) + MSG_WM_HSCROLL(OnHScroll) + MSG_WM_INITMENU(OnInitMenu) + MSG_WM_INITMENUPOPUP(OnInitMenuPopup) + MSG_WM_KILLFOCUS(OnKillFocus) + MSG_WM_LBUTTONDBLCLK(OnLButtonDblClk) + MSG_WM_LBUTTONDOWN(OnLButtonDown) + MSG_WM_LBUTTONUP(OnLButtonUp) + MSG_WM_MBUTTONDOWN(OnMButtonDown) + MSG_WM_MBUTTONUP(OnMButtonUp) + MSG_WM_MBUTTONDBLCLK(OnMButtonDblClk) + MSG_WM_MOUSEACTIVATE(OnMouseActivate) + MSG_WM_MOUSEMOVE(OnMouseMove) + MSG_WM_MOVE(OnMove) + MSG_WM_MOVING(OnMoving) + MSG_WM_NCACTIVATE(OnNCActivate) + MSG_WM_NCCALCSIZE(OnNCCalcSize) + MSG_WM_NCHITTEST(OnNCHitTest) + MSG_WM_NCMOUSEMOVE(OnNCMouseMove) + MSG_WM_NCLBUTTONDBLCLK(OnNCLButtonDblClk) + MSG_WM_NCLBUTTONDOWN(OnNCLButtonDown) + MSG_WM_NCLBUTTONUP(OnNCLButtonUp) + MSG_WM_NCMBUTTONDBLCLK(OnNCMButtonDblClk) + MSG_WM_NCMBUTTONDOWN(OnNCMButtonDown) + MSG_WM_NCMBUTTONUP(OnNCMButtonUp) + MSG_WM_NCPAINT(OnNCPaint) + MSG_WM_NCRBUTTONDBLCLK(OnNCRButtonDblClk) + MSG_WM_NCRBUTTONDOWN(OnNCRButtonDown) + MSG_WM_NCRBUTTONUP(OnNCRButtonUp) + MSG_WM_NOTIFY(OnNotify) + MSG_WM_PAINT(OnPaint) + MSG_WM_POWERBROADCAST(OnPowerBroadcast) + MSG_WM_RBUTTONDBLCLK(OnRButtonDblClk) + MSG_WM_RBUTTONDOWN(OnRButtonDown) + MSG_WM_RBUTTONUP(OnRButtonUp) + MSG_WM_SETFOCUS(OnSetFocus) + MSG_WM_SETICON(OnSetIcon) + MSG_WM_SETTEXT(OnSetText) + MSG_WM_SETTINGCHANGE(OnSettingChange) + MSG_WM_SIZE(OnSize) + MSG_WM_SYSCOMMAND(OnSysCommand) + MSG_WM_THEMECHANGED(OnThemeChanged) + MSG_WM_VSCROLL(OnVScroll) + MSG_WM_WINDOWPOSCHANGING(OnWindowPosChanging) + MSG_WM_WINDOWPOSCHANGED(OnWindowPosChanged) + END_MSG_MAP() + + // Overridden from Widget: + virtual void Init(gfx::NativeView parent, const gfx::Rect& bounds); + virtual void InitWithWidget(Widget* parent, const gfx::Rect& bounds); + virtual WidgetDelegate* GetWidgetDelegate(); + virtual void SetWidgetDelegate(WidgetDelegate* delegate); + virtual void SetContentsView(View* view); + virtual void GetBounds(gfx::Rect* out, bool including_frame) const; + virtual void SetBounds(const gfx::Rect& bounds); + virtual void MoveAbove(Widget* other); + virtual void SetShape(gfx::NativeRegion region); + virtual void Close(); + virtual void CloseNow(); + virtual void Show(); + virtual void Hide(); + virtual gfx::NativeView GetNativeView() const; + virtual void PaintNow(const gfx::Rect& update_rect); + virtual void SetOpacity(unsigned char opacity); + virtual void SetAlwaysOnTop(bool on_top); + virtual RootView* GetRootView(); + virtual Widget* GetRootWidget() const; + virtual bool IsVisible() const; + virtual bool IsActive() const; + virtual bool IsAccessibleWidget() const; + virtual TooltipManager* GetTooltipManager(); + virtual void GenerateMousePressedForView(View* view, + const gfx::Point& point); + virtual bool GetAccelerator(int cmd_id, ui::Accelerator* accelerator); + virtual Window* GetWindow(); + virtual const Window* GetWindow() const; + virtual void SetNativeWindowProperty(const char* name, void* value); + virtual void* GetNativeWindowProperty(const char* name); + virtual ThemeProvider* GetThemeProvider() const; + virtual ThemeProvider* GetDefaultThemeProvider() const; + virtual FocusManager* GetFocusManager(); + virtual void ViewHierarchyChanged(bool is_add, View *parent, + View *child); + virtual bool ContainsNativeView(gfx::NativeView native_view); + + // Overridden from MessageLoop::Observer: + void WillProcessMessage(const MSG& msg); + virtual void DidProcessMessage(const MSG& msg); + + // Overridden from FocusTraversable: + virtual FocusSearch* GetFocusSearch(); + virtual FocusTraversable* GetFocusTraversableParent(); + virtual View* GetFocusTraversableParentView(); + + void SetFocusTraversableParent(FocusTraversable* parent); + void SetFocusTraversableParentView(View* parent_view); + + BOOL IsWindow() const { + return ::IsWindow(GetNativeView()); + } + + BOOL ShowWindow(int command) { + DCHECK(::IsWindow(GetNativeView())); + return ::ShowWindow(GetNativeView(), command); + } + + HWND SetCapture() { + DCHECK(::IsWindow(GetNativeView())); + return ::SetCapture(GetNativeView()); + } + + HWND GetParent() const { + return ::GetParent(GetNativeView()); + } + + LONG GetWindowLong(int index) { + DCHECK(::IsWindow(GetNativeView())); + return ::GetWindowLong(GetNativeView(), index); + } + + BOOL GetWindowRect(RECT* rect) const { + return ::GetWindowRect(GetNativeView(), rect); + } + + LONG SetWindowLong(int index, LONG new_long) { + DCHECK(::IsWindow(GetNativeView())); + return ::SetWindowLong(GetNativeView(), index, new_long); + } + + BOOL SetWindowPos(HWND hwnd_after, int x, int y, int cx, int cy, UINT flags) { + DCHECK(::IsWindow(GetNativeView())); + return ::SetWindowPos(GetNativeView(), hwnd_after, x, y, cx, cy, flags); + } + + BOOL IsZoomed() const { + DCHECK(::IsWindow(GetNativeView())); + return ::IsZoomed(GetNativeView()); + } + + BOOL MoveWindow(int x, int y, int width, int height) { + return MoveWindow(x, y, width, height, TRUE); + } + + BOOL MoveWindow(int x, int y, int width, int height, BOOL repaint) { + DCHECK(::IsWindow(GetNativeView())); + return ::MoveWindow(GetNativeView(), x, y, width, height, repaint); + } + + int SetWindowRgn(HRGN region, BOOL redraw) { + DCHECK(::IsWindow(GetNativeView())); + return ::SetWindowRgn(GetNativeView(), region, redraw); + } + + BOOL GetClientRect(RECT* rect) const { + DCHECK(::IsWindow(GetNativeView())); + return ::GetClientRect(GetNativeView(), rect); + } + + // Resets the last move flag so that we can go around the optimization + // that disregards duplicate mouse moves when ending animation requires + // a new hit-test to do some highlighting as in TabStrip::RemoveTabAnimation + // to cause the close button to highlight. + void ResetLastMouseMoveFlag() { + last_mouse_event_was_move_ = false; + } + + protected: + // Overridden from WindowImpl: + virtual HICON GetDefaultWindowIcon() const; + virtual LRESULT OnWndProc(UINT message, WPARAM w_param, LPARAM l_param); + + // Message Handlers + // These are all virtual so that specialized Widgets can modify or augment + // processing. + // This list is in _ALPHABETICAL_ order! + // Note: in the base class these functions must do nothing but convert point + // coordinates to client coordinates (if necessary) and forward the + // handling to the appropriate Process* function. This is so that + // subclasses can easily override these methods to do different things + // and have a convenient function to call to get the default behavior. + virtual void OnActivate(UINT action, BOOL minimized, HWND window); + virtual void OnActivateApp(BOOL active, DWORD thread_id); + virtual LRESULT OnAppCommand(HWND window, short app_command, WORD device, + int keystate); + virtual void OnCancelMode(); + virtual void OnCaptureChanged(HWND hwnd); + virtual void OnClose(); + virtual void OnCommand(UINT notification_code, int command_id, HWND window); + virtual LRESULT OnCreate(CREATESTRUCT* create_struct); + // WARNING: If you override this be sure and invoke super, otherwise we'll + // leak a few things. + virtual void OnDestroy(); + virtual void OnDisplayChange(UINT bits_per_pixel, CSize screen_size); + virtual LRESULT OnDwmCompositionChanged(UINT msg, + WPARAM w_param, + LPARAM l_param); + virtual void OnEndSession(BOOL ending, UINT logoff); + virtual void OnEnterSizeMove(); + virtual LRESULT OnEraseBkgnd(HDC dc); + virtual void OnExitMenuLoop(BOOL is_track_popup_menu); + virtual void OnExitSizeMove(); + virtual LRESULT OnGetObject(UINT uMsg, WPARAM w_param, LPARAM l_param); + virtual void OnGetMinMaxInfo(MINMAXINFO* minmax_info); + virtual void OnHScroll(int scroll_type, short position, HWND scrollbar); + virtual void OnInitMenu(HMENU menu); + virtual void OnInitMenuPopup(HMENU menu, UINT position, BOOL is_system_menu); + virtual LRESULT OnKeyDown(UINT message, WPARAM w_param, LPARAM l_param); + virtual LRESULT OnKeyUp(UINT message, WPARAM w_param, LPARAM l_param); + virtual void OnKillFocus(HWND focused_window); + virtual void OnLButtonDblClk(UINT flags, const CPoint& point); + virtual void OnLButtonDown(UINT flags, const CPoint& point); + virtual void OnLButtonUp(UINT flags, const CPoint& point); + virtual void OnMButtonDblClk(UINT flags, const CPoint& point); + virtual void OnMButtonDown(UINT flags, const CPoint& point); + virtual void OnMButtonUp(UINT flags, const CPoint& point); + virtual LRESULT OnMouseActivate(HWND window, UINT hittest_code, UINT message); + virtual void OnMouseMove(UINT flags, const CPoint& point); + virtual LRESULT OnMouseLeave(UINT message, WPARAM w_param, LPARAM l_param); + virtual LRESULT OnMouseWheel(UINT message, WPARAM w_param, LPARAM l_param); + virtual void OnMove(const CPoint& point); + virtual void OnMoving(UINT param, LPRECT new_bounds); + virtual LRESULT OnMouseRange(UINT msg, WPARAM w_param, LPARAM l_param); + virtual LRESULT OnNCActivate(BOOL active); + virtual LRESULT OnNCCalcSize(BOOL w_param, LPARAM l_param); + virtual LRESULT OnNCHitTest(const CPoint& pt); + virtual void OnNCLButtonDblClk(UINT flags, const CPoint& point); + virtual void OnNCLButtonDown(UINT flags, const CPoint& point); + virtual void OnNCLButtonUp(UINT flags, const CPoint& point); + virtual void OnNCMButtonDblClk(UINT flags, const CPoint& point); + virtual void OnNCMButtonDown(UINT flags, const CPoint& point); + virtual void OnNCMButtonUp(UINT flags, const CPoint& point); + virtual LRESULT OnNCMouseLeave(UINT uMsg, WPARAM w_param, LPARAM l_param); + virtual LRESULT OnNCMouseMove(UINT flags, const CPoint& point); + virtual void OnNCPaint(HRGN rgn); + virtual void OnNCRButtonDblClk(UINT flags, const CPoint& point); + virtual void OnNCRButtonDown(UINT flags, const CPoint& point); + virtual void OnNCRButtonUp(UINT flags, const CPoint& point); + virtual LRESULT OnNCUAHDrawCaption(UINT msg, + WPARAM w_param, + LPARAM l_param); + virtual LRESULT OnNCUAHDrawFrame(UINT msg, WPARAM w_param, LPARAM l_param); + virtual LRESULT OnNotify(int w_param, NMHDR* l_param); + virtual void OnPaint(HDC dc); + virtual LRESULT OnPowerBroadcast(DWORD power_event, DWORD data); + virtual void OnRButtonDblClk(UINT flags, const CPoint& point); + virtual void OnRButtonDown(UINT flags, const CPoint& point); + virtual void OnRButtonUp(UINT flags, const CPoint& point); + virtual LRESULT OnReflectedMessage(UINT msg, WPARAM w_param, LPARAM l_param); + virtual void OnSetFocus(HWND focused_window); + virtual LRESULT OnSetIcon(UINT size_type, HICON new_icon); + virtual LRESULT OnSetText(const wchar_t* text); + virtual void OnSettingChange(UINT flags, const wchar_t* section); + virtual void OnSize(UINT param, const CSize& size); + virtual void OnSysCommand(UINT notification_code, CPoint click); + virtual void OnThemeChanged(); + virtual void OnVScroll(int scroll_type, short position, HWND scrollbar); + virtual void OnWindowPosChanging(WINDOWPOS* window_pos); + virtual void OnWindowPosChanged(WINDOWPOS* window_pos); + + // deletes this window as it is destroyed, override to provide different + // behavior. + virtual void OnFinalMessage(HWND window); + + // Returns the size that the RootView should be set to in LayoutRootView(). + virtual gfx::Size GetRootViewSize() const; + + // Start tracking all mouse events so that this window gets sent mouse leave + // messages too. + void TrackMouseEvents(DWORD mouse_tracking_flags); + + // Actually handle mouse events. These functions are called by subclasses who + // override the message handlers above to do the actual real work of handling + // the event in the View system. + bool ProcessMousePressed(const CPoint& point, + UINT flags, + bool dbl_click, + bool non_client); + void ProcessMouseDragged(const CPoint& point, UINT flags); + void ProcessMouseReleased(const CPoint& point, UINT flags); + void ProcessMouseMoved(const CPoint& point, UINT flags, bool is_nonclient); + void ProcessMouseExited(); + + // Lays out the root view to fit the appropriate area within the widget. + // Called when the window size or non client metrics change. + void LayoutRootView(); + + // Called when a MSAA screen reader cleint is detected. + virtual void OnScreenReaderDetected(); + + // Returns whether capture should be released on mouse release. The default + // is true. + virtual bool ReleaseCaptureOnMouseReleased(); + + // Creates the RootView to be used within this Widget. Can be overridden to + // create specialized RootView implementations. + virtual RootView* CreateRootView(); + + // Returns true if this WidgetWin is opaque. + bool opaque() const { return opaque_; } + + // The TooltipManager. + // WARNING: RootView's destructor calls into the TooltipManager. As such, this + // must be destroyed AFTER root_view_. + scoped_ptr<TooltipManagerWin> tooltip_manager_; + + scoped_refptr<DropTargetWin> drop_target_; + + // The focus manager keeping track of focus for this Widget and any of its + // children. NULL for non top-level widgets. + // WARNING: RootView's destructor calls into the FocusManager. As such, this + // must be destroyed AFTER root_view_. + scoped_ptr<FocusManager> focus_manager_; + + // The root of the View hierarchy attached to this window. + // WARNING: see warning in tooltip_manager_ for ordering dependencies with + // this and tooltip_manager_. + scoped_ptr<RootView> root_view_; + + // Whether or not we have capture the mouse. + bool has_capture_; + + // If true, the mouse is currently down. + bool is_mouse_down_; + + // Are a subclass of WindowWin? + bool is_window_; + + private: + typedef ScopedVector<ui::ViewProp> ViewProps; + + // Implementation of GetWindow. Ascends the parents of |hwnd| returning the + // first ancestor that is a Window. + static Window* GetWindowImpl(HWND hwnd); + + // Resize the bitmap used to contain the contents of the layered window. This + // recreates the entire bitmap. + void SizeContents(const gfx::Size& window_size); + + // Paint into a DIB and then update the layered window with its contents. + void PaintLayeredWindow(); + + // In layered mode, update the layered window. |dib_dc| represents a handle + // to a device context that contains the contents of the window. + void UpdateWindowFromContents(HDC dib_dc); + + // Invoked from WM_DESTROY. Does appropriate cleanup and invokes OnDestroy + // so that subclasses can do any cleanup they need to. + // void OnDestroyImpl(); + + // Returns the RootView that contains the focused view, or NULL if there is no + // focused view. + RootView* GetFocusedViewRootView(); + + // Called after the WM_ACTIVATE message has been processed by the default + // windows procedure. + static void PostProcessActivateMessage(WidgetWin* widget, + int activation_state); + + // Fills out a MSG struct with the supplied values. + void MakeMSG(MSG* msg, UINT message, WPARAM w_param, LPARAM l_param) const; + + // The following factory is used for calls to close the WidgetWin + // instance. + ScopedRunnableMethodFactory<WidgetWin> close_widget_factory_; + + // The flags currently being used with TrackMouseEvent to track mouse + // messages. 0 if there is no active tracking. The value of this member is + // used when tracking is canceled. + DWORD active_mouse_tracking_flags_; + + bool opaque_; + + // Should we keep an offscreen buffer? This is initially true and if the + // window has WS_EX_LAYERED then it remains true. You can set this to false + // at any time to ditch the buffer, and similarly set back to true to force + // creation of the buffer. + // + // NOTE: this is intended to be used with a layered window (a window with an + // extended window style of WS_EX_LAYERED). If you are using a layered window + // and NOT changing the layered alpha or anything else, then leave this value + // alone. OTOH if you are invoking SetLayeredWindowAttributes then you'll + // must likely want to set this to false, or after changing the alpha toggle + // the extended style bit to false than back to true. See MSDN for more + // details. + bool use_layered_buffer_; + + // The default alpha to be applied to the layered window. + BYTE layered_alpha_; + + // A canvas that contains the window contents in the case of a layered + // window. + scoped_ptr<gfx::CanvasSkia> contents_; + + // Whether or not the window should delete itself when it is destroyed. + // Set this to false via its setter for stack allocated instances. + bool delete_on_destroy_; + + // True if we are allowed to update the layered window from the DIB backing + // store if necessary. + bool can_update_layered_window_; + + // The following are used to detect duplicate mouse move events and not + // deliver them. Displaying a window may result in the system generating + // duplicate move events even though the mouse hasn't moved. + + // If true, the last event was a mouse move event. + bool last_mouse_event_was_move_; + + // Coordinates of the last mouse move event, in screen coordinates. + int last_mouse_move_x_; + int last_mouse_move_y_; + + // Whether the focus should be restored next time we get enabled. Needed to + // restore focus correctly when Windows modal dialogs are displayed. + bool restore_focus_when_enabled_; + + // Instance of accessibility information and handling for MSAA root + base::win::ScopedComPtr<IAccessible> accessibility_root_; + + scoped_ptr<DefaultThemeProvider> default_theme_provider_; + + // Non owned pointer to optional delegate. May be NULL if no delegate is + // being used. + WidgetDelegate* delegate_; + + // Value determines whether the Widget is customized for accessibility. + static bool screen_reader_active_; + + // The maximum number of view events in our vector below. + static const int kMaxAccessibilityViewEvents = 20; + + // A vector used to access views for which we have sent notifications to + // accessibility clients. It is used as a circular queue. + std::vector<View*> accessibility_view_events_; + + // The current position of the view events vector. When incrementing, + // we always mod this value with the max view events above . + int accessibility_view_events_index_; + + ViewProps props_; + + DISALLOW_COPY_AND_ASSIGN(WidgetWin); +}; + +} // namespace views + +#endif // VIEWS_WIDGET_WIDGET_WIN_H_ diff --git a/views/widget/widget_win_unittest.cc b/views/widget/widget_win_unittest.cc new file mode 100644 index 0000000..d0cb4e4 --- /dev/null +++ b/views/widget/widget_win_unittest.cc @@ -0,0 +1,82 @@ +// Copyright (c) 2011 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 "views/widget/widget_win.h" + +#include "base/basictypes.h" +#include "base/message_loop.h" +#include "base/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +using namespace views; + +class WidgetWinTest : public testing::Test { + public: + WidgetWinTest() { + OleInitialize(NULL); + } + + ~WidgetWinTest() { + OleUninitialize(); + } + + virtual void TearDown() { + // Flush the message loop because we have pending release tasks + // and these tasks if un-executed would upset Valgrind. + RunPendingMessages(); + } + + // Create a simple widget win. The caller is responsible for taking ownership + // of the returned value. + WidgetWin* CreateWidgetWin(); + + void RunPendingMessages() { + message_loop_.RunAllPending(); + } + + private: + MessageLoopForUI message_loop_; + + DISALLOW_COPY_AND_ASSIGN(WidgetWinTest); +}; + + +WidgetWin* WidgetWinTest::CreateWidgetWin() { + scoped_ptr<WidgetWin> window(new WidgetWin()); + window->set_delete_on_destroy(false); + window->set_window_style(WS_OVERLAPPEDWINDOW); + window->Init(NULL, gfx::Rect(50, 50, 650, 650)); + return window.release(); +} + +TEST_F(WidgetWinTest, ZoomWindow) { + scoped_ptr<WidgetWin> window(CreateWidgetWin()); + window->ShowWindow(SW_HIDE); + EXPECT_FALSE(window->IsActive()); + window->ShowWindow(SW_MAXIMIZE); + EXPECT_TRUE(window->IsZoomed()); + window->CloseNow(); +} + +TEST_F(WidgetWinTest, SetBoundsForZoomedWindow) { + scoped_ptr<WidgetWin> window(CreateWidgetWin()); + window->ShowWindow(SW_MAXIMIZE); + EXPECT_TRUE(window->IsZoomed()); + + // Create another window, so that it will be active. + scoped_ptr<WidgetWin> window2(CreateWidgetWin()); + window2->ShowWindow(SW_MAXIMIZE); + EXPECT_TRUE(window2->IsActive()); + EXPECT_FALSE(window->IsActive()); + + // Verify that setting the bounds of a zoomed window will unzoom it and not + // cause it to be activated. + window->SetBounds(gfx::Rect(50, 50, 650, 650)); + EXPECT_FALSE(window->IsZoomed()); + EXPECT_FALSE(window->IsActive()); + + // Cleanup. + window->CloseNow(); + window2->CloseNow(); +} |