summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/win/PopupMenuWin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/platform/win/PopupMenuWin.cpp')
-rw-r--r--WebCore/platform/win/PopupMenuWin.cpp826
1 files changed, 826 insertions, 0 deletions
diff --git a/WebCore/platform/win/PopupMenuWin.cpp b/WebCore/platform/win/PopupMenuWin.cpp
new file mode 100644
index 0000000..466d881
--- /dev/null
+++ b/WebCore/platform/win/PopupMenuWin.cpp
@@ -0,0 +1,826 @@
+/*
+ * Copyright (C) 2006, 2007 Apple Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+#include "PopupMenu.h"
+
+#include "Document.h"
+#include "FloatRect.h"
+#include "FontSelector.h"
+#include "Frame.h"
+#include "FrameView.h"
+#include "GraphicsContext.h"
+#include "HTMLNames.h"
+#include "Page.h"
+#include "PlatformMouseEvent.h"
+#include "PlatformScreen.h"
+#include "PlatformScrollBar.h"
+#include "RenderTheme.h"
+#include "RenderView.h"
+#include "SimpleFontData.h"
+#include <tchar.h>
+#include <windows.h>
+
+using std::min;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+// Default Window animation duration in milliseconds
+static const int defaultAnimationDuration = 200;
+// Maximum height of a popup window
+static const int maxPopupHeight = 320;
+
+static const int popupWindowAlphaPercent = 95;
+
+const int optionSpacingMiddle = 1;
+const int popupWindowBorderWidth = 1;
+
+static LPCTSTR kPopupWindowClassName = _T("PopupWindowClass");
+static ATOM registerPopup();
+static LRESULT CALLBACK PopupWndProc(HWND, UINT, WPARAM, LPARAM);
+
+// FIXME: Remove this as soon as practical.
+static inline bool isASCIIPrintable(unsigned c)
+{
+ return c >= 0x20 && c <= 0x7E;
+}
+
+PopupMenu::PopupMenu(PopupMenuClient* client)
+ : RefCounted<PopupMenu>(0)
+ , m_popupClient(client)
+ , m_scrollBar(0)
+ , m_popup(0)
+ , m_DC(0)
+ , m_bmp(0)
+ , m_wasClicked(false)
+ , m_itemHeight(0)
+ , m_scrollOffset(0)
+ , m_wheelDelta(0)
+ , m_focusedIndex(0)
+ , m_scrollbarCapturingMouse(false)
+{
+}
+
+PopupMenu::~PopupMenu()
+{
+ if (m_bmp)
+ ::DeleteObject(m_bmp);
+ if (m_DC)
+ ::DeleteObject(m_DC);
+ if (m_popup)
+ ::DestroyWindow(m_popup);
+}
+
+void PopupMenu::show(const IntRect& r, FrameView* v, int index)
+{
+ calculatePositionAndSize(r, v);
+ if (clientRect().isEmpty())
+ return;
+
+ if (!m_popup) {
+ registerPopup();
+
+ DWORD exStyle = WS_EX_LAYERED | WS_EX_LTRREADING;
+
+ // Even though we already know our size and location at this point, we pass (0,0,0,0) as our size/location here.
+ // We need to wait until after the call to ::SetWindowLongPtr to set our size so that in our WM_SIZE handler we can get access to the PopupMenu object
+ m_popup = ::CreateWindowEx(exStyle, kPopupWindowClassName, _T("PopupMenu"),
+ WS_POPUP | WS_BORDER,
+ 0, 0, 0, 0,
+ v->containingWindow(), 0, 0, 0);
+
+ if (!m_popup)
+ return;
+
+ ::SetWindowLongPtr(m_popup, 0, (LONG_PTR)this);
+ ::SetLayeredWindowAttributes(m_popup, 0, (255 * popupWindowAlphaPercent) / 100, LWA_ALPHA);
+ }
+
+ if (!m_scrollBar)
+ if (visibleItems() < client()->listSize()) {
+ // We need a scroll bar
+ m_scrollBar = new PlatformScrollbar(this, VerticalScrollbar, SmallScrollbar);
+ m_scrollBar->setContainingWindow(m_popup);
+ }
+
+ ::SetWindowPos(m_popup, HWND_TOP, m_windowRect.x(), m_windowRect.y(), m_windowRect.width(), m_windowRect.height(), 0);
+
+ // Determine whether we should animate our popups
+ // Note: Must use 'BOOL' and 'FALSE' instead of 'bool' and 'false' to avoid stack corruption with SystemParametersInfo
+ BOOL shouldAnimate = FALSE;
+#ifdef CAN_ANIMATE_TRANSPARENT_WINDOWS_SMOOTHLY
+ ::SystemParametersInfo(SPI_GETCOMBOBOXANIMATION, 0, &shouldAnimate, 0);
+#endif
+
+ if (shouldAnimate) {
+ RECT viewRect = {0};
+ ::GetWindowRect(v->containingWindow(), &viewRect);
+
+ if (!::IsRectEmpty(&viewRect)) {
+ // Popups should slide into view away from the <select> box
+ // NOTE: This may have to change for Vista
+ DWORD slideDirection = (m_windowRect.y() < viewRect.top + v->contentsToWindow(r.location()).y()) ? AW_VER_NEGATIVE : AW_VER_POSITIVE;
+
+ ::AnimateWindow(m_popup, defaultAnimationDuration, AW_SLIDE | slideDirection | AW_ACTIVATE);
+ }
+ } else
+ ::ShowWindow(m_popup, SW_SHOWNORMAL);
+ ::SetCapture(m_popup);
+
+ if (client()) {
+ int index = client()->selectedIndex();
+ if (index >= 0)
+ setFocusedIndex(index);
+ }
+}
+
+void PopupMenu::hide()
+{
+ ::ShowWindow(m_popup, SW_HIDE);
+}
+
+const int endOfLinePadding = 2;
+void PopupMenu::calculatePositionAndSize(const IntRect& r, FrameView* v)
+{
+ // r is in absolute document coordinates, but we want to be in screen coordinates
+
+ // First, move to WebView coordinates
+ IntRect rScreenCoords(v->contentsToWindow(r.location()), r.size());
+
+ // Then, translate to screen coordinates
+ POINT location(rScreenCoords.location());
+ if (!::ClientToScreen(v->containingWindow(), &location))
+ return;
+
+ rScreenCoords.setLocation(location);
+
+ // First, determine the popup's height
+ int itemCount = client()->listSize();
+ m_itemHeight = client()->clientStyle()->font().height() + optionSpacingMiddle;
+ int naturalHeight = m_itemHeight * itemCount;
+ int popupHeight = min(maxPopupHeight, naturalHeight);
+ // The popup should show an integral number of items (i.e. no partial items should be visible)
+ popupHeight -= popupHeight % m_itemHeight;
+
+ // Next determine its width
+ int popupWidth = 0;
+ for (int i = 0; i < itemCount; ++i) {
+ String text = client()->itemText(i);
+ if (text.isEmpty())
+ continue;
+
+ popupWidth = max(popupWidth, client()->clientStyle()->font().width(TextRun(text.characters(), text.length())));
+ }
+
+ if (naturalHeight > maxPopupHeight)
+ // We need room for a scrollbar
+ popupWidth += PlatformScrollbar::verticalScrollbarWidth(SmallScrollbar);
+
+ // Add padding to align the popup text with the <select> text
+ // Note: We can't add paddingRight() because that value includes the width
+ // of the dropdown button, so we must use our own endOfLinePadding constant.
+ popupWidth += max(0, endOfLinePadding - client()->clientInsetRight()) + max(0, client()->clientPaddingLeft() - client()->clientInsetLeft());
+
+ // Leave room for the border
+ popupWidth += 2 * popupWindowBorderWidth;
+ popupHeight += 2 * popupWindowBorderWidth;
+
+ // The popup should be at least as wide as the control on the page
+ popupWidth = max(rScreenCoords.width() - client()->clientInsetLeft() - client()->clientInsetRight(), popupWidth);
+
+ // Always left-align items in the popup. This matches popup menus on the mac.
+ int popupX = rScreenCoords.x() + client()->clientInsetLeft();
+
+ IntRect popupRect(popupX, rScreenCoords.bottom(), popupWidth, popupHeight);
+
+ // The popup needs to stay within the bounds of the screen and not overlap any toolbars
+ FloatRect screen = screenAvailableRect(v);
+
+ // Check that we don't go off the screen vertically
+ if (popupRect.bottom() > screen.height()) {
+ // The popup will go off the screen, so try placing it above the client
+ if (rScreenCoords.y() - popupRect.height() < 0) {
+ // The popup won't fit above, either, so place it whereever's bigger and resize it to fit
+ if ((rScreenCoords.y() + rScreenCoords.height() / 2) < (screen.height() / 2)) {
+ // Below is bigger
+ popupRect.setHeight(screen.height() - popupRect.y());
+ } else {
+ // Above is bigger
+ popupRect.setY(0);
+ popupRect.setHeight(rScreenCoords.y());
+ }
+ } else {
+ // The popup fits above, so reposition it
+ popupRect.setY(rScreenCoords.y() - popupRect.height());
+ }
+ }
+
+ // Check that we don't go off the screen horizontally
+ if (popupRect.x() < screen.x()) {
+ popupRect.setWidth(popupRect.width() - (screen.x() - popupRect.x()));
+ popupRect.setX(screen.x());
+ }
+ m_windowRect = popupRect;
+ return;
+}
+
+bool PopupMenu::setFocusedIndex(int i, bool hotTracking)
+{
+ if (i < 0 || i >= client()->listSize() || i == focusedIndex())
+ return false;
+
+ if (!client()->itemIsEnabled(i))
+ return false;
+
+ invalidateItem(focusedIndex());
+ invalidateItem(i);
+
+ m_focusedIndex = i;
+
+ if (!hotTracking)
+ client()->setTextFromItem(i);
+
+ if (!scrollToRevealSelection())
+ ::UpdateWindow(m_popup);
+
+ return true;
+}
+
+int PopupMenu::visibleItems() const
+{
+ return clientRect().height() / m_itemHeight;
+}
+
+int PopupMenu::listIndexAtPoint(const IntPoint& point) const
+{
+ return m_scrollOffset + point.y() / m_itemHeight;
+}
+
+int PopupMenu::focusedIndex() const
+{
+ return m_focusedIndex;
+}
+
+void PopupMenu::focusFirst()
+{
+ if (!client())
+ return;
+
+ int size = client()->listSize();
+
+ for (int i = 0; i < size; ++i)
+ if (client()->itemIsEnabled(i)) {
+ setFocusedIndex(i);
+ break;
+ }
+}
+
+void PopupMenu::focusLast()
+{
+ if (!client())
+ return;
+
+ int size = client()->listSize();
+
+ for (int i = size - 1; i > 0; --i)
+ if (client()->itemIsEnabled(i)) {
+ setFocusedIndex(i);
+ break;
+ }
+}
+
+bool PopupMenu::down(unsigned lines)
+{
+ if (!client())
+ return false;
+
+ int size = client()->listSize();
+
+ int lastSelectableIndex, selectedListIndex;
+ lastSelectableIndex = selectedListIndex = focusedIndex();
+ for (int i = selectedListIndex + 1; i >= 0 && i < size; ++i)
+ if (client()->itemIsEnabled(i)) {
+ lastSelectableIndex = i;
+ if (i >= selectedListIndex + (int)lines)
+ break;
+ }
+
+ return setFocusedIndex(lastSelectableIndex);
+}
+
+bool PopupMenu::up(unsigned lines)
+{
+ if (!client())
+ return false;
+
+ int size = client()->listSize();
+
+ int lastSelectableIndex, selectedListIndex;
+ lastSelectableIndex = selectedListIndex = focusedIndex();
+ for (int i = selectedListIndex - 1; i >= 0 && i < size; --i)
+ if (client()->itemIsEnabled(i)) {
+ lastSelectableIndex = i;
+ if (i <= selectedListIndex - (int)lines)
+ break;
+ }
+
+ return setFocusedIndex(lastSelectableIndex);
+}
+
+void PopupMenu::invalidateItem(int index)
+{
+ if (!m_popup)
+ return;
+
+ IntRect damageRect(clientRect());
+ damageRect.setY(m_itemHeight * (index - m_scrollOffset));
+ damageRect.setHeight(m_itemHeight);
+ if (m_scrollBar)
+ damageRect.setWidth(damageRect.width() - m_scrollBar->frameGeometry().width());
+
+ RECT r = damageRect;
+ ::InvalidateRect(m_popup, &r, TRUE);
+}
+
+IntRect PopupMenu::clientRect() const
+{
+ IntRect clientRect = m_windowRect;
+ clientRect.inflate(-popupWindowBorderWidth);
+ clientRect.setLocation(IntPoint(0, 0));
+ return clientRect;
+}
+
+void PopupMenu::incrementWheelDelta(int delta)
+{
+ m_wheelDelta += delta;
+}
+
+void PopupMenu::reduceWheelDelta(int delta)
+{
+ ASSERT(delta >= 0);
+ ASSERT(delta <= abs(m_wheelDelta));
+
+ if (m_wheelDelta > 0)
+ m_wheelDelta -= delta;
+ else if (m_wheelDelta < 0)
+ m_wheelDelta += delta;
+ else
+ return;
+}
+
+bool PopupMenu::scrollToRevealSelection()
+{
+ if (!m_scrollBar)
+ return false;
+
+ int index = focusedIndex();
+
+ if (index < m_scrollOffset) {
+ m_scrollBar->setValue(index);
+ return true;
+ }
+
+ if (index >= m_scrollOffset + visibleItems()) {
+ m_scrollBar->setValue(index - visibleItems() + 1);
+ return true;
+ }
+
+ return false;
+}
+
+void PopupMenu::updateFromElement()
+{
+ if (!m_popup)
+ return;
+
+ m_focusedIndex = client()->selectedIndex();
+
+ ::InvalidateRect(m_popup, 0, TRUE);
+ if (!scrollToRevealSelection())
+ ::UpdateWindow(m_popup);
+}
+
+bool PopupMenu::itemWritingDirectionIsNatural()
+{
+ return true;
+}
+
+const int separatorPadding = 4;
+const int separatorHeight = 1;
+void PopupMenu::paint(const IntRect& damageRect, HDC hdc)
+{
+ if (!m_popup)
+ return;
+
+ if (!m_DC) {
+ m_DC = ::CreateCompatibleDC(::GetDC(m_popup));
+ if (!m_DC)
+ return;
+ }
+
+ if (m_bmp) {
+ bool keepBitmap = false;
+ BITMAP bitmap;
+ if (GetObject(m_bmp, sizeof(bitmap), &bitmap))
+ keepBitmap = bitmap.bmWidth == clientRect().width()
+ && bitmap.bmHeight == clientRect().height();
+ if (!keepBitmap) {
+ DeleteObject(m_bmp);
+ m_bmp = 0;
+ }
+ }
+ if (!m_bmp) {
+ BITMAPINFO bitmapInfo;
+ bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bitmapInfo.bmiHeader.biWidth = clientRect().width();
+ bitmapInfo.bmiHeader.biHeight = -clientRect().height();
+ bitmapInfo.bmiHeader.biPlanes = 1;
+ bitmapInfo.bmiHeader.biBitCount = 32;
+ bitmapInfo.bmiHeader.biCompression = BI_RGB;
+ bitmapInfo.bmiHeader.biSizeImage = 0;
+ bitmapInfo.bmiHeader.biXPelsPerMeter = 0;
+ bitmapInfo.bmiHeader.biYPelsPerMeter = 0;
+ bitmapInfo.bmiHeader.biClrUsed = 0;
+ bitmapInfo.bmiHeader.biClrImportant = 0;
+
+ void* pixels = 0;
+ m_bmp = ::CreateDIBSection(m_DC, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0);
+ if (!m_bmp)
+ return;
+
+ ::SelectObject(m_DC, m_bmp);
+ }
+
+ GraphicsContext context(m_DC);
+
+ int itemCount = client()->listSize();
+
+ // listRect is the damageRect translated into the coordinates of the entire menu list (which is itemCount * m_itemHeight pixels tall)
+ IntRect listRect = damageRect;
+ listRect.move(IntSize(0, m_scrollOffset * m_itemHeight));
+
+ RenderStyle* clientStyle = client()->clientStyle();
+
+ for (int y = listRect.y(); y < listRect.bottom(); y += m_itemHeight) {
+ int index = y / m_itemHeight;
+
+ Color optionBackgroundColor, optionTextColor;
+ RenderStyle* itemStyle = client()->itemStyle(index);
+ if (index == focusedIndex()) {
+ optionBackgroundColor = theme()->activeListBoxSelectionBackgroundColor();
+ optionTextColor = theme()->activeListBoxSelectionForegroundColor();
+ } else {
+ optionBackgroundColor = client()->itemBackgroundColor(index);
+ optionTextColor = itemStyle->color();
+ }
+
+ // itemRect is in client coordinates
+ IntRect itemRect(0, (index - m_scrollOffset) * m_itemHeight, damageRect.width(), m_itemHeight);
+
+ // Draw the background for this menu item
+ if (itemStyle->visibility() != HIDDEN)
+ context.fillRect(itemRect, optionBackgroundColor);
+
+ if (client()->itemIsSeparator(index)) {
+ IntRect separatorRect(itemRect.x() + separatorPadding, itemRect.y() + (itemRect.height() - separatorHeight) / 2, itemRect.width() - 2 * separatorPadding, separatorHeight);
+ context.fillRect(separatorRect, optionTextColor);
+ continue;
+ }
+
+ String itemText = client()->itemText(index);
+
+ unsigned length = itemText.length();
+ const UChar* string = itemText.characters();
+ TextRun textRun(string, length, false, 0, 0, itemText.defaultWritingDirection() == WTF::Unicode::RightToLeft);
+
+ context.setFillColor(optionTextColor);
+
+ Font itemFont = client()->clientStyle()->font();
+ if (client()->itemIsLabel(index)) {
+ FontDescription d = itemFont.fontDescription();
+ d.setBold(true);
+ itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing());
+ itemFont.update(m_popupClient->fontSelector());
+ }
+ context.setFont(itemFont);
+
+ // Draw the item text
+ if (itemStyle->visibility() != HIDDEN) {
+ int textX = max(0, client()->clientPaddingLeft() - client()->clientInsetLeft());
+ int textY = itemRect.y() + itemFont.ascent() + (itemRect.height() - itemFont.height()) / 2;
+ context.drawBidiText(textRun, IntPoint(textX, textY));
+ }
+ }
+
+ if (m_scrollBar)
+ m_scrollBar->paint(&context, damageRect);
+
+ if (!hdc)
+ hdc = ::GetDC(m_popup);
+
+ ::BitBlt(hdc, damageRect.x(), damageRect.y(), damageRect.width(), damageRect.height(), m_DC, damageRect.x(), damageRect.y(), SRCCOPY);
+}
+
+void PopupMenu::valueChanged(Scrollbar* scrollBar)
+{
+ ASSERT(m_scrollBar);
+
+ if (!m_popup)
+ return;
+
+ int offset = scrollBar->value();
+
+ if (m_scrollOffset == offset)
+ return;
+
+ int scrolledLines = m_scrollOffset - offset;
+ m_scrollOffset = offset;
+
+ UINT flags = SW_INVALIDATE;
+
+#ifdef CAN_SET_SMOOTH_SCROLLING_DURATION
+ BOOL shouldSmoothScroll = FALSE;
+ ::SystemParametersInfo(SPI_GETLISTBOXSMOOTHSCROLLING, 0, &shouldSmoothScroll, 0);
+ if (shouldSmoothScroll)
+ flags |= MAKEWORD(SW_SMOOTHSCROLL, smoothScrollAnimationDuration);
+#endif
+
+ IntRect listRect = clientRect();
+ if (m_scrollBar)
+ listRect.setWidth(listRect.width() - m_scrollBar->frameGeometry().width());
+ RECT r = listRect;
+ ::ScrollWindowEx(m_popup, 0, scrolledLines * m_itemHeight, &r, 0, 0, 0, flags);
+ if (m_scrollBar) {
+ r = m_scrollBar->frameGeometry();
+ ::InvalidateRect(m_popup, &r, TRUE);
+ }
+ ::UpdateWindow(m_popup);
+}
+
+IntRect PopupMenu::windowClipRect() const
+{
+ return m_windowRect;
+}
+
+static ATOM registerPopup()
+{
+ static bool haveRegisteredWindowClass = false;
+
+ if (haveRegisteredWindowClass)
+ return true;
+
+ WNDCLASSEX wcex;
+
+ wcex.cbSize = sizeof(WNDCLASSEX);
+
+ wcex.style = 0;
+ wcex.lpfnWndProc = PopupWndProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = sizeof(PopupMenu*); // For the PopupMenu pointer
+ wcex.hInstance = Page::instanceHandle();
+ wcex.hIcon = 0;
+ wcex.hCursor = LoadCursor(0, IDC_ARROW);
+ wcex.hbrBackground = 0;
+ wcex.lpszMenuName = 0;
+ wcex.lpszClassName = kPopupWindowClassName;
+ wcex.hIconSm = 0;
+
+ haveRegisteredWindowClass = true;
+
+ return ::RegisterClassEx(&wcex);
+}
+
+const int smoothScrollAnimationDuration = 5000;
+static LRESULT CALLBACK PopupWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ LRESULT lResult = 0;
+ LONG_PTR longPtr = GetWindowLongPtr(hWnd, 0);
+ PopupMenu* popup = reinterpret_cast<PopupMenu*>(longPtr);
+
+ switch (message) {
+ case WM_SIZE:
+ if (popup && popup->scrollBar()) {
+ IntSize size(LOWORD(lParam), HIWORD(lParam));
+ popup->scrollBar()->setRect(IntRect(size.width() - popup->scrollBar()->width(), 0, popup->scrollBar()->width(), size.height()));
+
+ int visibleItems = popup->visibleItems();
+ popup->scrollBar()->setEnabled(visibleItems < popup->client()->listSize());
+ popup->scrollBar()->setSteps(1, max(1, visibleItems - 1));
+ popup->scrollBar()->setProportion(visibleItems, popup->client()->listSize());
+ }
+ break;
+ case WM_ACTIVATE:
+ if (popup && popup->client() && wParam == WA_INACTIVE)
+ popup->client()->hidePopup();
+ break;
+ case WM_KILLFOCUS:
+ if (popup && popup->client() && (HWND)wParam != popup->popupHandle())
+ // Focus is going elsewhere, so hide
+ popup->client()->hidePopup();
+ break;
+ case WM_KEYDOWN:
+ if (popup && popup->client()) {
+ lResult = 0;
+ switch (LOWORD(wParam)) {
+ case VK_DOWN:
+ case VK_RIGHT:
+ popup->down();
+ break;
+ case VK_UP:
+ case VK_LEFT:
+ popup->up();
+ break;
+ case VK_HOME:
+ popup->focusFirst();
+ break;
+ case VK_END:
+ popup->focusLast();
+ break;
+ case VK_PRIOR:
+ if (popup->focusedIndex() != popup->scrollOffset()) {
+ // Set the selection to the first visible item
+ int firstVisibleItem = popup->scrollOffset();
+ popup->up(popup->focusedIndex() - firstVisibleItem);
+ } else
+ // The first visible item is selected, so move the selection back one page
+ popup->up(popup->visibleItems());
+ break;
+ case VK_NEXT:
+ if (popup) {
+ int lastVisibleItem = popup->scrollOffset() + popup->visibleItems() - 1;
+ if (popup->focusedIndex() != lastVisibleItem) {
+ // Set the selection to the last visible item
+ popup->down(lastVisibleItem - popup->focusedIndex());
+ } else
+ // The last visible item is selected, so move the selection forward one page
+ popup->down(popup->visibleItems());
+ }
+ break;
+ case VK_TAB:
+ ::SendMessage(popup->client()->clientDocument()->view()->containingWindow(), message, wParam, lParam);
+ popup->client()->hidePopup();
+ break;
+ default:
+ if (isASCIIPrintable(wParam))
+ // Send the keydown to the WebView so it can be used for type-to-select.
+ ::PostMessage(popup->client()->clientDocument()->view()->containingWindow(), message, wParam, lParam);
+ else
+ lResult = 1;
+ break;
+ }
+ }
+ break;
+ case WM_CHAR:
+ if (popup && popup->client()) {
+ lResult = 0;
+ int index;
+ switch (wParam) {
+ case 0x0D: // Enter/Return
+ popup->client()->hidePopup();
+ index = popup->focusedIndex();
+ ASSERT(index >= 0);
+ popup->client()->valueChanged(index);
+ break;
+ case 0x1B: // Escape
+ popup->client()->hidePopup();
+ break;
+ case 0x09: // TAB
+ case 0x08: // Backspace
+ case 0x0A: // Linefeed
+ default: // Character
+ lResult = 1;
+ break;
+ }
+ }
+ break;
+ case WM_MOUSEMOVE:
+ if (popup) {
+ IntPoint mousePoint(MAKEPOINTS(lParam));
+ if (popup->scrollBar()) {
+ IntRect scrollBarRect = popup->scrollBar()->frameGeometry();
+ if (popup->scrollbarCapturingMouse() || scrollBarRect.contains(mousePoint)) {
+ // Put the point into coordinates relative to the scroll bar
+ mousePoint.move(-scrollBarRect.x(), -scrollBarRect.y());
+ PlatformMouseEvent event(hWnd, message, wParam, MAKELPARAM(mousePoint.x(), mousePoint.y()));
+ popup->scrollBar()->handleMouseMoveEvent(event);
+ break;
+ }
+ }
+
+ BOOL shouldHotTrack = FALSE;
+ ::SystemParametersInfo(SPI_GETHOTTRACKING, 0, &shouldHotTrack, 0);
+
+ RECT bounds;
+ GetClientRect(popup->popupHandle(), &bounds);
+ if ((shouldHotTrack || wParam & MK_LBUTTON) && ::PtInRect(&bounds, mousePoint))
+ popup->setFocusedIndex(popup->listIndexAtPoint(mousePoint), true);
+
+ // Release capture if the left button isn't down, and the mousePoint is outside the popup window.
+ // This way, the WebView will get future mouse events in the rest of the window.
+ if (!(wParam & MK_LBUTTON) && !::PtInRect(&bounds, mousePoint)) {
+ ::ReleaseCapture();
+ break;
+ }
+ }
+ break;
+ case WM_LBUTTONDOWN:
+ if (popup) {
+ ::SetCapture(popup->popupHandle());
+ IntPoint mousePoint(MAKEPOINTS(lParam));
+ if (popup->scrollBar()) {
+ IntRect scrollBarRect = popup->scrollBar()->frameGeometry();
+ if (scrollBarRect.contains(mousePoint)) {
+ // Put the point into coordinates relative to the scroll bar
+ mousePoint.move(-scrollBarRect.x(), -scrollBarRect.y());
+ PlatformMouseEvent event(hWnd, message, wParam, MAKELPARAM(mousePoint.x(), mousePoint.y()));
+ popup->scrollBar()->handleMousePressEvent(event);
+ popup->setScrollbarCapturingMouse(true);
+ break;
+ }
+ }
+
+ popup->setFocusedIndex(popup->listIndexAtPoint(mousePoint), true);
+ }
+ break;
+ case WM_LBUTTONUP:
+ if (popup) {
+ IntPoint mousePoint(MAKEPOINTS(lParam));
+ if (popup->scrollBar()) {
+ ::ReleaseCapture();
+ IntRect scrollBarRect = popup->scrollBar()->frameGeometry();
+ if (popup->scrollbarCapturingMouse() || scrollBarRect.contains(mousePoint)) {
+ popup->setScrollbarCapturingMouse(false);
+ // Put the point into coordinates relative to the scroll bar
+ mousePoint.move(-scrollBarRect.x(), -scrollBarRect.y());
+ PlatformMouseEvent event(hWnd, message, wParam, MAKELPARAM(mousePoint.x(), mousePoint.y()));
+ popup->scrollBar()->handleMouseReleaseEvent(event);
+ // FIXME: This is a hack to work around PlatformScrollbar not invalidating correctly when it doesn't have a parent widget
+ RECT r = scrollBarRect;
+ ::InvalidateRect(popup->popupHandle(), &r, TRUE);
+ break;
+ }
+ }
+ // Only release capture and hide the popup if the mouse is inside the popup window.
+ RECT bounds;
+ GetClientRect(popup->popupHandle(), &bounds);
+ if (popup->client() && ::PtInRect(&bounds, mousePoint)) {
+ ::ReleaseCapture();
+ popup->client()->hidePopup();
+ int index = popup->focusedIndex();
+ if (index >= 0)
+ popup->client()->valueChanged(index);
+ }
+ }
+ break;
+ case WM_MOUSEWHEEL:
+ if (popup && popup->scrollBar()) {
+ int i = 0;
+ for (popup->incrementWheelDelta(GET_WHEEL_DELTA_WPARAM(wParam)); abs(popup->wheelDelta()) >= WHEEL_DELTA; popup->reduceWheelDelta(WHEEL_DELTA))
+ if (popup->wheelDelta() > 0)
+ ++i;
+ else
+ --i;
+
+ popup->scrollBar()->scroll(i > 0 ? ScrollUp : ScrollDown, ScrollByLine, abs(i));
+ }
+ break;
+ case WM_PAINT:
+ if (popup) {
+ PAINTSTRUCT paintInfo;
+ ::BeginPaint(popup->popupHandle(), &paintInfo);
+ popup->paint(paintInfo.rcPaint, paintInfo.hdc);
+ ::EndPaint(popup->popupHandle(), &paintInfo);
+ lResult = 0;
+ }
+ break;
+ case WM_PRINTCLIENT:
+ if (popup)
+ popup->paint(popup->clientRect(), (HDC)wParam);
+ break;
+ default:
+ lResult = DefWindowProc(hWnd, message, wParam, lParam);
+ }
+
+ return lResult;
+}
+
+}