1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ui/views/controls/native_control_win.h" 6 7 #include <windowsx.h> 8 9 #include "base/logging.h" 10 #include "ui/base/accessibility/accessibility_types.h" 11 #include "ui/base/l10n/l10n_util_win.h" 12 #include "ui/base/view_prop.h" 13 #include "ui/gfx/win/hwnd_util.h" 14 #include "ui/views/controls/combobox/combobox.h" 15 #include "ui/views/focus/focus_manager.h" 16 #include "ui/views/widget/widget.h" 17 18 using ui::ViewProp; 19 20 const char kNativeControlWinKey[] = "__NATIVE_CONTROL_WIN__"; 21 22 namespace views { 23 24 //////////////////////////////////////////////////////////////////////////////// 25 // NativeControlWin, public: 26 27 NativeControlWin::NativeControlWin() : original_wndproc_(NULL) { 28 } 29 30 NativeControlWin::~NativeControlWin() { 31 HWND hwnd = native_view(); 32 if (hwnd) { 33 // Destroy the hwnd if it still exists. Otherwise we won't have shut things 34 // down correctly, leading to leaking and crashing if another message 35 // comes in for the hwnd. 36 Detach(); 37 DestroyWindow(hwnd); 38 } 39 } 40 41 bool NativeControlWin::ProcessMessage(UINT message, 42 WPARAM w_param, 43 LPARAM l_param, 44 LRESULT* result) { 45 switch (message) { 46 case WM_CONTEXTMENU: 47 ShowContextMenu(gfx::Point(GET_X_LPARAM(l_param), GET_Y_LPARAM(l_param))); 48 *result = 0; 49 return true; 50 case WM_CTLCOLORBTN: 51 case WM_CTLCOLORSTATIC: 52 *result = GetControlColor(message, reinterpret_cast<HDC>(w_param), 53 native_view()); 54 return true; 55 } 56 return false; 57 } 58 59 //////////////////////////////////////////////////////////////////////////////// 60 // NativeControlWin, View overrides: 61 62 void NativeControlWin::OnEnabledChanged() { 63 View::OnEnabledChanged(); 64 if (native_view()) 65 EnableWindow(native_view(), enabled()); 66 } 67 68 void NativeControlWin::ViewHierarchyChanged( 69 const ViewHierarchyChangedDetails& details) { 70 // Call the base class to hide the view if we're being removed. 71 NativeViewHost::ViewHierarchyChanged(details); 72 73 // Create the HWND when we're added to a valid Widget. Many controls need a 74 // parent HWND to function properly. 75 if (details.is_add && GetWidget() && !native_view()) 76 CreateNativeControl(); 77 } 78 79 void NativeControlWin::VisibilityChanged(View* starting_from, bool is_visible) { 80 // We might get called due to visibility changes at any point in the 81 // hierarchy, lets check whether we are really visible or not. 82 bool is_drawn = IsDrawn(); 83 if (!is_drawn && native_view()) { 84 // We destroy the child control HWND when we become invisible because of the 85 // performance cost of maintaining many HWNDs. 86 HWND hwnd = native_view(); 87 Detach(); 88 DestroyWindow(hwnd); 89 } else if (is_drawn && !native_view()) { 90 if (GetWidget()) 91 CreateNativeControl(); 92 } 93 if (is_drawn) { 94 // The view becomes visible after native control is created. 95 // Layout now. 96 Layout(); 97 } 98 } 99 100 void NativeControlWin::OnFocus() { 101 DCHECK(native_view()); 102 SetFocus(native_view()); 103 104 // Since we are being wrapped by a view, accessibility should receive 105 // the super class as the focused view. 106 View* parent_view = parent(); 107 108 // Due to some controls not behaving as expected without having 109 // a native win32 control, we don't always send a native (MSAA) 110 // focus notification. 111 bool send_native_event = 112 strcmp(parent_view->GetClassName(), Combobox::kViewClassName) && 113 parent_view->HasFocus(); 114 115 // Send the accessibility focus notification. 116 parent_view->NotifyAccessibilityEvent( 117 ui::AccessibilityTypes::EVENT_FOCUS, send_native_event); 118 } 119 120 //////////////////////////////////////////////////////////////////////////////// 121 // NativeControlWin, protected: 122 123 void NativeControlWin::ShowContextMenu(const gfx::Point& location) { 124 if (!context_menu_controller()) 125 return; 126 127 if (location.x() == -1 && location.y() == -1) { 128 View::ShowContextMenu(GetKeyboardContextMenuLocation(), 129 ui::MENU_SOURCE_KEYBOARD); 130 } else { 131 View::ShowContextMenu(location, ui::MENU_SOURCE_MOUSE); 132 } 133 } 134 135 void NativeControlWin::NativeControlCreated(HWND native_control) { 136 // Associate this object with the control's HWND so that NativeWidgetWin can 137 // find this object when it receives messages from it. 138 props_.push_back(new ViewProp(native_control, kNativeControlWinKey, this)); 139 props_.push_back(ChildWindowMessageProcessor::Register(native_control, this)); 140 141 // Subclass so we get WM_KEYDOWN and WM_SETFOCUS messages. 142 original_wndproc_ = gfx::SetWindowProc( 143 native_control, &NativeControlWin::NativeControlWndProc); 144 145 Attach(native_control); 146 // native_view() is now valid. 147 148 // Update the newly created HWND with any resident enabled state. 149 EnableWindow(native_view(), enabled()); 150 151 // This message ensures that the focus border is shown. 152 SendMessage(native_view(), WM_CHANGEUISTATE, 153 MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0); 154 } 155 156 DWORD NativeControlWin::GetAdditionalExStyle() const { 157 // If the UI for the view is mirrored, we should make sure we add the 158 // extended window style for a right-to-left layout so the subclass creates 159 // a mirrored HWND for the underlying control. 160 DWORD ex_style = 0; 161 if (base::i18n::IsRTL()) 162 ex_style |= l10n_util::GetExtendedStyles(); 163 164 return ex_style; 165 } 166 167 DWORD NativeControlWin::GetAdditionalRTLStyle() const { 168 // If the UI for the view is mirrored, we should make sure we add the 169 // extended window style for a right-to-left layout so the subclass creates 170 // a mirrored HWND for the underlying control. 171 DWORD ex_style = 0; 172 if (base::i18n::IsRTL()) 173 ex_style |= l10n_util::GetExtendedTooltipStyles(); 174 175 return ex_style; 176 } 177 178 //////////////////////////////////////////////////////////////////////////////// 179 // NativeControlWin, private: 180 181 LRESULT NativeControlWin::GetControlColor(UINT message, HDC dc, HWND sender) { 182 View *ancestor = this; 183 while (ancestor) { 184 const Background* background = ancestor->background(); 185 if (background) { 186 HBRUSH brush = background->GetNativeControlBrush(); 187 if (brush) 188 return reinterpret_cast<LRESULT>(brush); 189 } 190 ancestor = ancestor->parent(); 191 } 192 193 // COLOR_BTNFACE is the default for dialog box backgrounds. 194 return reinterpret_cast<LRESULT>(GetSysColorBrush(COLOR_BTNFACE)); 195 } 196 197 // static 198 LRESULT NativeControlWin::NativeControlWndProc(HWND window, 199 UINT message, 200 WPARAM w_param, 201 LPARAM l_param) { 202 NativeControlWin* native_control = reinterpret_cast<NativeControlWin*>( 203 ViewProp::GetValue(window, kNativeControlWinKey)); 204 DCHECK(native_control); 205 206 if (message == WM_KEYDOWN && 207 native_control->OnKeyDown(static_cast<int>(w_param))) { 208 return 0; 209 } else if (message == WM_SETFOCUS) { 210 // Let the focus manager know that the focus changed. 211 FocusManager* focus_manager = native_control->GetFocusManager(); 212 if (focus_manager) { 213 focus_manager->SetFocusedView(native_control->focus_view()); 214 } else { 215 NOTREACHED(); 216 } 217 } else if (message == WM_DESTROY) { 218 native_control->props_.clear(); 219 gfx::SetWindowProc(window, native_control->original_wndproc_); 220 } 221 222 return CallWindowProc(native_control->original_wndproc_, window, message, 223 w_param, l_param); 224 } 225 226 } // namespace views 227