diff options
Diffstat (limited to 'ui/views/controls/native_control_win.cc')
-rw-r--r-- | ui/views/controls/native_control_win.cc | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/ui/views/controls/native_control_win.cc b/ui/views/controls/native_control_win.cc new file mode 100644 index 0000000..2b71964 --- /dev/null +++ b/ui/views/controls/native_control_win.cc @@ -0,0 +1,226 @@ +// 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 "ui/views/controls/native_control_win.h" + +#include <windowsx.h> + +#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 "ui/views/controls/combobox/combobox.h" +#include "ui/views/focus/focus_manager.h" +#include "ui/views/widget/widget.h" + +using ui::ViewProp; + +const char kNativeControlWinKey[] = "__NATIVE_CONTROL_WIN__"; + +namespace views { + +//////////////////////////////////////////////////////////////////////////////// +// 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<HDC>(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() != 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<LRESULT>(brush); + } + ancestor = ancestor->parent(); + } + + // COLOR_BTNFACE is the default for dialog box backgrounds. + return reinterpret_cast<LRESULT>(GetSysColorBrush(COLOR_BTNFACE)); +} + +// static +LRESULT NativeControlWin::NativeControlWndProc(HWND window, + UINT message, + WPARAM w_param, + LPARAM l_param) { + NativeControlWin* native_control = reinterpret_cast<NativeControlWin*>( + ViewProp::GetValue(window, kNativeControlWinKey)); + DCHECK(native_control); + + if (message == WM_KEYDOWN && + native_control->OnKeyDown(static_cast<int>(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 |