2
* Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved.
3
* Copyright (C) 2007-2009 Torch Mobile Inc.
4
* Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
6
* This library is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU Library General Public
8
* License as published by the Free Software Foundation; either
9
* version 2 of the License, or (at your option) any later version.
11
* This library is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
* Library General Public License for more details.
16
* You should have received a copy of the GNU Library General Public License
17
* along with this library; see the file COPYING.LIB. If not, write to
18
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19
* Boston, MA 02110-1301, USA.
24
#include "PopupMenuWin.h"
26
#include "BitmapInfo.h"
28
#include "FloatRect.h"
29
#include "FontSelector.h"
31
#include "FrameView.h"
32
#include "GraphicsContext.h"
33
#include "HTMLNames.h"
35
#include "HostWindow.h"
36
#include "LengthFunctions.h"
38
#include "PlatformMouseEvent.h"
39
#include "PlatformScreen.h"
40
#include "RenderMenuList.h"
41
#include "RenderTheme.h"
42
#include "RenderView.h"
43
#include "Scrollbar.h"
44
#include "ScrollbarTheme.h"
45
#include "SimpleFontData.h"
47
#include "WebCoreInstanceHandle.h"
48
#include "WindowsExtras.h"
54
#define MAKEPOINTS(l) (*((POINTS FAR *)&(l)))
57
#define HIGH_BIT_MASK_SHORT 0x8000
63
using namespace HTMLNames;
65
// Default Window animation duration in milliseconds
66
static const int defaultAnimationDuration = 200;
67
// Maximum height of a popup window
68
static const int maxPopupHeight = 320;
70
const int optionSpacingMiddle = 1;
71
const int popupWindowBorderWidth = 1;
73
static LPCWSTR kPopupWindowClassName = L"PopupWindowClass";
75
// This is used from within our custom message pump when we want to send a
76
// message to the web view and not have our message stolen and sent to
78
static const UINT WM_HOST_WINDOW_FIRST = WM_USER;
79
static const UINT WM_HOST_WINDOW_CHAR = WM_USER + WM_CHAR;
80
static const UINT WM_HOST_WINDOW_MOUSEMOVE = WM_USER + WM_MOUSEMOVE;
82
// FIXME: Remove this as soon as practical.
83
static inline bool isASCIIPrintable(unsigned c)
85
return c >= 0x20 && c <= 0x7E;
88
static void translatePoint(LPARAM& lParam, HWND from, HWND to)
91
pt.x = (short)GET_X_LPARAM(lParam);
92
pt.y = (short)GET_Y_LPARAM(lParam);
93
::MapWindowPoints(from, to, &pt, 1);
94
lParam = MAKELPARAM(pt.x, pt.y);
97
static FloatRect monitorFromHwnd(HWND hwnd)
99
HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
100
MONITORINFOEX monitorInfo;
101
monitorInfo.cbSize = sizeof(MONITORINFOEX);
102
GetMonitorInfo(monitor, &monitorInfo);
103
return monitorInfo.rcWork;
106
PopupMenuWin::PopupMenuWin(PopupMenuClient* client)
107
: m_popupClient(client)
112
, m_wasClicked(false)
117
, m_scrollbarCapturingMouse(false)
122
PopupMenuWin::~PopupMenuWin()
125
::DeleteObject(m_bmp);
129
::DestroyWindow(m_popup);
131
m_scrollbar->setParent(0);
134
void PopupMenuWin::disconnectClient()
139
LPCWSTR PopupMenuWin::popupClassName()
141
return kPopupWindowClassName;
144
void PopupMenuWin::show(const IntRect& r, FrameView* view, int index)
146
calculatePositionAndSize(r, view);
147
if (clientRect().isEmpty())
150
HWND hostWindow = view->hostWindow()->platformPageClient();
152
if (!m_scrollbar && visibleItems() < client()->listSize()) {
153
// We need a scroll bar
154
m_scrollbar = client()->createScrollbar(this, VerticalScrollbar, SmallScrollbar);
155
m_scrollbar->styleChanged();
158
// We need to reposition the popup window to its final coordinates.
159
// Before calling this, the popup hwnd is currently the size of and at the location of the menu list client so it needs to be updated.
160
::MoveWindow(m_popup, m_windowRect.x(), m_windowRect.y(), m_windowRect.width(), m_windowRect.height(), false);
162
// Determine whether we should animate our popups
163
// Note: Must use 'BOOL' and 'FALSE' instead of 'bool' and 'false' to avoid stack corruption with SystemParametersInfo
164
BOOL shouldAnimate = FALSE;
166
::SystemParametersInfo(SPI_GETCOMBOBOXANIMATION, 0, &shouldAnimate, 0);
170
::GetWindowRect(hostWindow, &viewRect);
172
if (!::IsRectEmpty(&viewRect)) {
173
// Popups should slide into view away from the <select> box
174
// NOTE: This may have to change for Vista
175
DWORD slideDirection = (m_windowRect.y() < viewRect.top + view->contentsToWindow(r.location()).y()) ? AW_VER_NEGATIVE : AW_VER_POSITIVE;
177
::AnimateWindow(m_popup, defaultAnimationDuration, AW_SLIDE | slideDirection);
181
::ShowWindow(m_popup, SW_SHOWNOACTIVATE);
184
int index = client()->selectedIndex();
186
setFocusedIndex(index);
191
// Protect the popup menu in case its owner is destroyed while we're running the message pump.
192
RefPtr<PopupMenu> protect(this);
194
::SetCapture(hostWindow);
199
while (::GetMessage(&msg, 0, 0, 0)) {
200
switch (msg.message) {
201
case WM_HOST_WINDOW_MOUSEMOVE:
202
case WM_HOST_WINDOW_CHAR:
203
if (msg.hwnd == m_popup) {
204
// This message should be sent to the host window.
205
msg.hwnd = hostWindow;
206
msg.message -= WM_HOST_WINDOW_FIRST;
210
// Steal mouse messages.
213
case WM_NCLBUTTONDOWN:
215
case WM_NCLBUTTONDBLCLK:
216
case WM_NCRBUTTONDOWN:
218
case WM_NCRBUTTONDBLCLK:
219
case WM_NCMBUTTONDOWN:
221
case WM_NCMBUTTONDBLCLK:
227
// These mouse messages use client coordinates so we need to convert them.
231
case WM_LBUTTONDBLCLK:
234
case WM_RBUTTONDBLCLK:
237
case WM_MBUTTONDBLCLK: {
238
// Translate the coordinate.
239
translatePoint(msg.lParam, msg.hwnd, m_popup);
245
// Steal all keyboard messages.
258
::TranslateMessage(&msg);
259
::DispatchMessage(&msg);
266
activeWindow = ::GetActiveWindow();
267
if (activeWindow != hostWindow && !::IsChild(activeWindow, hostWindow))
269
if (::GetCapture() != hostWindow)
273
if (::GetCapture() == hostWindow)
276
// We're done, hide the popup if necessary.
280
void PopupMenuWin::hide()
287
::ShowWindow(m_popup, SW_HIDE);
290
client()->popupDidHide();
292
// Post a WM_NULL message to wake up the message pump if necessary.
293
::PostMessage(m_popup, WM_NULL, 0, 0);
296
// The screen that the popup is placed on should be whichever one the popup menu button lies on.
297
// We fake an hwnd (here we use the popup's hwnd) on top of the button which we can then use to determine the screen.
298
// We can then proceed with our final position/size calculations.
299
void PopupMenuWin::calculatePositionAndSize(const IntRect& r, FrameView* v)
301
// First get the screen coordinates of the popup menu client.
302
HWND hostWindow = v->hostWindow()->platformPageClient();
303
IntRect absoluteBounds = ((RenderMenuList*)m_popupClient)->absoluteBoundingBoxRect();
304
IntRect absoluteScreenCoords(v->contentsToWindow(absoluteBounds.location()), absoluteBounds.size());
305
POINT absoluteLocation(absoluteScreenCoords.location());
306
if (!::ClientToScreen(v->hostWindow()->platformPageClient(), &absoluteLocation))
308
absoluteScreenCoords.setLocation(absoluteLocation);
310
// Now set the popup menu's location temporarily to these coordinates so we can determine which screen the popup should lie on.
311
// We create or move m_popup as necessary.
314
DWORD exStyle = WS_EX_LTRREADING;
315
m_popup = ::CreateWindowExW(exStyle, kPopupWindowClassName, L"PopupMenu",
316
WS_POPUP | WS_BORDER,
317
absoluteScreenCoords.x(), absoluteScreenCoords.y(), absoluteScreenCoords.width(), absoluteScreenCoords.height(),
318
hostWindow, 0, WebCore::instanceHandle(), this);
323
::MoveWindow(m_popup, absoluteScreenCoords.x(), absoluteScreenCoords.y(), absoluteScreenCoords.width(), absoluteScreenCoords.height(), false);
325
FloatRect screen = monitorFromHwnd(m_popup);
327
// Now we determine the actual location and measurements of the popup itself.
328
// r is in absolute document coordinates, but we want to be in screen coordinates.
330
// First, move to WebView coordinates
331
IntRect rScreenCoords(v->contentsToWindow(r.location()), r.size());
333
// Then, translate to screen coordinates
334
POINT location(rScreenCoords.location());
335
if (!::ClientToScreen(v->hostWindow()->platformPageClient(), &location))
338
rScreenCoords.setLocation(location);
340
// First, determine the popup's height
341
int itemCount = client()->listSize();
342
m_itemHeight = client()->menuStyle().font().fontMetrics().height() + optionSpacingMiddle;
343
int naturalHeight = m_itemHeight * itemCount;
344
int popupHeight = min(maxPopupHeight, naturalHeight);
345
// The popup should show an integral number of items (i.e. no partial items should be visible)
346
popupHeight -= popupHeight % m_itemHeight;
348
// Next determine its width
350
for (int i = 0; i < itemCount; ++i) {
351
String text = client()->itemText(i);
355
Font itemFont = client()->menuStyle().font();
356
if (client()->itemIsLabel(i)) {
357
FontDescription d = itemFont.fontDescription();
358
d.setWeight(d.bolderWeight());
359
itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing());
360
itemFont.update(m_popupClient->fontSelector());
363
popupWidth = max(popupWidth, static_cast<int>(ceilf(itemFont.width(TextRun(text.characters(), text.length())))));
366
if (naturalHeight > maxPopupHeight)
367
// We need room for a scrollbar
368
popupWidth += ScrollbarTheme::theme()->scrollbarThickness(SmallScrollbar);
370
// Add padding to align the popup text with the <select> text
371
popupWidth += max<int>(0, client()->clientPaddingRight() - client()->clientInsetRight()) + max<int>(0, client()->clientPaddingLeft() - client()->clientInsetLeft());
373
// Leave room for the border
374
popupWidth += 2 * popupWindowBorderWidth;
375
popupHeight += 2 * popupWindowBorderWidth;
377
// The popup should be at least as wide as the control on the page
378
popupWidth = max(rScreenCoords.width() - client()->clientInsetLeft() - client()->clientInsetRight(), popupWidth);
380
// Always left-align items in the popup. This matches popup menus on the mac.
381
int popupX = rScreenCoords.x() + client()->clientInsetLeft();
383
IntRect popupRect(popupX, rScreenCoords.maxY(), popupWidth, popupHeight);
385
// Check that we don't go off the screen vertically
386
if (popupRect.maxY() > screen.height()) {
387
// The popup will go off the screen, so try placing it above the client
388
if (rScreenCoords.y() - popupRect.height() < 0) {
389
// The popup won't fit above, either, so place it whereever's bigger and resize it to fit
390
if ((rScreenCoords.y() + rScreenCoords.height() / 2) < (screen.height() / 2)) {
392
popupRect.setHeight(screen.height() - popupRect.y());
396
popupRect.setHeight(rScreenCoords.y());
399
// The popup fits above, so reposition it
400
popupRect.setY(rScreenCoords.y() - popupRect.height());
404
// Check that we don't go off the screen horizontally
405
if (popupRect.x() + popupRect.width() > screen.width() + screen.x())
406
popupRect.setX(screen.x() + screen.width() - popupRect.width());
407
if (popupRect.x() < screen.x())
408
popupRect.setX(screen.x());
410
m_windowRect = popupRect;
414
bool PopupMenuWin::setFocusedIndex(int i, bool hotTracking)
416
if (i < 0 || i >= client()->listSize() || i == focusedIndex())
419
if (!client()->itemIsEnabled(i))
422
invalidateItem(focusedIndex());
428
client()->setTextFromItem(i);
430
if (!scrollToRevealSelection())
431
::UpdateWindow(m_popup);
436
int PopupMenuWin::visibleItems() const
438
return clientRect().height() / m_itemHeight;
441
int PopupMenuWin::listIndexAtPoint(const IntPoint& point) const
443
return m_scrollOffset + point.y() / m_itemHeight;
446
int PopupMenuWin::focusedIndex() const
448
return m_focusedIndex;
451
void PopupMenuWin::focusFirst()
456
int size = client()->listSize();
458
for (int i = 0; i < size; ++i)
459
if (client()->itemIsEnabled(i)) {
465
void PopupMenuWin::focusLast()
470
int size = client()->listSize();
472
for (int i = size - 1; i > 0; --i)
473
if (client()->itemIsEnabled(i)) {
479
bool PopupMenuWin::down(unsigned lines)
484
int size = client()->listSize();
486
int lastSelectableIndex, selectedListIndex;
487
lastSelectableIndex = selectedListIndex = focusedIndex();
488
for (int i = selectedListIndex + 1; i >= 0 && i < size; ++i)
489
if (client()->itemIsEnabled(i)) {
490
lastSelectableIndex = i;
491
if (i >= selectedListIndex + (int)lines)
495
return setFocusedIndex(lastSelectableIndex);
498
bool PopupMenuWin::up(unsigned lines)
503
int size = client()->listSize();
505
int lastSelectableIndex, selectedListIndex;
506
lastSelectableIndex = selectedListIndex = focusedIndex();
507
for (int i = selectedListIndex - 1; i >= 0 && i < size; --i)
508
if (client()->itemIsEnabled(i)) {
509
lastSelectableIndex = i;
510
if (i <= selectedListIndex - (int)lines)
514
return setFocusedIndex(lastSelectableIndex);
517
void PopupMenuWin::invalidateItem(int index)
522
IntRect damageRect(clientRect());
523
damageRect.setY(m_itemHeight * (index - m_scrollOffset));
524
damageRect.setHeight(m_itemHeight);
526
damageRect.setWidth(damageRect.width() - m_scrollbar->frameRect().width());
529
::InvalidateRect(m_popup, &r, TRUE);
532
IntRect PopupMenuWin::clientRect() const
534
IntRect clientRect = m_windowRect;
535
clientRect.inflate(-popupWindowBorderWidth);
536
clientRect.setLocation(IntPoint(0, 0));
540
void PopupMenuWin::incrementWheelDelta(int delta)
542
m_wheelDelta += delta;
545
void PopupMenuWin::reduceWheelDelta(int delta)
548
ASSERT(delta <= abs(m_wheelDelta));
550
if (m_wheelDelta > 0)
551
m_wheelDelta -= delta;
552
else if (m_wheelDelta < 0)
553
m_wheelDelta += delta;
558
bool PopupMenuWin::scrollToRevealSelection()
563
int index = focusedIndex();
565
if (index < m_scrollOffset) {
566
ScrollableArea::scrollToOffsetWithoutAnimation(VerticalScrollbar, index);
570
if (index >= m_scrollOffset + visibleItems()) {
571
ScrollableArea::scrollToOffsetWithoutAnimation(VerticalScrollbar, index - visibleItems() + 1);
578
void PopupMenuWin::updateFromElement()
583
m_focusedIndex = client()->selectedIndex();
585
::InvalidateRect(m_popup, 0, TRUE);
586
if (!scrollToRevealSelection())
587
::UpdateWindow(m_popup);
590
const int separatorPadding = 4;
591
const int separatorHeight = 1;
592
void PopupMenuWin::paint(const IntRect& damageRect, HDC hdc)
598
m_DC = ::CreateCompatibleDC(HWndDC(m_popup));
604
bool keepBitmap = false;
606
if (GetObject(m_bmp, sizeof(bitmap), &bitmap))
607
keepBitmap = bitmap.bmWidth == clientRect().width()
608
&& bitmap.bmHeight == clientRect().height();
616
BitmapInfo bitmapInfo = BitmapInfo::createBottomUp(clientRect().size(), BitmapInfo::BitCount16);
618
BitmapInfo bitmapInfo = BitmapInfo::createBottomUp(clientRect().size());
621
m_bmp = ::CreateDIBSection(m_DC, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0);
625
::SelectObject(m_DC, m_bmp);
628
GraphicsContext context(m_DC);
630
int itemCount = client()->listSize();
632
// listRect is the damageRect translated into the coordinates of the entire menu list (which is itemCount * m_itemHeight pixels tall)
633
IntRect listRect = damageRect;
634
listRect.move(IntSize(0, m_scrollOffset * m_itemHeight));
636
for (int y = listRect.y(); y < listRect.maxY(); y += m_itemHeight) {
637
int index = y / m_itemHeight;
639
Color optionBackgroundColor, optionTextColor;
640
PopupMenuStyle itemStyle = client()->itemStyle(index);
641
if (index == focusedIndex()) {
642
optionBackgroundColor = RenderTheme::defaultTheme()->activeListBoxSelectionBackgroundColor();
643
optionTextColor = RenderTheme::defaultTheme()->activeListBoxSelectionForegroundColor();
645
optionBackgroundColor = itemStyle.backgroundColor();
646
optionTextColor = itemStyle.foregroundColor();
649
// itemRect is in client coordinates
650
IntRect itemRect(0, (index - m_scrollOffset) * m_itemHeight, damageRect.width(), m_itemHeight);
652
// Draw the background for this menu item
653
if (itemStyle.isVisible())
654
context.fillRect(itemRect, optionBackgroundColor, ColorSpaceDeviceRGB);
656
if (client()->itemIsSeparator(index)) {
657
IntRect separatorRect(itemRect.x() + separatorPadding, itemRect.y() + (itemRect.height() - separatorHeight) / 2, itemRect.width() - 2 * separatorPadding, separatorHeight);
658
context.fillRect(separatorRect, optionTextColor, ColorSpaceDeviceRGB);
662
String itemText = client()->itemText(index);
664
TextDirection direction = (itemText.defaultWritingDirection() == WTF::Unicode::RightToLeft) ? RTL : LTR;
665
TextRun textRun(itemText, 0, 0, TextRun::AllowTrailingExpansion, direction);
667
context.setFillColor(optionTextColor, ColorSpaceDeviceRGB);
669
Font itemFont = client()->menuStyle().font();
670
if (client()->itemIsLabel(index)) {
671
FontDescription d = itemFont.fontDescription();
672
d.setWeight(d.bolderWeight());
673
itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing());
674
itemFont.update(m_popupClient->fontSelector());
677
// Draw the item text
678
if (itemStyle.isVisible()) {
679
int textX = max<int>(0, client()->clientPaddingLeft() - client()->clientInsetLeft());
680
if (RenderTheme::defaultTheme()->popupOptionSupportsTextIndent() && itemStyle.textDirection() == LTR)
681
textX += minimumIntValueForLength(itemStyle.textIndent(), itemRect.width());
682
int textY = itemRect.y() + itemFont.fontMetrics().ascent() + (itemRect.height() - itemFont.fontMetrics().height()) / 2;
683
context.drawBidiText(itemFont, textRun, IntPoint(textX, textY));
688
m_scrollbar->paint(&context, damageRect);
691
HDC localDC = hdc ? hdc : hWndDC.setHWnd(m_popup);
693
::BitBlt(localDC, damageRect.x(), damageRect.y(), damageRect.width(), damageRect.height(), m_DC, damageRect.x(), damageRect.y(), SRCCOPY);
696
int PopupMenuWin::scrollSize(ScrollbarOrientation orientation) const
698
return ((orientation == VerticalScrollbar) && m_scrollbar) ? (m_scrollbar->totalSize() - m_scrollbar->visibleSize()) : 0;
701
int PopupMenuWin::scrollPosition(Scrollbar*) const
703
return m_scrollOffset;
706
void PopupMenuWin::setScrollOffset(const IntPoint& offset)
708
scrollTo(offset.y());
711
void PopupMenuWin::scrollTo(int offset)
718
if (m_scrollOffset == offset)
721
int scrolledLines = m_scrollOffset - offset;
722
m_scrollOffset = offset;
724
UINT flags = SW_INVALIDATE;
726
#ifdef CAN_SET_SMOOTH_SCROLLING_DURATION
727
BOOL shouldSmoothScroll = FALSE;
728
::SystemParametersInfo(SPI_GETLISTBOXSMOOTHSCROLLING, 0, &shouldSmoothScroll, 0);
729
if (shouldSmoothScroll)
730
flags |= MAKEWORD(SW_SMOOTHSCROLL, smoothScrollAnimationDuration);
733
IntRect listRect = clientRect();
735
listRect.setWidth(listRect.width() - m_scrollbar->frameRect().width());
737
::ScrollWindowEx(m_popup, 0, scrolledLines * m_itemHeight, &r, 0, 0, 0, flags);
739
r = m_scrollbar->frameRect();
740
::InvalidateRect(m_popup, &r, TRUE);
742
::UpdateWindow(m_popup);
745
void PopupMenuWin::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
747
IntRect scrollRect = rect;
748
scrollRect.move(scrollbar->x(), scrollbar->y());
750
::InvalidateRect(m_popup, &r, false);
753
int PopupMenuWin::visibleHeight() const
755
return m_scrollbar ? m_scrollbar->visibleSize() : m_windowRect.height();
758
int PopupMenuWin::visibleWidth() const
760
return m_windowRect.width();
763
IntSize PopupMenuWin::contentsSize() const
765
return m_windowRect.size();
768
bool PopupMenuWin::scrollbarsCanBeActive() const
773
IntRect PopupMenuWin::scrollableAreaBoundingBox() const
778
void PopupMenuWin::registerClass()
780
static bool haveRegisteredWindowClass = false;
782
if (haveRegisteredWindowClass)
789
wcex.cbSize = sizeof(WNDCLASSEX);
791
wcex.style = CS_DROPSHADOW;
794
wcex.lpfnWndProc = PopupMenuWndProc;
796
wcex.cbWndExtra = sizeof(PopupMenu*); // For the PopupMenu pointer
797
wcex.hInstance = WebCore::instanceHandle();
799
wcex.hCursor = LoadCursor(0, IDC_ARROW);
800
wcex.hbrBackground = 0;
801
wcex.lpszMenuName = 0;
802
wcex.lpszClassName = kPopupWindowClassName;
804
haveRegisteredWindowClass = true;
807
RegisterClass(&wcex);
809
RegisterClassEx(&wcex);
814
LRESULT CALLBACK PopupMenuWin::PopupMenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
816
if (PopupMenuWin* popup = static_cast<PopupMenuWin*>(getWindowPointer(hWnd, 0)))
817
return popup->wndProc(hWnd, message, wParam, lParam);
819
if (message == WM_CREATE) {
820
LPCREATESTRUCT createStruct = reinterpret_cast<LPCREATESTRUCT>(lParam);
822
// Associate the PopupMenu with the window.
823
setWindowPointer(hWnd, 0, createStruct->lpCreateParams);
827
return ::DefWindowProc(hWnd, message, wParam, lParam);
830
const int smoothScrollAnimationDuration = 5000;
832
LRESULT PopupMenuWin::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
838
case WM_MOUSEACTIVATE:
839
return MA_NOACTIVATE;
845
IntSize size(LOWORD(lParam), HIWORD(lParam));
846
scrollbar()->setFrameRect(IntRect(size.width() - scrollbar()->width(), 0, scrollbar()->width(), size.height()));
848
int visibleItems = this->visibleItems();
849
scrollbar()->setEnabled(visibleItems < client()->listSize());
850
scrollbar()->setSteps(1, max(1, visibleItems - 1));
851
scrollbar()->setProportion(visibleItems, client()->listSize());
860
bool altKeyPressed = GetKeyState(VK_MENU) & HIGH_BIT_MASK_SHORT;
861
bool ctrlKeyPressed = GetKeyState(VK_CONTROL) & HIGH_BIT_MASK_SHORT;
864
switch (LOWORD(wParam)) {
866
if (!altKeyPressed && !ctrlKeyPressed) {
867
int index = focusedIndex();
869
client()->valueChanged(index);
876
int index = focusedIndex();
878
client()->valueChanged(index);
888
int index = focusedIndex();
890
client()->valueChanged(index);
905
if (focusedIndex() != scrollOffset()) {
906
// Set the selection to the first visible item
907
int firstVisibleItem = scrollOffset();
908
up(focusedIndex() - firstVisibleItem);
910
// The first visible item is selected, so move the selection back one page
915
int lastVisibleItem = scrollOffset() + visibleItems() - 1;
916
if (focusedIndex() != lastVisibleItem) {
917
// Set the selection to the last visible item
918
down(lastVisibleItem - focusedIndex());
920
// The last visible item is selected, so move the selection forward one page
921
down(visibleItems());
926
::SendMessage(client()->hostWindow()->platformPageClient(), message, wParam, lParam);
933
if (isASCIIPrintable(wParam))
934
// Send the keydown to the WebView so it can be used for type-to-select.
935
// Since we know that the virtual key is ASCII printable, it's OK to convert this to
936
// a WM_CHAR message. (We don't want to call TranslateMessage because that will post a
937
// WM_CHAR message that will be stolen and redirected to the popup HWND.
938
::PostMessage(m_popup, WM_HOST_WINDOW_CHAR, wParam, lParam);
952
case 0x0D: // Enter/Return
954
index = focusedIndex();
956
client()->valueChanged(index);
962
case 0x08: // Backspace
963
case 0x0A: // Linefeed
964
default: // Character
971
IntPoint mousePoint(MAKEPOINTS(lParam));
973
IntRect scrollBarRect = scrollbar()->frameRect();
974
if (scrollbarCapturingMouse() || scrollBarRect.contains(mousePoint)) {
975
// Put the point into coordinates relative to the scroll bar
976
mousePoint.move(-scrollBarRect.x(), -scrollBarRect.y());
977
PlatformMouseEvent event(hWnd, message, wParam, MAKELPARAM(mousePoint.x(), mousePoint.y()));
978
scrollbar()->mouseMoved(event);
983
BOOL shouldHotTrack = FALSE;
985
::SystemParametersInfo(SPI_GETHOTTRACKING, 0, &shouldHotTrack, 0);
989
GetClientRect(popupHandle(), &bounds);
990
if (!::PtInRect(&bounds, mousePoint) && !(wParam & MK_LBUTTON) && client()) {
991
// When the mouse is not inside the popup menu and the left button isn't down, just
992
// repost the message to the web view.
994
// Translate the coordinate.
995
translatePoint(lParam, m_popup, client()->hostWindow()->platformPageClient());
997
::PostMessage(m_popup, WM_HOST_WINDOW_MOUSEMOVE, wParam, lParam);
1001
if ((shouldHotTrack || wParam & MK_LBUTTON) && ::PtInRect(&bounds, mousePoint))
1002
setFocusedIndex(listIndexAtPoint(mousePoint), true);
1006
case WM_LBUTTONDOWN: {
1007
IntPoint mousePoint(MAKEPOINTS(lParam));
1009
IntRect scrollBarRect = scrollbar()->frameRect();
1010
if (scrollBarRect.contains(mousePoint)) {
1011
// Put the point into coordinates relative to the scroll bar
1012
mousePoint.move(-scrollBarRect.x(), -scrollBarRect.y());
1013
PlatformMouseEvent event(hWnd, message, wParam, MAKELPARAM(mousePoint.x(), mousePoint.y()));
1014
scrollbar()->mouseDown(event);
1015
setScrollbarCapturingMouse(true);
1020
// If the mouse is inside the window, update the focused index. Otherwise,
1023
GetClientRect(m_popup, &bounds);
1024
if (::PtInRect(&bounds, mousePoint))
1025
setFocusedIndex(listIndexAtPoint(mousePoint), true);
1030
case WM_LBUTTONUP: {
1031
IntPoint mousePoint(MAKEPOINTS(lParam));
1033
IntRect scrollBarRect = scrollbar()->frameRect();
1034
if (scrollbarCapturingMouse() || scrollBarRect.contains(mousePoint)) {
1035
setScrollbarCapturingMouse(false);
1036
// Put the point into coordinates relative to the scroll bar
1037
mousePoint.move(-scrollBarRect.x(), -scrollBarRect.y());
1038
PlatformMouseEvent event(hWnd, message, wParam, MAKELPARAM(mousePoint.x(), mousePoint.y()));
1039
scrollbar()->mouseUp(event);
1040
// FIXME: This is a hack to work around Scrollbar not invalidating correctly when it doesn't have a parent widget
1041
RECT r = scrollBarRect;
1042
::InvalidateRect(popupHandle(), &r, TRUE);
1046
// Only hide the popup if the mouse is inside the popup window.
1048
GetClientRect(popupHandle(), &bounds);
1049
if (client() && ::PtInRect(&bounds, mousePoint)) {
1051
int index = focusedIndex();
1053
client()->valueChanged(index);
1058
case WM_MOUSEWHEEL: {
1063
for (incrementWheelDelta(GET_WHEEL_DELTA_WPARAM(wParam)); abs(wheelDelta()) >= WHEEL_DELTA; reduceWheelDelta(WHEEL_DELTA)) {
1064
if (wheelDelta() > 0)
1070
ScrollableArea::scroll(i > 0 ? ScrollUp : ScrollDown, ScrollByLine, abs(i));
1075
PAINTSTRUCT paintInfo;
1076
::BeginPaint(popupHandle(), &paintInfo);
1077
paint(paintInfo.rcPaint, paintInfo.hdc);
1078
::EndPaint(popupHandle(), &paintInfo);
1083
case WM_PRINTCLIENT:
1084
paint(clientRect(), (HDC)wParam);
1088
lResult = DefWindowProc(hWnd, message, wParam, lParam);