/* * Copyright (C) 2010 Apple 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: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. */ // NOTE: This implementation is very similar to the implementation of popups in WebCore::PopupMenuWin. // We should try and factor out the common bits and share them. #include "config.h" #include "WebPopupMenuProxyWin.h" #include "WebView.h" #include #include #include #include #include using namespace WebCore; using namespace std; namespace WebKit { static const LPCWSTR kWebKit2WebPopupMenuProxyWindowClassName = L"WebKit2WebPopupMenuProxyWindowClass"; static const int defaultAnimationDuration = 200; static const int maxPopupHeight = 320; static const int popupWindowBorderWidth = 1; static const int separatorPadding = 4; static const int separatorHeight = 1; // This is used from within our custom message pump when we want to send a // message to the web view and not have our message stolen and sent to // the popup window. static const UINT WM_HOST_WINDOW_FIRST = WM_USER; static const UINT WM_HOST_WINDOW_CHAR = WM_USER + WM_CHAR; static const UINT WM_HOST_WINDOW_MOUSEMOVE = WM_USER + WM_MOUSEMOVE; static inline bool isASCIIPrintable(unsigned c) { return c >= 0x20 && c <= 0x7E; } static void translatePoint(LPARAM& lParam, HWND from, HWND to) { POINT pt; pt.x = static_cast(GET_X_LPARAM(lParam)); pt.y = static_cast(GET_Y_LPARAM(lParam)); ::MapWindowPoints(from, to, &pt, 1); lParam = MAKELPARAM(pt.x, pt.y); } LRESULT CALLBACK WebPopupMenuProxyWin::WebPopupMenuProxyWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { LONG_PTR longPtr = ::GetWindowLongPtr(hWnd, 0); if (WebPopupMenuProxyWin* popupMenuProxy = reinterpret_cast(longPtr)) return popupMenuProxy->wndProc(hWnd, message, wParam, lParam); if (message == WM_CREATE) { LPCREATESTRUCT createStruct = reinterpret_cast(lParam); // Associate the WebView with the window. ::SetWindowLongPtr(hWnd, 0, (LONG_PTR)createStruct->lpCreateParams); return 0; } return ::DefWindowProc(hWnd, message, wParam, lParam); } LRESULT WebPopupMenuProxyWin::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { LRESULT lResult = 0; bool handled = true; switch (message) { case WM_MOUSEACTIVATE: lResult = onMouseActivate(hWnd, message, wParam, lParam, handled); break; case WM_SIZE: lResult = onSize(hWnd, message, wParam, lParam, handled); break; case WM_KEYDOWN: lResult = onKeyDown(hWnd, message, wParam, lParam, handled); break; case WM_CHAR: lResult = onChar(hWnd, message, wParam, lParam, handled); break; case WM_MOUSEMOVE: lResult = onMouseMove(hWnd, message, wParam, lParam, handled); break; case WM_LBUTTONDOWN: lResult = onLButtonDown(hWnd, message, wParam, lParam, handled); break; case WM_LBUTTONUP: lResult = onLButtonUp(hWnd, message, wParam, lParam, handled); break; case WM_MOUSEWHEEL: lResult = onMouseWheel(hWnd, message, wParam, lParam, handled); break; case WM_PAINT: lResult = onPaint(hWnd, message, wParam, lParam, handled); break; case WM_PRINTCLIENT: lResult = onPrintClient(hWnd, message, wParam, lParam, handled); break; default: handled = false; break; } if (!handled) lResult = ::DefWindowProc(hWnd, message, wParam, lParam); return lResult; } bool WebPopupMenuProxyWin::registerWindowClass() { static bool haveRegisteredWindowClass = false; if (haveRegisteredWindowClass) return true; haveRegisteredWindowClass = true; WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_DROPSHADOW; wcex.lpfnWndProc = WebPopupMenuProxyWin::WebPopupMenuProxyWndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = sizeof(WebPopupMenuProxyWin*); wcex.hInstance = instanceHandle(); wcex.hIcon = 0; wcex.hCursor = ::LoadCursor(0, IDC_ARROW); wcex.hbrBackground = 0; wcex.lpszMenuName = 0; wcex.lpszClassName = kWebKit2WebPopupMenuProxyWindowClassName; wcex.hIconSm = 0; return !!::RegisterClassEx(&wcex); } WebPopupMenuProxyWin::WebPopupMenuProxyWin(WebView* webView, WebPopupMenuProxy::Client* client) : WebPopupMenuProxy(client) , m_webView(webView) , m_newSelectedIndex(0) , m_popup(0) , m_DC(0) , m_bmp(0) , m_itemHeight(0) , m_scrollOffset(0) , m_wheelDelta(0) , m_focusedIndex(0) , m_wasClicked(false) , m_scrollbarCapturingMouse(false) , m_showPopup(false) { } WebPopupMenuProxyWin::~WebPopupMenuProxyWin() { if (m_bmp) ::DeleteObject(m_bmp); if (m_DC) ::DeleteDC(m_DC); if (m_popup) ::DestroyWindow(m_popup); if (m_scrollbar) m_scrollbar->setParent(0); } void WebPopupMenuProxyWin::showPopupMenu(const IntRect& rect, TextDirection, double, const Vector& items, const PlatformPopupMenuData& data, int32_t selectedIndex) { m_items = items; m_data = data; m_newSelectedIndex = selectedIndex; calculatePositionAndSize(rect); if (clientRect().isEmpty()) return; HWND hostWindow = m_webView->window(); if (!m_scrollbar && visibleItems() < m_items.size()) { m_scrollbar = Scrollbar::createNativeScrollbar(this, VerticalScrollbar, SmallScrollbar); m_scrollbar->styleChanged(); } if (!m_popup) { registerWindowClass(); DWORD exStyle = WS_EX_LTRREADING; m_popup = ::CreateWindowEx(exStyle, kWebKit2WebPopupMenuProxyWindowClassName, TEXT("PopupMenu"), WS_POPUP | WS_BORDER, m_windowRect.x(), m_windowRect.y(), m_windowRect.width(), m_windowRect.height(), hostWindow, 0, instanceHandle(), this); if (!m_popup) return; } BOOL shouldAnimate = FALSE; ::SystemParametersInfo(SPI_GETCOMBOBOXANIMATION, 0, &shouldAnimate, 0); if (shouldAnimate) { RECT viewRect = {0}; ::GetWindowRect(hostWindow, &viewRect); if (!::IsRectEmpty(&viewRect)) { // Popups should slide into view away from the