// 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/controls/native_control_win.h" #include #include "base/logging.h" #include "ui/base/accessibility/accessibility_types.h" #include "ui/base/l10n/l10n_util_win.h" #include "ui/base/view_prop.h" #include "ui/base/win/hwnd_util.h" #include "views/focus/focus_manager.h" #include "views/widget/widget.h" using ui::ViewProp; namespace views { static const char* const kNativeControlWinKey = "__NATIVE_CONTROL_WIN__"; //////////////////////////////////////////////////////////////////////////////// // NativeControlWin, public: NativeControlWin::NativeControlWin() { } NativeControlWin::~NativeControlWin() { HWND hwnd = native_view(); if (hwnd) { // Destroy the hwnd if it still exists. Otherwise we won't have shut things // down correctly, leading to leaking and crashing if another message // comes in for the hwnd. Detach(); DestroyWindow(hwnd); } } bool NativeControlWin::ProcessMessage(UINT message, WPARAM w_param, LPARAM l_param, LRESULT* result) { switch (message) { case WM_CONTEXTMENU: ShowContextMenu(gfx::Point(GET_X_LPARAM(l_param), GET_Y_LPARAM(l_param))); *result = 0; return true; case WM_CTLCOLORBTN: case WM_CTLCOLORSTATIC: *result = GetControlColor(message, reinterpret_cast(w_param), native_view()); return true; } return false; } //////////////////////////////////////////////////////////////////////////////// // NativeControlWin, View overrides: void NativeControlWin::OnEnabledChanged() { View::OnEnabledChanged(); if (native_view()) EnableWindow(native_view(), IsEnabled()); } void NativeControlWin::ViewHierarchyChanged(bool is_add, View* parent, View* child) { // Call the base class to hide the view if we're being removed. NativeViewHost::ViewHierarchyChanged(is_add, parent, child); // Create the HWND when we're added to a valid Widget. Many controls need a // parent HWND to function properly. if (is_add && GetWidget() && !native_view()) CreateNativeControl(); } void NativeControlWin::VisibilityChanged(View* starting_from, bool is_visible) { // We might get called due to visibility changes at any point in the // hierarchy, lets check whether we are really visible or not. bool visible = IsVisibleInRootView(); if (!visible && native_view()) { // We destroy the child control HWND when we become invisible because of the // performance cost of maintaining many HWNDs. HWND hwnd = native_view(); Detach(); DestroyWindow(hwnd); } else if (visible && !native_view()) { if (GetWidget()) CreateNativeControl(); } if (visible) { // The view becomes visible after native control is created. // Layout now. Layout(); } } void NativeControlWin::OnFocus() { DCHECK(native_view()); SetFocus(native_view()); // Since we are being wrapped by a view, accessibility should receive // the super class as the focused view. View* parent_view = parent(); // Due to some controls not behaving as expected without having // a native win32 control, we don't always send a native (MSAA) // focus notification. bool send_native_event = parent_view->GetClassName() != views::Combobox::kViewClassName && parent_view->HasFocus(); // Send the accessibility focus notification. if (parent_view->GetWidget()) { parent_view->GetWidget()->NotifyAccessibilityEvent( parent_view, ui::AccessibilityTypes::EVENT_FOCUS, send_native_event); } } //////////////////////////////////////////////////////////////////////////////// // NativeControlWin, protected: void NativeControlWin::ShowContextMenu(const gfx::Point& location) { if (!context_menu_controller()) return; if (location.x() == -1 && location.y() == -1) View::ShowContextMenu(GetKeyboardContextMenuLocation(), false); else View::ShowContextMenu(location, true); } void NativeControlWin::NativeControlCreated(HWND native_control) { // Associate this object with the control's HWND so that NativeWidgetWin can // find this object when it receives messages from it. props_.push_back(new ViewProp(native_control, kNativeControlWinKey, this)); props_.push_back(ChildWindowMessageProcessor::Register(native_control, this)); // Subclass so we get WM_KEYDOWN and WM_SETFOCUS messages. original_wndproc_ = ui::SetWindowProc( native_control, &NativeControlWin::NativeControlWndProc); Attach(native_control); // native_view() is now valid. // Update the newly created HWND with any resident enabled state. EnableWindow(native_view(), IsEnabled()); // This message ensures that the focus border is shown. SendMessage(native_view(), WM_CHANGEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0); } DWORD NativeControlWin::GetAdditionalExStyle() const { // If the UI for the view is mirrored, we should make sure we add the // extended window style for a right-to-left layout so the subclass creates // a mirrored HWND for the underlying control. DWORD ex_style = 0; if (base::i18n::IsRTL()) ex_style |= l10n_util::GetExtendedStyles(); return ex_style; } DWORD NativeControlWin::GetAdditionalRTLStyle() const { // If the UI for the view is mirrored, we should make sure we add the // extended window style for a right-to-left layout so the subclass creates // a mirrored HWND for the underlying control. DWORD ex_style = 0; if (base::i18n::IsRTL()) ex_style |= l10n_util::GetExtendedTooltipStyles(); return ex_style; } //////////////////////////////////////////////////////////////////////////////// // NativeControlWin, private: LRESULT NativeControlWin::GetControlColor(UINT message, HDC dc, HWND sender) { View *ancestor = this; while (ancestor) { const Background* background = ancestor->background(); if (background) { HBRUSH brush = background->GetNativeControlBrush(); if (brush) return reinterpret_cast(brush); } ancestor = ancestor->parent(); } // COLOR_BTNFACE is the default for dialog box backgrounds. return reinterpret_cast(GetSysColorBrush(COLOR_BTNFACE)); } // static LRESULT NativeControlWin::NativeControlWndProc(HWND window, UINT message, WPARAM w_param, LPARAM l_param) { NativeControlWin* native_control = reinterpret_cast( ViewProp::GetValue(window, kNativeControlWinKey)); DCHECK(native_control); if (message == WM_KEYDOWN && native_control->OnKeyDown(static_cast(w_param))) { return 0; } else if (message == WM_SETFOCUS) { // Let the focus manager know that the focus changed. FocusManager* focus_manager = native_control->GetFocusManager(); if (focus_manager) { focus_manager->SetFocusedView(native_control->focus_view()); } else { NOTREACHED(); } } else if (message == WM_DESTROY) { native_control->props_.reset(); ui::SetWindowProc(window, native_control->original_wndproc_); } return CallWindowProc(native_control->original_wndproc_, window, message, w_param, l_param); } } // namespace views