| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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.h" | |
| 6 | |
| 7 #include <atlbase.h> | |
| 8 #include <atlapp.h> | |
| 9 #include <atlcrack.h> | |
| 10 #include <atlframe.h> | |
| 11 #include <atlmisc.h> | |
| 12 | |
| 13 #include "base/logging.h" | |
| 14 #include "base/memory/scoped_ptr.h" | |
| 15 #include "ui/base/accessibility/accessibility_types.h" | |
| 16 #include "ui/base/l10n/l10n_util_win.h" | |
| 17 #include "ui/base/view_prop.h" | |
| 18 #include "ui/events/keycodes/keyboard_code_conversion_win.h" | |
| 19 #include "ui/events/keycodes/keyboard_codes.h" | |
| 20 #include "ui/gfx/win/hwnd_util.h" | |
| 21 #include "ui/views/background.h" | |
| 22 #include "ui/views/controls/native/native_view_host.h" | |
| 23 #include "ui/views/focus/focus_manager.h" | |
| 24 #include "ui/views/widget/widget.h" | |
| 25 | |
| 26 using ui::ViewProp; | |
| 27 | |
| 28 namespace views { | |
| 29 | |
| 30 // Maps to the NativeControl. | |
| 31 static const char* const kNativeControlKey = "__NATIVE_CONTROL__"; | |
| 32 | |
| 33 class NativeControlContainer : public CWindowImpl<NativeControlContainer, | |
| 34 CWindow, | |
| 35 CWinTraits<WS_CHILD | WS_CLIPSIBLINGS | | |
| 36 WS_CLIPCHILDREN>> { | |
| 37 public: | |
| 38 explicit NativeControlContainer(NativeControl* parent) | |
| 39 : parent_(parent), | |
| 40 control_(NULL), | |
| 41 original_handler_(NULL) { | |
| 42 } | |
| 43 | |
| 44 void Init() { | |
| 45 Create(parent_->GetWidget()->GetNativeView()); | |
| 46 ::ShowWindow(m_hWnd, SW_SHOW); | |
| 47 } | |
| 48 | |
| 49 virtual ~NativeControlContainer() { | |
| 50 } | |
| 51 | |
| 52 // NOTE: If you add a new message, be sure and verify parent_ is valid before | |
| 53 // calling into parent_. | |
| 54 DECLARE_FRAME_WND_CLASS(L"ChromeViewsNativeControlContainer", NULL); | |
| 55 BEGIN_MSG_MAP(NativeControlContainer); | |
| 56 MSG_WM_CREATE(OnCreate); | |
| 57 MSG_WM_ERASEBKGND(OnEraseBkgnd); | |
| 58 MSG_WM_PAINT(OnPaint); | |
| 59 MSG_WM_SIZE(OnSize); | |
| 60 MSG_WM_NOTIFY(OnNotify); | |
| 61 MSG_WM_COMMAND(OnCommand); | |
| 62 MSG_WM_DESTROY(OnDestroy); | |
| 63 MSG_WM_CONTEXTMENU(OnContextMenu); | |
| 64 MSG_WM_CTLCOLORBTN(OnCtlColorBtn); | |
| 65 MSG_WM_CTLCOLORSTATIC(OnCtlColorStatic) | |
| 66 END_MSG_MAP(); | |
| 67 | |
| 68 HWND GetControl() { | |
| 69 return control_; | |
| 70 } | |
| 71 | |
| 72 // Called when the parent is getting deleted. This control stays around until | |
| 73 // it gets the OnFinalMessage call. | |
| 74 void ResetParent() { | |
| 75 parent_ = NULL; | |
| 76 } | |
| 77 | |
| 78 void OnFinalMessage(HWND hwnd) { | |
| 79 if (parent_) | |
| 80 parent_->NativeControlDestroyed(); | |
| 81 delete this; | |
| 82 } | |
| 83 | |
| 84 private: | |
| 85 friend class NativeControl; | |
| 86 | |
| 87 LRESULT OnCreate(LPCREATESTRUCT create_struct) { | |
| 88 control_ = parent_->CreateNativeControl(m_hWnd); | |
| 89 | |
| 90 // We subclass the control hwnd so we get the WM_KEYDOWN messages. | |
| 91 original_handler_ = gfx::SetWindowProc( | |
| 92 control_, &NativeControl::NativeControlWndProc); | |
| 93 prop_.reset(new ViewProp(control_, kNativeControlKey , parent_)); | |
| 94 | |
| 95 ::ShowWindow(control_, SW_SHOW); | |
| 96 return 1; | |
| 97 } | |
| 98 | |
| 99 LRESULT OnEraseBkgnd(HDC dc) { | |
| 100 return 1; | |
| 101 } | |
| 102 | |
| 103 void OnPaint(HDC ignore) { | |
| 104 PAINTSTRUCT ps; | |
| 105 HDC dc = ::BeginPaint(*this, &ps); | |
| 106 ::EndPaint(*this, &ps); | |
| 107 } | |
| 108 | |
| 109 void OnSize(int type, const CSize& sz) { | |
| 110 ::MoveWindow(control_, 0, 0, sz.cx, sz.cy, TRUE); | |
| 111 } | |
| 112 | |
| 113 LRESULT OnCommand(UINT code, int id, HWND source) { | |
| 114 return parent_ ? parent_->OnCommand(code, id, source) : 0; | |
| 115 } | |
| 116 | |
| 117 LRESULT OnNotify(int w_param, LPNMHDR l_param) { | |
| 118 if (parent_) | |
| 119 return parent_->OnNotify(w_param, l_param); | |
| 120 else | |
| 121 return 0; | |
| 122 } | |
| 123 | |
| 124 void OnDestroy() { | |
| 125 if (parent_) | |
| 126 parent_->OnDestroy(); | |
| 127 } | |
| 128 | |
| 129 void OnContextMenu(HWND window, const POINT& location) { | |
| 130 if (parent_) | |
| 131 parent_->OnContextMenu(location); | |
| 132 } | |
| 133 | |
| 134 // We need to find an ancestor with a non-null background, and | |
| 135 // ask it for a (solid color) brush that approximates | |
| 136 // the background. The caller will use this when drawing | |
| 137 // the native control as a background color, particularly | |
| 138 // for radiobuttons and XP style pushbuttons. | |
| 139 LRESULT OnCtlColor(UINT msg, HDC dc, HWND control) { | |
| 140 const View *ancestor = parent_; | |
| 141 while (ancestor) { | |
| 142 const Background *background = ancestor->background(); | |
| 143 if (background) { | |
| 144 HBRUSH brush = background->GetNativeControlBrush(); | |
| 145 if (brush) | |
| 146 return reinterpret_cast<LRESULT>(brush); | |
| 147 } | |
| 148 ancestor = ancestor->parent(); | |
| 149 } | |
| 150 | |
| 151 // COLOR_BTNFACE is the default for dialog box backgrounds. | |
| 152 return reinterpret_cast<LRESULT>(GetSysColorBrush(COLOR_BTNFACE)); | |
| 153 } | |
| 154 | |
| 155 LRESULT OnCtlColorBtn(HDC dc, HWND control) { | |
| 156 return OnCtlColor(WM_CTLCOLORBTN, dc, control); | |
| 157 } | |
| 158 | |
| 159 LRESULT OnCtlColorStatic(HDC dc, HWND control) { | |
| 160 return OnCtlColor(WM_CTLCOLORSTATIC, dc, control); | |
| 161 } | |
| 162 | |
| 163 NativeControl* parent_; | |
| 164 HWND control_; | |
| 165 | |
| 166 // Message handler that was set before we reset it. | |
| 167 WNDPROC original_handler_; | |
| 168 | |
| 169 scoped_ptr<ViewProp> prop_; | |
| 170 | |
| 171 DISALLOW_COPY_AND_ASSIGN(NativeControlContainer); | |
| 172 }; | |
| 173 | |
| 174 NativeControl::NativeControl() : hwnd_view_(NULL), | |
| 175 container_(NULL), | |
| 176 fixed_width_(-1), | |
| 177 horizontal_alignment_(CENTER), | |
| 178 fixed_height_(-1), | |
| 179 vertical_alignment_(CENTER) { | |
| 180 SetFocusable(true); | |
| 181 } | |
| 182 | |
| 183 NativeControl::~NativeControl() { | |
| 184 if (container_) { | |
| 185 container_->ResetParent(); | |
| 186 ::DestroyWindow(*container_); | |
| 187 } | |
| 188 } | |
| 189 | |
| 190 void NativeControl::ValidateNativeControl() { | |
| 191 if (hwnd_view_ == NULL) { | |
| 192 hwnd_view_ = new NativeViewHost; | |
| 193 AddChildView(hwnd_view_); | |
| 194 } | |
| 195 | |
| 196 if (!container_ && visible()) { | |
| 197 container_ = new NativeControlContainer(this); | |
| 198 container_->Init(); | |
| 199 hwnd_view_->Attach(*container_); | |
| 200 if (!enabled()) | |
| 201 EnableWindow(GetNativeControlHWND(), enabled()); | |
| 202 | |
| 203 // This message ensures that the focus border is shown. | |
| 204 ::SendMessage(container_->GetControl(), | |
| 205 WM_CHANGEUISTATE, | |
| 206 MAKELPARAM(UIS_CLEAR, UISF_HIDEFOCUS), | |
| 207 0); | |
| 208 } | |
| 209 } | |
| 210 | |
| 211 void NativeControl::ViewHierarchyChanged( | |
| 212 const ViewHierarchyChangedDetails& details) { | |
| 213 if (details.is_add && details.parent != this && !container_ && GetWidget()) { | |
| 214 ValidateNativeControl(); | |
| 215 Layout(); | |
| 216 } | |
| 217 } | |
| 218 | |
| 219 void NativeControl::Layout() { | |
| 220 if (!container_ && GetWidget()) | |
| 221 ValidateNativeControl(); | |
| 222 | |
| 223 if (hwnd_view_) { | |
| 224 gfx::Rect lb = GetLocalBounds(); | |
| 225 | |
| 226 int x = lb.x(); | |
| 227 int y = lb.y(); | |
| 228 int width = lb.width(); | |
| 229 int height = lb.height(); | |
| 230 if (fixed_width_ > 0) { | |
| 231 width = std::min(fixed_width_, width); | |
| 232 switch (horizontal_alignment_) { | |
| 233 case LEADING: | |
| 234 // Nothing to do. | |
| 235 break; | |
| 236 case CENTER: | |
| 237 x += (lb.width() - width) / 2; | |
| 238 break; | |
| 239 case TRAILING: | |
| 240 x = x + lb.width() - width; | |
| 241 break; | |
| 242 default: | |
| 243 NOTREACHED(); | |
| 244 } | |
| 245 } | |
| 246 | |
| 247 if (fixed_height_ > 0) { | |
| 248 height = std::min(fixed_height_, height); | |
| 249 switch (vertical_alignment_) { | |
| 250 case LEADING: | |
| 251 // Nothing to do. | |
| 252 break; | |
| 253 case CENTER: | |
| 254 y += (lb.height() - height) / 2; | |
| 255 break; | |
| 256 case TRAILING: | |
| 257 y = y + lb.height() - height; | |
| 258 break; | |
| 259 default: | |
| 260 NOTREACHED(); | |
| 261 } | |
| 262 } | |
| 263 | |
| 264 hwnd_view_->SetBounds(x, y, width, height); | |
| 265 } | |
| 266 } | |
| 267 | |
| 268 void NativeControl::OnContextMenu(const POINT& location) { | |
| 269 if (!context_menu_controller()) | |
| 270 return; | |
| 271 | |
| 272 if (location.x == -1 && location.y == -1) { | |
| 273 ShowContextMenu(GetKeyboardContextMenuLocation(), | |
| 274 ui::MENU_SOURCE_KEYBOARD); | |
| 275 } else { | |
| 276 ShowContextMenu(gfx::Point(location), ui::MENU_SOURCE_MOUSE); | |
| 277 } | |
| 278 } | |
| 279 | |
| 280 void NativeControl::OnFocus() { | |
| 281 if (container_) { | |
| 282 DCHECK(container_->GetControl()); | |
| 283 ::SetFocus(container_->GetControl()); | |
| 284 NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_FOCUS, false); | |
| 285 } | |
| 286 } | |
| 287 | |
| 288 HWND NativeControl::GetNativeControlHWND() { | |
| 289 if (container_) | |
| 290 return container_->GetControl(); | |
| 291 else | |
| 292 return NULL; | |
| 293 } | |
| 294 | |
| 295 void NativeControl::NativeControlDestroyed() { | |
| 296 if (hwnd_view_) | |
| 297 hwnd_view_->Detach(); | |
| 298 container_ = NULL; | |
| 299 } | |
| 300 | |
| 301 void NativeControl::SetVisible(bool is_visible) { | |
| 302 if (is_visible != visible()) { | |
| 303 View::SetVisible(is_visible); | |
| 304 if (!is_visible && container_) | |
| 305 ::DestroyWindow(*container_); | |
| 306 else if (is_visible && !container_) | |
| 307 ValidateNativeControl(); | |
| 308 } | |
| 309 } | |
| 310 | |
| 311 void NativeControl::OnEnabledChanged() { | |
| 312 View::OnEnabledChanged(); | |
| 313 if (GetNativeControlHWND()) | |
| 314 EnableWindow(GetNativeControlHWND(), enabled()); | |
| 315 } | |
| 316 | |
| 317 void NativeControl::OnPaint(gfx::Canvas* canvas) { | |
| 318 } | |
| 319 | |
| 320 void NativeControl::VisibilityChanged(View* starting_from, bool is_visible) { | |
| 321 SetVisible(is_visible); | |
| 322 } | |
| 323 | |
| 324 void NativeControl::SetFixedWidth(int width, Alignment alignment) { | |
| 325 DCHECK_GT(width, 0); | |
| 326 fixed_width_ = width; | |
| 327 horizontal_alignment_ = alignment; | |
| 328 } | |
| 329 | |
| 330 void NativeControl::SetFixedHeight(int height, Alignment alignment) { | |
| 331 DCHECK_GT(height, 0); | |
| 332 fixed_height_ = height; | |
| 333 vertical_alignment_ = alignment; | |
| 334 } | |
| 335 | |
| 336 DWORD NativeControl::GetAdditionalExStyle() const { | |
| 337 // If the UI for the view is mirrored, we should make sure we add the | |
| 338 // extended window style for a right-to-left layout so the subclass creates | |
| 339 // a mirrored HWND for the underlying control. | |
| 340 DWORD ex_style = 0; | |
| 341 if (base::i18n::IsRTL()) | |
| 342 ex_style |= l10n_util::GetExtendedStyles(); | |
| 343 | |
| 344 return ex_style; | |
| 345 } | |
| 346 | |
| 347 DWORD NativeControl::GetAdditionalRTLStyle() const { | |
| 348 // If the UI for the view is mirrored, we should make sure we add the | |
| 349 // extended window style for a right-to-left layout so the subclass creates | |
| 350 // a mirrored HWND for the underlying control. | |
| 351 DWORD ex_style = 0; | |
| 352 if (base::i18n::IsRTL()) | |
| 353 ex_style |= l10n_util::GetExtendedTooltipStyles(); | |
| 354 | |
| 355 return ex_style; | |
| 356 } | |
| 357 | |
| 358 // static | |
| 359 LRESULT CALLBACK NativeControl::NativeControlWndProc(HWND window, | |
| 360 UINT message, | |
| 361 WPARAM w_param, | |
| 362 LPARAM l_param) { | |
| 363 NativeControl* native_control = static_cast<NativeControl*>( | |
| 364 ViewProp::GetValue(window, kNativeControlKey)); | |
| 365 DCHECK(native_control); | |
| 366 WNDPROC original_handler = native_control->container_->original_handler_; | |
| 367 DCHECK(original_handler); | |
| 368 | |
| 369 if (message == WM_KEYDOWN && | |
| 370 native_control->OnKeyDown(ui::KeyboardCodeForWindowsKeyCode(w_param))) { | |
| 371 return 0; | |
| 372 } else if (message == WM_SETFOCUS) { | |
| 373 // Let the focus manager know that the focus changed. | |
| 374 FocusManager* focus_manager = native_control->GetFocusManager(); | |
| 375 if (focus_manager) { | |
| 376 focus_manager->SetFocusedView(native_control); | |
| 377 } else { | |
| 378 NOTREACHED(); | |
| 379 } | |
| 380 } else if (message == WM_DESTROY) { | |
| 381 gfx::SetWindowProc(window, reinterpret_cast<WNDPROC>(original_handler)); | |
| 382 native_control->container_->prop_.reset(); | |
| 383 } | |
| 384 | |
| 385 return CallWindowProc(reinterpret_cast<WNDPROC>(original_handler), window, | |
| 386 message, w_param, l_param); | |
| 387 } | |
| 388 | |
| 389 } // namespace views | |
| OLD | NEW |