summaryrefslogtreecommitdiffstats
path: root/chrome/views/native_control.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/views/native_control.cc')
-rw-r--r--chrome/views/native_control.cc381
1 files changed, 381 insertions, 0 deletions
diff --git a/chrome/views/native_control.cc b/chrome/views/native_control.cc
new file mode 100644
index 0000000..d9ef5a9
--- /dev/null
+++ b/chrome/views/native_control.cc
@@ -0,0 +1,381 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/views/native_control.h"
+
+#include <atlbase.h>
+#include <atlapp.h>
+#include <atlcrack.h>
+#include <atlframe.h>
+
+#include "base/win_util.h"
+#include "chrome/views/border.h"
+#include "chrome/views/focus_manager.h"
+#include "chrome/views/view_container.h"
+#include "chrome/views/hwnd_view.h"
+#include "chrome/views/background.h"
+#include "base/gfx/native_theme.h"
+
+namespace ChromeViews {
+
+// Maps to the original WNDPROC for the controller window before we subclassed
+// it.
+static const wchar_t* const kHandlerKey =
+ L"__CONTROL_ORIGINAL_MESSAGE_HANDLER__";
+
+// Maps to the NativeControl.
+static const wchar_t* const kNativeControlKey = L"__NATIVE_CONTROL__";
+
+class NativeControlContainer : public CWindowImpl<NativeControlContainer,
+ CWindow,
+ CWinTraits<WS_CHILD | WS_CLIPSIBLINGS |
+ WS_CLIPCHILDREN>> {
+ public:
+
+ explicit NativeControlContainer(NativeControl* parent) : parent_(parent),
+ control_(NULL) {
+ Create(parent->GetViewContainer()->GetHWND());
+ ::ShowWindow(m_hWnd, SW_SHOW);
+ }
+
+ virtual ~NativeControlContainer() {
+ }
+
+ // NOTE: If you add a new message, be sure and verify parent_ is valid before
+ // calling into parent_.
+ DECLARE_FRAME_WND_CLASS(L"ChromeViewsNativeControlContainer", NULL);
+ BEGIN_MSG_MAP(NativeControlContainer);
+ MSG_WM_CREATE(OnCreate);
+ MSG_WM_ERASEBKGND(OnEraseBkgnd);
+ MSG_WM_PAINT(OnPaint);
+ MSG_WM_SIZE(OnSize);
+ MSG_WM_NOTIFY(OnNotify);
+ MSG_WM_COMMAND(OnCommand);
+ MSG_WM_DESTROY(OnDestroy);
+ MSG_WM_CONTEXTMENU(OnContextMenu);
+ MSG_WM_CTLCOLORBTN(OnCtlColorBtn);
+ MSG_WM_CTLCOLORSTATIC(OnCtlColorStatic)
+ END_MSG_MAP();
+
+ HWND GetControl() {
+ return control_;
+ }
+
+ // Called when the parent is getting deleted. This control stays around until
+ // it gets the OnFinalMessage call.
+ void ResetParent() {
+ parent_ = NULL;
+ }
+
+ void OnFinalMessage(HWND hwnd) {
+ if (parent_)
+ parent_->NativeControlDestroyed();
+ delete this;
+ }
+ private:
+
+ LRESULT OnCreate(LPCREATESTRUCT create_struct) {
+ control_ = parent_->CreateNativeControl(m_hWnd);
+ FocusManager::InstallFocusSubclass(control_, parent_);
+ if (parent_->NotifyOnKeyDown()) {
+ // We subclass the control hwnd so we get the WM_KEYDOWN messages.
+ WNDPROC original_handler =
+ win_util::SetWindowProc(control_,
+ &NativeControl::NativeControlWndProc);
+ SetProp(control_, kHandlerKey, original_handler);
+ SetProp(control_, kNativeControlKey , parent_);
+ }
+ ::ShowWindow(control_, SW_SHOW);
+ return 1;
+ }
+
+ LRESULT OnEraseBkgnd(HDC dc) {
+ return 1;
+ }
+
+ void OnPaint(HDC ignore) {
+ PAINTSTRUCT ps;
+ HDC dc = ::BeginPaint(*this, &ps);
+ ::EndPaint(*this, &ps);
+ }
+
+ void OnSize(int type, const CSize& sz) {
+ ::MoveWindow(control_, 0, 0, sz.cx, sz.cy, TRUE);
+ }
+
+ LRESULT OnCommand(UINT code, int id, HWND source) {
+ return parent_ ? parent_->OnCommand(code, id, source) : 0;
+ }
+
+ LRESULT OnNotify(int w_param, LPNMHDR l_param) {
+ if (parent_)
+ return parent_->OnNotify(w_param, l_param);
+ else
+ return 0;
+ }
+
+ void OnDestroy() {
+ if (parent_)
+ parent_->OnDestroy();
+ }
+
+ void OnContextMenu(HWND window, const CPoint& location) {
+ if (parent_)
+ parent_->OnContextMenu(location);
+ }
+
+ // We need to find an ancestor with a non-null background, and
+ // ask it for a (solid color) brush that approximates
+ // the background. The caller will use this when drawing
+ // the native control as a background color, particularly
+ // for radiobuttons and XP style pushbuttons.
+ LRESULT OnCtlColor(UINT msg, HDC dc, HWND control) {
+ const View *ancestor = parent_;
+ while (ancestor) {
+ const Background *background = ancestor->GetBackground();
+ if (background) {
+ HBRUSH brush = background->GetNativeControlBrush();
+ if (brush)
+ return reinterpret_cast<LRESULT>(brush);
+ }
+ ancestor = ancestor->GetParent();
+ }
+
+ // COLOR_BTNFACE is the default for dialog box backgrounds.
+ return reinterpret_cast<LRESULT>(GetSysColorBrush(COLOR_BTNFACE));
+ }
+
+ LRESULT OnCtlColorBtn(HDC dc, HWND control) {
+ return OnCtlColor(WM_CTLCOLORBTN, dc, control);
+ }
+
+ LRESULT OnCtlColorStatic(HDC dc, HWND control) {
+ return OnCtlColor(WM_CTLCOLORSTATIC, dc, control);
+ }
+
+ NativeControl* parent_;
+ HWND control_;
+ DISALLOW_EVIL_CONSTRUCTORS(NativeControlContainer);
+};
+
+NativeControl::NativeControl() : hwnd_view_(NULL),
+ container_(NULL),
+ fixed_width_(-1),
+ horizontal_alignment_(CENTER),
+ fixed_height_(-1),
+ vertical_alignment_(CENTER) {
+ enabled_ = true;
+ focusable_ = true;
+}
+
+NativeControl::~NativeControl() {
+ if (container_) {
+ container_->ResetParent();
+ ::DestroyWindow(*container_);
+ }
+}
+
+void NativeControl::ValidateNativeControl() {
+ if (hwnd_view_ == NULL) {
+ hwnd_view_ = new HWNDView();
+ AddChildView(hwnd_view_);
+ }
+
+ if (!container_ && IsVisible()) {
+ container_ = new NativeControlContainer(this);
+ hwnd_view_->Attach(*container_);
+ if (!enabled_)
+ EnableWindow(GetNativeControlHWND(), enabled_);
+
+ // This message ensures that the focus border is shown.
+ ::SendMessage(container_->GetControl(),
+ WM_CHANGEUISTATE,
+ MAKELPARAM(UIS_CLEAR, UISF_HIDEFOCUS),
+ 0);
+ }
+}
+
+void NativeControl::ViewHierarchyChanged(bool is_add, View *parent,
+ View *child) {
+ if (is_add && GetViewContainer()) {
+ ValidateNativeControl();
+ Layout();
+ }
+}
+
+void NativeControl::Layout() {
+ if (!container_ && GetViewContainer())
+ ValidateNativeControl();
+
+ if (hwnd_view_) {
+ CRect lb;
+ GetLocalBounds(&lb, false);
+
+ int x = lb.left;
+ int y = lb.top;
+ int width = lb.Width();
+ int height = lb.Height();
+ if (fixed_width_ > 0) {
+ width = std::min(fixed_width_, width);
+ switch (horizontal_alignment_) {
+ case LEADING:
+ // Nothing to do.
+ break;
+ case CENTER:
+ x += (lb.Width() - width) / 2;
+ break;
+ case TRAILING:
+ x = x + lb.Width() - width;
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+
+ if (fixed_height_ > 0) {
+ height = std::min(fixed_height_, height);
+ switch (vertical_alignment_) {
+ case LEADING:
+ // Nothing to do.
+ break;
+ case CENTER:
+ y += (lb.Height() - height) / 2;
+ break;
+ case TRAILING:
+ y = y + lb.Height() - height;
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+
+ hwnd_view_->SetBounds(x, y, width, height);
+ }
+}
+
+void NativeControl::DidChangeBounds(const CRect& previous,
+ const CRect& current) {
+ Layout();
+}
+
+void NativeControl::Focus() {
+ if (container_) {
+ DCHECK(container_->GetControl());
+ ::SetFocus(container_->GetControl());
+ }
+}
+
+HWND NativeControl::GetNativeControlHWND() {
+ if (container_)
+ return container_->GetControl();
+ else
+ return NULL;
+}
+
+void NativeControl::NativeControlDestroyed() {
+ if (hwnd_view_)
+ hwnd_view_->Detach();
+ container_ = NULL;
+}
+
+void NativeControl::SetVisible(bool f) {
+ if (f != IsVisible()) {
+ View::SetVisible(f);
+ if (!f && container_) {
+ ::DestroyWindow(*container_);
+ } else if (f && !container_) {
+ ValidateNativeControl();
+ }
+ }
+}
+
+void NativeControl::SetEnabled(bool enabled) {
+ if (enabled_ != enabled) {
+ View::SetEnabled(enabled);
+ if (GetNativeControlHWND()) {
+ EnableWindow(GetNativeControlHWND(), enabled_);
+ }
+ }
+}
+
+void NativeControl::Paint(ChromeCanvas* canvas) {
+}
+
+void NativeControl::VisibilityChanged(View* starting_from, bool is_visible) {
+ SetVisible(is_visible);
+}
+
+void NativeControl::SetFixedWidth(int width, Alignment alignment) {
+ DCHECK(width > 0);
+ fixed_width_ = width;
+ horizontal_alignment_ = alignment;
+}
+
+void NativeControl::SetFixedHeight(int height, Alignment alignment) {
+ DCHECK(height > 0);
+ fixed_height_ = height;
+ vertical_alignment_ = alignment;
+}
+
+DWORD NativeControl::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 (UILayoutIsRightToLeft())
+ ex_style |= l10n_util::GetExtendedStyles();
+
+ return ex_style;
+}
+
+// static
+LRESULT CALLBACK NativeControl::NativeControlWndProc(HWND window, UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ HANDLE original_handler = GetProp(window, kHandlerKey);
+ DCHECK(original_handler);
+ NativeControl* native_control =
+ static_cast<NativeControl*>(GetProp(window, kNativeControlKey));
+ DCHECK(native_control);
+
+ if (message == WM_KEYDOWN) {
+ if (native_control->OnKeyDown(static_cast<int>(w_param)))
+ return 0;
+ } else if (message == WM_DESTROY) {
+ win_util::SetWindowProc(window,
+ reinterpret_cast<WNDPROC>(original_handler));
+ RemoveProp(window, kHandlerKey);
+ RemoveProp(window, kNativeControlKey);
+ }
+
+ return CallWindowProc(reinterpret_cast<WNDPROC>(original_handler), window,
+ message, w_param, l_param);
+}
+
+}