| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2008, Google Inc. | |
| 2 // All rights reserved. | |
| 3 // | |
| 4 // Redistribution and use in source and binary forms, with or without | |
| 5 // modification, are permitted provided that the following conditions are | |
| 6 // met: | |
| 7 // | |
| 8 // * Redistributions of source code must retain the above copyright | |
| 9 // notice, this list of conditions and the following disclaimer. | |
| 10 // * Redistributions in binary form must reproduce the above | |
| 11 // copyright notice, this list of conditions and the following disclaimer | |
| 12 // in the documentation and/or other materials provided with the | |
| 13 // distribution. | |
| 14 // * Neither the name of Google Inc. nor the names of its | |
| 15 // contributors may be used to endorse or promote products derived from | |
| 16 // this software without specific prior written permission. | |
| 17 // | |
| 18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 29 | |
| 30 #include "config.h" | |
| 31 | |
| 32 #pragma warning(push, 0) | |
| 33 #include "PopupMenu.h" | |
| 34 | |
| 35 #include "CharacterNames.h" | |
| 36 #include "ChromeClientWin.h" | |
| 37 #include "Document.h" | |
| 38 #include "Font.h" | |
| 39 #include "Frame.h" | |
| 40 #include "FontSelector.h" | |
| 41 #include "FramelessScrollView.h" | |
| 42 #include "GraphicsContext.h" | |
| 43 #include "IntRect.h" | |
| 44 #include "Page.h" | |
| 45 #include "PlatformKeyboardEvent.h" | |
| 46 #include "PlatformMouseEvent.h" | |
| 47 #include "PlatformScreen.h" | |
| 48 #include "PlatformScrollbar.h" | |
| 49 #include "PlatformWheelEvent.h" | |
| 50 #include "SystemTime.h" | |
| 51 #include "RenderBlock.h" | |
| 52 #include "RenderTheme.h" | |
| 53 #include "Widget.h" | |
| 54 #include "WidgetClientWin.h" | |
| 55 #pragma warning(pop) | |
| 56 | |
| 57 //#define LOG_ENABLE | |
| 58 #include "LogWin.h" | |
| 59 | |
| 60 using namespace WTF; | |
| 61 using namespace Unicode; | |
| 62 | |
| 63 using std::min; | |
| 64 using std::max; | |
| 65 | |
| 66 namespace WebCore { | |
| 67 | |
| 68 typedef unsigned long long TimeStamp; | |
| 69 | |
| 70 static const int kMaxVisibleRows = 20; | |
| 71 static const int kMaxHeight = 500; | |
| 72 static const int kBorderSize = 1; | |
| 73 static const TimeStamp kTypeAheadTimeoutMs = 1000; | |
| 74 | |
| 75 class PopupListBox; | |
| 76 | |
| 77 // This class holds a PopupListBox. Its sole purpose is to be able to draw | |
| 78 // a border around its child. All its paint/event handling is just forwarded | |
| 79 // to the child listBox (with the appropriate transforms). | |
| 80 class PopupContainer : public FramelessScrollView { | |
| 81 public: | |
| 82 static HWND Create(PopupMenuClient* client); | |
| 83 | |
| 84 // FramelessScrollView | |
| 85 virtual void paint(GraphicsContext* gc, const IntRect& rect); | |
| 86 virtual void hide(); | |
| 87 virtual bool handleMouseDownEvent(const PlatformMouseEvent& event); | |
| 88 virtual bool handleMouseMoveEvent(const PlatformMouseEvent& event); | |
| 89 virtual bool handleMouseReleaseEvent(const PlatformMouseEvent& event); | |
| 90 virtual bool handleWheelEvent(const PlatformWheelEvent& event); | |
| 91 virtual bool handleKeyEvent(const PlatformKeyboardEvent& event); | |
| 92 | |
| 93 // PopupContainer methods | |
| 94 | |
| 95 // Show the popup | |
| 96 void showPopup(FrameView* view); | |
| 97 | |
| 98 // Hide the popup. Do not call this directly: use client->hidePopup(). | |
| 99 void hidePopup(); | |
| 100 | |
| 101 // Compute size of widget and children. | |
| 102 void layout(); | |
| 103 | |
| 104 PopupListBox* listBox() const { return m_listBox.get(); } | |
| 105 | |
| 106 private: | |
| 107 PopupContainer(PopupMenuClient* client); | |
| 108 ~PopupContainer(); | |
| 109 | |
| 110 // Paint the border. | |
| 111 void paintBorder(GraphicsContext* gc, const IntRect& rect); | |
| 112 | |
| 113 RefPtr<PopupListBox> m_listBox; | |
| 114 }; | |
| 115 | |
| 116 // This class uses WebCore code to paint and handle events for a drop-down list | |
| 117 // box ("combobox" on Windows). | |
| 118 class PopupListBox : public FramelessScrollView { | |
| 119 public: | |
| 120 // FramelessScrollView | |
| 121 virtual void paint(GraphicsContext* gc, const IntRect& rect); | |
| 122 virtual bool handleMouseDownEvent(const PlatformMouseEvent& event); | |
| 123 virtual bool handleMouseMoveEvent(const PlatformMouseEvent& event); | |
| 124 virtual bool handleMouseReleaseEvent(const PlatformMouseEvent& event); | |
| 125 virtual bool handleWheelEvent(const PlatformWheelEvent& event); | |
| 126 virtual bool handleKeyEvent(const PlatformKeyboardEvent& event); | |
| 127 | |
| 128 // PopupListBox methods | |
| 129 | |
| 130 // Show the popup | |
| 131 void showPopup(); | |
| 132 | |
| 133 // Hide the popup. Do not call this directly: use client->hidePopup(). | |
| 134 void hidePopup(); | |
| 135 | |
| 136 // Update our internal list to match the client. | |
| 137 void updateFromElement(); | |
| 138 | |
| 139 // Free any allocated resources used in a particular popup session. | |
| 140 void clear(); | |
| 141 | |
| 142 // Set the index of the option that is displayed in the <select> widget in t
he page | |
| 143 void setOriginalIndex(int index); | |
| 144 | |
| 145 // Get the index of the item that the user is currently moused over or has s
elected with | |
| 146 // the keyboard. This is not the same as the original index, since the user
has not yet | |
| 147 // accepted this input. | |
| 148 int selectedIndex() const { return m_selectedIndex; } | |
| 149 | |
| 150 // Move selection down/up the given number of items, scrolling if necessary. | |
| 151 // Positive is down. The resulting index will be clamped to the range | |
| 152 // [0, numItems), and non-option items will be skipped. | |
| 153 void adjustSelectedIndex(int delta); | |
| 154 | |
| 155 // Returns the number of items in the list. | |
| 156 int numItems() const { return static_cast<int>(m_items.size()); } | |
| 157 | |
| 158 void setBaseWidth(int width) | |
| 159 { | |
| 160 m_baseWidth = width; | |
| 161 } | |
| 162 | |
| 163 // Compute size of widget and children. | |
| 164 void layout(); | |
| 165 | |
| 166 private: | |
| 167 friend class PopupContainer; | |
| 168 | |
| 169 // A type of List Item | |
| 170 enum ListItemType { | |
| 171 TYPE_OPTION, | |
| 172 TYPE_GROUP, | |
| 173 TYPE_SEPARATOR | |
| 174 }; | |
| 175 | |
| 176 // A item (represented by <option> or <optgroup>) in the <select> widget. | |
| 177 struct ListItem { | |
| 178 ListItem(const String& label, ListItemType type) | |
| 179 : label(label.copy()), type(type), y(0) {} | |
| 180 String label; | |
| 181 ListItemType type; | |
| 182 int y; // y offset of this item, relative to the top of the popup. | |
| 183 }; | |
| 184 | |
| 185 PopupListBox(PopupMenuClient* client) | |
| 186 : m_originalIndex(0) | |
| 187 , m_selectedIndex(0) | |
| 188 , m_visibleRows(0) | |
| 189 , m_popupClient(client) | |
| 190 , m_repeatingChar(0) | |
| 191 , m_lastCharTime(0) | |
| 192 , m_acceptOnAbandon(false) | |
| 193 { | |
| 194 setScrollbarsMode(ScrollbarAlwaysOff); | |
| 195 } | |
| 196 | |
| 197 ~PopupListBox() | |
| 198 { | |
| 199 clear(); | |
| 200 } | |
| 201 | |
| 202 void disconnectClient() { m_popupClient = 0; } | |
| 203 | |
| 204 // Closes the popup | |
| 205 void abandon(); | |
| 206 // Select an index in the list, scrolling if necessary. | |
| 207 void selectIndex(int index); | |
| 208 // Accepts the selected index as the value to be displayed in the <select> w
idget on | |
| 209 // the web page, and closes the popup. | |
| 210 void acceptIndex(int index); | |
| 211 | |
| 212 // Returns true if the selection can be changed to index. | |
| 213 // Disabled items, or labels cannot be selected. | |
| 214 bool isSelectableItem(int index); | |
| 215 | |
| 216 // Scrolls to reveal the given index. | |
| 217 void scrollToRevealRow(int index); | |
| 218 void scrollToRevealSelection() { scrollToRevealRow(m_selectedIndex); } | |
| 219 | |
| 220 // Invalidates the row at the given index. | |
| 221 void invalidateRow(int index); | |
| 222 | |
| 223 // Gets the height of a row. | |
| 224 int getRowHeight(int index); | |
| 225 // Get the bounds of a row. | |
| 226 IntRect getRowBounds(int index); | |
| 227 | |
| 228 // Converts a point to an index of the row the point is over | |
| 229 int pointToRowIndex(const IntPoint& point); | |
| 230 | |
| 231 // Paint an individual row | |
| 232 void paintRow(GraphicsContext* gc, const IntRect& rect, int rowIndex); | |
| 233 | |
| 234 // Test if the given point is within the bounds of the popup window. | |
| 235 bool isPointInBounds(const IntPoint& point); | |
| 236 | |
| 237 // Called when the user presses a text key. Does a prefix-search of the ite
ms. | |
| 238 void typeAheadFind(const PlatformKeyboardEvent& event); | |
| 239 | |
| 240 // Returns the font to use for the given row | |
| 241 Font getRowFont(int index); | |
| 242 | |
| 243 // This is the index of the item marked as "selected" - i.e. displayed in th
e widget on the | |
| 244 // page. | |
| 245 int m_originalIndex; | |
| 246 | |
| 247 // This is the index of the item that the user is hovered over or has select
ed using the | |
| 248 // keyboard in the list. They have not confirmed this selection by clicking
or pressing | |
| 249 // enter yet however. | |
| 250 int m_selectedIndex; | |
| 251 | |
| 252 // True if we should accept the selectedIndex as chosen, even if the popup | |
| 253 // is "abandoned". This is used for keyboard navigation, where we want the | |
| 254 // selection to change immediately. | |
| 255 bool m_acceptOnAbandon; | |
| 256 | |
| 257 // This is the number of rows visible in the popup. The maximum number visib
le at a time is | |
| 258 // defined as being kMaxVisibleRows. For a scrolled popup, this can be thoug
ht of as the | |
| 259 // page size in data units. | |
| 260 int m_visibleRows; | |
| 261 | |
| 262 // Our suggested width, not including scrollbar. | |
| 263 int m_baseWidth; | |
| 264 | |
| 265 // A list of the options contained within the <select> | |
| 266 Vector<ListItem*> m_items; | |
| 267 | |
| 268 // The <select> PopupMenuClient that opened us. | |
| 269 PopupMenuClient* m_popupClient; | |
| 270 | |
| 271 // The scrollbar which has mouse capture. Mouse events go straight to this | |
| 272 // if non-NULL. | |
| 273 RefPtr<PlatformScrollbar> m_capturingScrollbar; | |
| 274 | |
| 275 // The last scrollbar that the mouse was over. Used for mouseover highlight
s. | |
| 276 RefPtr<PlatformScrollbar> m_lastScrollbarUnderMouse; | |
| 277 | |
| 278 // The string the user has typed so far into the popup. Used for typeAheadFi
nd. | |
| 279 String m_typedString; | |
| 280 | |
| 281 // The char the user has hit repeatedly. Used for typeAheadFind. | |
| 282 UChar m_repeatingChar; | |
| 283 | |
| 284 // The last time the user hit a key. Used for typeAheadFind. | |
| 285 TimeStamp m_lastCharTime; | |
| 286 }; | |
| 287 | |
| 288 static PlatformMouseEvent constructRelativeMouseEvent(const PlatformMouseEvent&
e, | |
| 289 FrameView* parent, | |
| 290 FrameView* child) | |
| 291 { | |
| 292 IntPoint pos = parent->convertSelfToChild(child, e.pos()); | |
| 293 | |
| 294 // FIXME(beng): This is a horrible hack since PlatformWheelEvent has no sett
ers for x/y. | |
| 295 // Need to add setters and get patch back upstream to webkit so
urce. | |
| 296 PlatformMouseEvent relativeEvent = e; | |
| 297 IntPoint& relativePos = const_cast<IntPoint&>(relativeEvent.pos()); | |
| 298 relativePos.setX(pos.x()); | |
| 299 relativePos.setY(pos.y()); | |
| 300 return relativeEvent; | |
| 301 } | |
| 302 | |
| 303 static PlatformWheelEvent constructRelativeWheelEvent(const PlatformWheelEvent&
e, | |
| 304 FrameView* parent, | |
| 305 FrameView* child) | |
| 306 { | |
| 307 IntPoint pos = parent->convertSelfToChild(child, e.pos()); | |
| 308 | |
| 309 // FIXME(beng): This is a horrible hack since PlatformWheelEvent has no sett
ers for x/y. | |
| 310 // Need to add setters and get patch back upstream to webkit so
urce. | |
| 311 PlatformWheelEvent relativeEvent = e; | |
| 312 IntPoint& relativePos = const_cast<IntPoint&>(relativeEvent.pos()); | |
| 313 relativePos.setX(pos.x()); | |
| 314 relativePos.setY(pos.y()); | |
| 315 return relativeEvent; | |
| 316 } | |
| 317 | |
| 318 /////////////////////////////////////////////////////////////////////////////// | |
| 319 // PopupContainer implementation | |
| 320 | |
| 321 // Get a pointer to the PopupContainer instance for the m_popup HWND, since we | |
| 322 // can't augment the PopupMenu class (above the portability layer). We store the | |
| 323 // PopupContainer in the HWND member, which is fairly hacky. | |
| 324 static PopupContainer* popupWindow(HWND popup) | |
| 325 { | |
| 326 return reinterpret_cast<PopupContainer*>(popup); | |
| 327 } | |
| 328 | |
| 329 // static | |
| 330 HWND PopupContainer::Create(PopupMenuClient* client) | |
| 331 { | |
| 332 PopupContainer* container = new PopupContainer(client); | |
| 333 return reinterpret_cast<HWND>(container); | |
| 334 } | |
| 335 | |
| 336 PopupContainer::PopupContainer(PopupMenuClient* client) | |
| 337 : m_listBox(new PopupListBox(client)) | |
| 338 { | |
| 339 // FrameViews are created with a refcount of 1 so it needs releasing after w
e | |
| 340 // assign it to a RefPtr. | |
| 341 m_listBox->deref(); | |
| 342 | |
| 343 setScrollbarsMode(ScrollbarAlwaysOff); | |
| 344 } | |
| 345 | |
| 346 PopupContainer::~PopupContainer() | |
| 347 { | |
| 348 if (m_listBox) | |
| 349 removeChild(m_listBox.get()); | |
| 350 } | |
| 351 | |
| 352 void PopupContainer::showPopup(FrameView* view) | |
| 353 { | |
| 354 // Pre-layout, our size matches the <select> dropdown control. | |
| 355 int selectHeight = frameGeometry().height(); | |
| 356 | |
| 357 // Lay everything out to figure out our preferred size, then tell the view's | |
| 358 // WidgetClient about it. It should assign us a client. | |
| 359 layout(); | |
| 360 | |
| 361 WidgetClientWin* widgetClient = | |
| 362 static_cast<WidgetClientWin*>(view->client()); | |
| 363 ChromeClientWin* chromeClient = | |
| 364 static_cast<ChromeClientWin*>(view->frame()->page()->chrome()->client())
; | |
| 365 if (widgetClient && chromeClient) { | |
| 366 // If the popup would extend past the bottom of the screen, open upwards | |
| 367 // instead. | |
| 368 FloatRect screen = screenRect(view); | |
| 369 IntRect widgetRect = chromeClient->windowToScreen(frameGeometry()); | |
| 370 if (widgetRect.bottom() > static_cast<int>(screen.bottom())) | |
| 371 widgetRect.move(0, -(widgetRect.height() + selectHeight)); | |
| 372 | |
| 373 widgetClient->popupOpened(this, widgetRect); | |
| 374 } | |
| 375 | |
| 376 // Must get called after we have a client and containingWindow. | |
| 377 addChild(m_listBox.get()); | |
| 378 | |
| 379 // Enable scrollbars after the listbox is inserted into the hierarchy, so | |
| 380 // it has a proper WidgetClient. | |
| 381 m_listBox->setVScrollbarMode(ScrollbarAuto); | |
| 382 | |
| 383 m_listBox->scrollToRevealSelection(); | |
| 384 | |
| 385 invalidate(); | |
| 386 } | |
| 387 | |
| 388 void PopupContainer::hidePopup() | |
| 389 { | |
| 390 invalidate(); | |
| 391 | |
| 392 m_listBox->disconnectClient(); | |
| 393 removeChild(m_listBox.get()); | |
| 394 | |
| 395 if (client()) | |
| 396 static_cast<WidgetClientWin*>(client())->popupClosed(this); | |
| 397 } | |
| 398 | |
| 399 void PopupContainer::layout() | |
| 400 { | |
| 401 m_listBox->layout(); | |
| 402 | |
| 403 // Place the listbox within our border. | |
| 404 m_listBox->move(kBorderSize, kBorderSize); | |
| 405 | |
| 406 // Size ourselves to contain listbox + border. | |
| 407 resize(m_listBox->width() + kBorderSize*2, m_listBox->height() + kBorderSize
*2); | |
| 408 | |
| 409 invalidate(); | |
| 410 } | |
| 411 | |
| 412 bool PopupContainer::handleMouseDownEvent(const PlatformMouseEvent& event) | |
| 413 { | |
| 414 return m_listBox->handleMouseDownEvent( | |
| 415 constructRelativeMouseEvent(event, this, m_listBox.get())); | |
| 416 } | |
| 417 | |
| 418 bool PopupContainer::handleMouseMoveEvent(const PlatformMouseEvent& event) | |
| 419 { | |
| 420 return m_listBox->handleMouseMoveEvent( | |
| 421 constructRelativeMouseEvent(event, this, m_listBox.get())); | |
| 422 } | |
| 423 | |
| 424 bool PopupContainer::handleMouseReleaseEvent(const PlatformMouseEvent& event) | |
| 425 { | |
| 426 return m_listBox->handleMouseReleaseEvent( | |
| 427 constructRelativeMouseEvent(event, this, m_listBox.get())); | |
| 428 } | |
| 429 | |
| 430 bool PopupContainer::handleWheelEvent(const PlatformWheelEvent& event) | |
| 431 { | |
| 432 return m_listBox->handleWheelEvent( | |
| 433 constructRelativeWheelEvent(event, this, m_listBox.get())); | |
| 434 } | |
| 435 | |
| 436 bool PopupContainer::handleKeyEvent(const PlatformKeyboardEvent& event) | |
| 437 { | |
| 438 return m_listBox->handleKeyEvent(event); | |
| 439 } | |
| 440 | |
| 441 void PopupContainer::hide() { | |
| 442 m_listBox->abandon(); | |
| 443 } | |
| 444 | |
| 445 void PopupContainer::paint(GraphicsContext* gc, const IntRect& rect) | |
| 446 { | |
| 447 // adjust coords for scrolled frame | |
| 448 IntRect r = intersection(rect, frameGeometry()); | |
| 449 int tx = x(); | |
| 450 int ty = y(); | |
| 451 | |
| 452 r.move(-tx, -ty); | |
| 453 | |
| 454 gc->translate(static_cast<float>(tx), static_cast<float>(ty)); | |
| 455 m_listBox->paint(gc, r); | |
| 456 gc->translate(-static_cast<float>(tx), -static_cast<float>(ty)); | |
| 457 | |
| 458 paintBorder(gc, rect); | |
| 459 } | |
| 460 | |
| 461 void PopupContainer::paintBorder(GraphicsContext* gc, const IntRect& rect) | |
| 462 { | |
| 463 // FIXME(mpcomplete): where do we get the border color from? | |
| 464 Color borderColor(127, 157, 185); | |
| 465 | |
| 466 gc->setStrokeStyle(NoStroke); | |
| 467 gc->setFillColor(borderColor); | |
| 468 | |
| 469 int tx = x(); | |
| 470 int ty = y(); | |
| 471 | |
| 472 // top, left, bottom, right | |
| 473 gc->drawRect(IntRect(tx, ty, width(), kBorderSize)); | |
| 474 gc->drawRect(IntRect(tx, ty, kBorderSize, height())); | |
| 475 gc->drawRect(IntRect(tx, ty + height() - kBorderSize, width(), kBorderSize))
; | |
| 476 gc->drawRect(IntRect(tx + width() - kBorderSize, ty, kBorderSize, height()))
; | |
| 477 } | |
| 478 | |
| 479 | |
| 480 /////////////////////////////////////////////////////////////////////////////// | |
| 481 // PopupListBox implementation | |
| 482 | |
| 483 bool PopupListBox::handleMouseDownEvent(const PlatformMouseEvent& event) | |
| 484 { | |
| 485 PlatformScrollbar* scrollbar = scrollbarUnderMouse(event); | |
| 486 if (scrollbar) { | |
| 487 m_capturingScrollbar = scrollbar; | |
| 488 m_capturingScrollbar->handleMousePressEvent(event); | |
| 489 return true; | |
| 490 } | |
| 491 | |
| 492 if (!isPointInBounds(event.pos())) | |
| 493 abandon(); | |
| 494 | |
| 495 return true; | |
| 496 } | |
| 497 | |
| 498 bool PopupListBox::handleMouseMoveEvent(const PlatformMouseEvent& event) | |
| 499 { | |
| 500 if (m_capturingScrollbar) { | |
| 501 m_capturingScrollbar->handleMouseMoveEvent(event); | |
| 502 return true; | |
| 503 } | |
| 504 | |
| 505 PlatformScrollbar* scrollbar = scrollbarUnderMouse(event); | |
| 506 if (m_lastScrollbarUnderMouse != scrollbar) { | |
| 507 // Send mouse exited to the old scrollbar. | |
| 508 if (m_lastScrollbarUnderMouse) | |
| 509 m_lastScrollbarUnderMouse->handleMouseOutEvent(event); | |
| 510 m_lastScrollbarUnderMouse = scrollbar; | |
| 511 } | |
| 512 | |
| 513 if (scrollbar) { | |
| 514 scrollbar->handleMouseMoveEvent(event); | |
| 515 return true; | |
| 516 } | |
| 517 | |
| 518 if (!isPointInBounds(event.pos())) | |
| 519 return false; | |
| 520 | |
| 521 selectIndex(pointToRowIndex(event.pos())); | |
| 522 return true; | |
| 523 } | |
| 524 | |
| 525 bool PopupListBox::handleMouseReleaseEvent(const PlatformMouseEvent& event) | |
| 526 { | |
| 527 if (m_capturingScrollbar) { | |
| 528 m_capturingScrollbar->handleMouseReleaseEvent(event); | |
| 529 m_capturingScrollbar = 0; | |
| 530 return true; | |
| 531 } | |
| 532 | |
| 533 if (!isPointInBounds(event.pos())) | |
| 534 return true; | |
| 535 | |
| 536 acceptIndex(pointToRowIndex(event.pos())); | |
| 537 return true; | |
| 538 } | |
| 539 | |
| 540 bool PopupListBox::handleWheelEvent(const PlatformWheelEvent& event) | |
| 541 { | |
| 542 if (!isPointInBounds(event.pos())) { | |
| 543 abandon(); | |
| 544 return true; | |
| 545 } | |
| 546 | |
| 547 // Pass it off to the scroll view. | |
| 548 // Sadly, WebCore devs don't understand the whole "const" thing. | |
| 549 wheelEvent(const_cast<PlatformWheelEvent&>(event)); | |
| 550 return true; | |
| 551 } | |
| 552 | |
| 553 bool PopupListBox::handleKeyEvent(const PlatformKeyboardEvent& event) | |
| 554 { | |
| 555 if (event.type() == PlatformKeyboardEvent::KeyUp) | |
| 556 return true; | |
| 557 | |
| 558 if (numItems() == 0 && event.windowsVirtualKeyCode() != VK_ESCAPE) | |
| 559 return true; | |
| 560 | |
| 561 int oldIndex = m_selectedIndex; | |
| 562 | |
| 563 switch (event.windowsVirtualKeyCode()) { | |
| 564 case VK_ESCAPE: | |
| 565 abandon(); // may delete this | |
| 566 return true; | |
| 567 case VK_RETURN: | |
| 568 acceptIndex(m_selectedIndex); // may delete this | |
| 569 return true; | |
| 570 case VK_UP: | |
| 571 adjustSelectedIndex(-1); | |
| 572 break; | |
| 573 case VK_DOWN: | |
| 574 adjustSelectedIndex(1); | |
| 575 break; | |
| 576 case VK_PRIOR: | |
| 577 adjustSelectedIndex(-m_visibleRows); | |
| 578 break; | |
| 579 case VK_NEXT: | |
| 580 adjustSelectedIndex(m_visibleRows); | |
| 581 break; | |
| 582 case VK_HOME: | |
| 583 adjustSelectedIndex(-m_selectedIndex); | |
| 584 break; | |
| 585 case VK_END: | |
| 586 adjustSelectedIndex(m_items.size()); | |
| 587 break; | |
| 588 default: | |
| 589 if (!event.ctrlKey() && !event.altKey() && !event.metaKey() && | |
| 590 isPrintableChar(event.windowsVirtualKeyCode())) { | |
| 591 typeAheadFind(event); | |
| 592 } | |
| 593 break; | |
| 594 } | |
| 595 | |
| 596 if (m_originalIndex != m_selectedIndex) { | |
| 597 // Keyboard events should update the selection immediately (but we don't | |
| 598 // want to fire the onchange event until the popup is closed, to match | |
| 599 // IE). We change the original index so we revert to that when the | |
| 600 // popup is closed. | |
| 601 m_acceptOnAbandon = true; | |
| 602 setOriginalIndex(m_selectedIndex); | |
| 603 m_popupClient->setTextFromItem(m_selectedIndex); | |
| 604 } | |
| 605 | |
| 606 return true; | |
| 607 } | |
| 608 | |
| 609 // From HTMLSelectElement.cpp | |
| 610 static String stripLeadingWhiteSpace(const String& string) | |
| 611 { | |
| 612 int length = string.length(); | |
| 613 int i; | |
| 614 for (i = 0; i < length; ++i) | |
| 615 if (string[i] != noBreakSpace && | |
| 616 (string[i] <= 0x7F ? !isspace(string[i]) : (direction(string[i]) !=
WhiteSpaceNeutral))) | |
| 617 break; | |
| 618 | |
| 619 return string.substring(i, length - i); | |
| 620 } | |
| 621 | |
| 622 // From HTMLSelectElement.cpp, with modifications | |
| 623 void PopupListBox::typeAheadFind(const PlatformKeyboardEvent& event) | |
| 624 { | |
| 625 TimeStamp now = static_cast<TimeStamp>(currentTime() * 1000.0f); | |
| 626 TimeStamp delta = now - m_lastCharTime; | |
| 627 | |
| 628 m_lastCharTime = now; | |
| 629 | |
| 630 UChar c = event.windowsVirtualKeyCode(); | |
| 631 | |
| 632 String prefix; | |
| 633 int searchStartOffset = 1; | |
| 634 if (delta > kTypeAheadTimeoutMs) { | |
| 635 m_typedString = prefix = String(&c, 1); | |
| 636 m_repeatingChar = c; | |
| 637 } else { | |
| 638 m_typedString.append(c); | |
| 639 | |
| 640 if (c == m_repeatingChar) | |
| 641 // The user is likely trying to cycle through all the items starting
with this character, so just search on the character | |
| 642 prefix = String(&c, 1); | |
| 643 else { | |
| 644 m_repeatingChar = 0; | |
| 645 prefix = m_typedString; | |
| 646 searchStartOffset = 0; | |
| 647 } | |
| 648 } | |
| 649 | |
| 650 int itemCount = numItems(); | |
| 651 int index = (m_selectedIndex + searchStartOffset) % itemCount; | |
| 652 for (int i = 0; i < itemCount; i++, index = (index + 1) % itemCount) { | |
| 653 if (!isSelectableItem(index)) | |
| 654 continue; | |
| 655 | |
| 656 if (stripLeadingWhiteSpace(m_items[index]->label).startsWith(prefix, fal
se)) { | |
| 657 selectIndex(index); | |
| 658 return; | |
| 659 } | |
| 660 } | |
| 661 } | |
| 662 | |
| 663 void PopupListBox::paint(GraphicsContext* gc, const IntRect& rect) | |
| 664 { | |
| 665 // adjust coords for scrolled frame | |
| 666 IntRect r = intersection(rect, frameGeometry()); | |
| 667 int tx = x() - contentsX(); | |
| 668 int ty = y() - contentsY(); | |
| 669 | |
| 670 r.move(-tx, -ty); | |
| 671 | |
| 672 LOG(("PopupListBox::paint [%d,%d] [r: %d,%d,%d,%d]", tx, ty, | |
| 673 r.x(), r.y(), r.width(), r.height())); | |
| 674 | |
| 675 // set clip rect to match revised damage rect | |
| 676 gc->save(); | |
| 677 gc->translate(static_cast<float>(tx), static_cast<float>(ty)); | |
| 678 gc->clip(r); | |
| 679 | |
| 680 // TODO(mpcomplete): Can we optimize scrolling to not require repainting the | |
| 681 // entire window? Should we? | |
| 682 for (int i = 0; i < numItems(); ++i) | |
| 683 paintRow(gc, r, i); | |
| 684 | |
| 685 // Special case for an empty popup. | |
| 686 if (numItems() == 0) | |
| 687 gc->fillRect(r, Color::white); | |
| 688 | |
| 689 gc->restore(); | |
| 690 | |
| 691 ScrollView::paint(gc, rect); | |
| 692 } | |
| 693 | |
| 694 static RenderStyle* getPopupClientStyleForRow(PopupMenuClient* client, int rowIn
dex) { | |
| 695 RenderStyle* style = client->itemStyle(rowIndex); | |
| 696 if (!style) | |
| 697 style = client->clientStyle(); | |
| 698 return style; | |
| 699 } | |
| 700 | |
| 701 void PopupListBox::paintRow(GraphicsContext* gc, const IntRect& rect, int rowInd
ex) | |
| 702 { | |
| 703 // This code is based largely on RenderListBox::paint* methods. | |
| 704 | |
| 705 IntRect rowRect = getRowBounds(rowIndex); | |
| 706 if (!rowRect.intersects(rect)) | |
| 707 return; | |
| 708 | |
| 709 RenderStyle* style = getPopupClientStyleForRow(m_popupClient, rowIndex); | |
| 710 | |
| 711 // Paint background | |
| 712 Color backColor, textColor; | |
| 713 if (rowIndex == m_selectedIndex) { | |
| 714 backColor = theme()->activeListBoxSelectionBackgroundColor(); | |
| 715 textColor = theme()->activeListBoxSelectionForegroundColor(); | |
| 716 } else { | |
| 717 backColor = m_popupClient->itemBackgroundColor(rowIndex); | |
| 718 textColor = style->color(); | |
| 719 } | |
| 720 | |
| 721 // If we have a transparent background, make sure it has a color to blend | |
| 722 // against. | |
| 723 if (backColor.hasAlpha()) | |
| 724 gc->fillRect(rowRect, Color::white); | |
| 725 | |
| 726 gc->fillRect(rowRect, backColor); | |
| 727 gc->setFillColor(textColor); | |
| 728 | |
| 729 LOG(("paintRow %d, [%d, %d, %d, %d] %x on %x", rowIndex, | |
| 730 rowRect.x(), rowRect.y(), rowRect.width(), rowRect.height(), | |
| 731 textColor.rgb(), backColor.rgb())); | |
| 732 | |
| 733 Font itemFont = getRowFont(rowIndex); | |
| 734 gc->setFont(itemFont); | |
| 735 | |
| 736 // Bunch of shit to deal with RTL text... | |
| 737 String itemText = m_popupClient->itemText(rowIndex); | |
| 738 unsigned length = itemText.length(); | |
| 739 const UChar* str = itemText.characters(); | |
| 740 | |
| 741 TextRun textRun(str, length, false, 0, 0, style->direction() == RTL, style->
unicodeBidi() == Override); | |
| 742 | |
| 743 // Draw the item text | |
| 744 // TODO(ojan): http://b/1210481 We should get the padding of individual opti
on elements. | |
| 745 rowRect.move(theme()->popupInternalPaddingLeft(style), itemFont.ascent()); | |
| 746 if (style->direction() == RTL) { | |
| 747 // Right-justify the text for RTL style. | |
| 748 rowRect.move(rowRect.width() - itemFont.width(textRun) - | |
| 749 2 * theme()->popupInternalPaddingLeft(style), 0); | |
| 750 } | |
| 751 gc->drawBidiText(textRun, rowRect.location()); | |
| 752 } | |
| 753 | |
| 754 Font PopupListBox::getRowFont(int rowIndex) | |
| 755 { | |
| 756 Font itemFont = m_popupClient->itemStyle(rowIndex)->font(); | |
| 757 if (m_popupClient->itemIsLabel(rowIndex)) { | |
| 758 // Bold-ify labels (ie, an <optgroup> heading). | |
| 759 FontDescription d = itemFont.fontDescription(); | |
| 760 d.setWeight(FontWeightBold); | |
| 761 Font font(d, itemFont.letterSpacing(), itemFont.wordSpacing()); | |
| 762 font.update(0); | |
| 763 return font; | |
| 764 } | |
| 765 | |
| 766 return itemFont; | |
| 767 } | |
| 768 | |
| 769 void PopupListBox::abandon() | |
| 770 { | |
| 771 RefPtr<PopupListBox> keepAlive(this); | |
| 772 | |
| 773 m_selectedIndex = m_originalIndex; | |
| 774 | |
| 775 if (m_acceptOnAbandon) | |
| 776 m_popupClient->valueChanged(m_selectedIndex); | |
| 777 | |
| 778 // valueChanged may have torn down the popup! | |
| 779 if (m_popupClient) | |
| 780 m_popupClient->hidePopup(); | |
| 781 } | |
| 782 | |
| 783 int PopupListBox::pointToRowIndex(const IntPoint& point) | |
| 784 { | |
| 785 int y = contentsY() + point.y(); | |
| 786 | |
| 787 // TODO(mpcomplete): binary search if perf matters. | |
| 788 for (int i = 0; i < numItems(); ++i) { | |
| 789 if (y < m_items[i]->y) | |
| 790 return i-1; | |
| 791 } | |
| 792 | |
| 793 // Last item? | |
| 794 if (y < contentsHeight()) | |
| 795 return m_items.size()-1; | |
| 796 | |
| 797 return -1; | |
| 798 } | |
| 799 | |
| 800 void PopupListBox::acceptIndex(int index) | |
| 801 { | |
| 802 ASSERT(index >= 0 && index < numItems()); | |
| 803 | |
| 804 if (isSelectableItem(index)) { | |
| 805 RefPtr<PopupListBox> keepAlive(this); | |
| 806 | |
| 807 // Tell the <select> PopupMenuClient what index was selected, and hide o
urself. | |
| 808 m_popupClient->valueChanged(index); | |
| 809 | |
| 810 // valueChanged may have torn down the popup! | |
| 811 if (m_popupClient) | |
| 812 m_popupClient->hidePopup(); | |
| 813 } | |
| 814 } | |
| 815 | |
| 816 void PopupListBox::selectIndex(int index) | |
| 817 { | |
| 818 ASSERT(index >= 0 && index < numItems()); | |
| 819 | |
| 820 if (index != m_selectedIndex && isSelectableItem(index)) { | |
| 821 invalidateRow(m_selectedIndex); | |
| 822 m_selectedIndex = index; | |
| 823 invalidateRow(m_selectedIndex); | |
| 824 | |
| 825 scrollToRevealSelection(); | |
| 826 } | |
| 827 } | |
| 828 | |
| 829 void PopupListBox::setOriginalIndex(int index) | |
| 830 { | |
| 831 m_originalIndex = m_selectedIndex = index; | |
| 832 } | |
| 833 | |
| 834 int PopupListBox::getRowHeight(int index) | |
| 835 { | |
| 836 RenderStyle* style; | |
| 837 if ((index < 0) || (!(style = m_popupClient->itemStyle(index)))) | |
| 838 style = m_popupClient->clientStyle(); | |
| 839 return style->font().height(); | |
| 840 } | |
| 841 | |
| 842 IntRect PopupListBox::getRowBounds(int index) | |
| 843 { | |
| 844 if (index >= 0) { | |
| 845 return IntRect(0, m_items[index]->y, visibleWidth(), getRowHeight(index)
); | |
| 846 } else { | |
| 847 return IntRect(0, 0, visibleWidth(), getRowHeight(index)); | |
| 848 } | |
| 849 } | |
| 850 | |
| 851 void PopupListBox::invalidateRow(int index) | |
| 852 { | |
| 853 if (index < 0) | |
| 854 return; | |
| 855 | |
| 856 updateContents(getRowBounds(index)); | |
| 857 } | |
| 858 | |
| 859 void PopupListBox::scrollToRevealRow(int index) | |
| 860 { | |
| 861 if (index < 0) | |
| 862 return; | |
| 863 | |
| 864 IntRect rowRect = getRowBounds(index); | |
| 865 | |
| 866 if (rowRect.y() < contentsY()) { | |
| 867 // Row is above current scroll position, scroll up. | |
| 868 ScrollView::setContentsPos(0, rowRect.y()); | |
| 869 } else if (rowRect.bottom() > contentsY() + visibleHeight()) { | |
| 870 // Row is below current scroll position, scroll down. | |
| 871 ScrollView::setContentsPos(0, rowRect.bottom() - visibleHeight()); | |
| 872 } | |
| 873 } | |
| 874 | |
| 875 bool PopupListBox::isSelectableItem(int index) { | |
| 876 return m_items[index]->type == TYPE_OPTION && | |
| 877 m_popupClient->itemIsEnabled(index); | |
| 878 } | |
| 879 | |
| 880 void PopupListBox::adjustSelectedIndex(int delta) | |
| 881 { | |
| 882 int targetIndex = m_selectedIndex + delta; | |
| 883 targetIndex = min(max(targetIndex, 0), numItems() - 1); | |
| 884 if (!isSelectableItem(targetIndex)) { | |
| 885 // We didn't land on an option. Try to find one. | |
| 886 // We try to select the closest index to target, prioritizing any in | |
| 887 // the range [current, target]. | |
| 888 | |
| 889 int dir = delta > 0 ? 1 : -1; | |
| 890 int testIndex = m_selectedIndex; | |
| 891 int bestIndex = m_selectedIndex; | |
| 892 bool passedTarget = false; | |
| 893 while (testIndex >= 0 && testIndex < numItems()) { | |
| 894 if (isSelectableItem(testIndex)) | |
| 895 bestIndex = testIndex; | |
| 896 if (testIndex == targetIndex) | |
| 897 passedTarget = true; | |
| 898 if (passedTarget && bestIndex != m_selectedIndex) | |
| 899 break; | |
| 900 | |
| 901 testIndex += dir; | |
| 902 } | |
| 903 | |
| 904 // Pick the best index, which may mean we don't change. | |
| 905 targetIndex = bestIndex; | |
| 906 } | |
| 907 | |
| 908 // Select the new index, and ensure its visible. We do this regardless of | |
| 909 // whether the selection changed to ensure keyboard events always bring the | |
| 910 // selection into view. | |
| 911 selectIndex(targetIndex); | |
| 912 scrollToRevealSelection(); | |
| 913 } | |
| 914 | |
| 915 void PopupListBox::updateFromElement() | |
| 916 { | |
| 917 // It happens when pressing a key to jump to an item, then use tab or | |
| 918 // mouse to get away from the select box. In that case, updateFromElement | |
| 919 // is called before abandon, which causes discarding of the select result.
| |
| 920 if (m_acceptOnAbandon) { | |
| 921 m_popupClient->valueChanged(m_selectedIndex); | |
| 922 m_acceptOnAbandon = false; | |
| 923 } | |
| 924 | |
| 925 clear(); | |
| 926 | |
| 927 int size = m_popupClient->listSize(); | |
| 928 for (int i = 0; i < size; ++i) { | |
| 929 ListItemType type; | |
| 930 if (m_popupClient->itemIsSeparator(i)) | |
| 931 type = PopupListBox::TYPE_SEPARATOR; | |
| 932 else if (m_popupClient->itemIsLabel(i)) | |
| 933 type = PopupListBox::TYPE_GROUP; | |
| 934 else | |
| 935 type = PopupListBox::TYPE_OPTION; | |
| 936 m_items.append(new ListItem(m_popupClient->itemText(i), type)); | |
| 937 } | |
| 938 | |
| 939 m_selectedIndex = m_popupClient->selectedIndex(); | |
| 940 setOriginalIndex(m_selectedIndex); | |
| 941 | |
| 942 layout(); | |
| 943 } | |
| 944 | |
| 945 void PopupListBox::layout() | |
| 946 { | |
| 947 // Size our child items. | |
| 948 int baseWidth = 0; | |
| 949 int paddingWidth = 0; | |
| 950 int y = 0; | |
| 951 for (int i = 0; i < numItems(); ++i) { | |
| 952 Font itemFont = getRowFont(i); | |
| 953 | |
| 954 // Place the item vertically. | |
| 955 m_items[i]->y = y; | |
| 956 y += itemFont.height(); | |
| 957 | |
| 958 // Ensure the popup is wide enough to fit this item. | |
| 959 String text = m_popupClient->itemText(i); | |
| 960 if (!text.isEmpty()) { | |
| 961 int width = itemFont.width(TextRun(text)); | |
| 962 baseWidth = max(baseWidth, width); | |
| 963 } | |
| 964 RenderStyle* style = getPopupClientStyleForRow(m_popupClient, i); | |
| 965 // TODO(ojan): http://b/1210481 We should get the padding of individual
option elements. | |
| 966 paddingWidth = max(paddingWidth, | |
| 967 theme()->popupInternalPaddingLeft(style) + theme()->popupInternalPad
dingRight(style)); | |
| 968 } | |
| 969 | |
| 970 int windowHeight = 0; | |
| 971 m_visibleRows = min(numItems(), kMaxVisibleRows); | |
| 972 for (int i = 0; i < m_visibleRows; ++i) { | |
| 973 int rowHeight = getRowHeight(i); | |
| 974 if (windowHeight + rowHeight > kMaxHeight) { | |
| 975 m_visibleRows = i; | |
| 976 break; | |
| 977 } | |
| 978 | |
| 979 windowHeight += rowHeight; | |
| 980 } | |
| 981 | |
| 982 if (windowHeight == 0) | |
| 983 windowHeight = min(getRowHeight(-1), kMaxHeight); | |
| 984 | |
| 985 // Set our widget and scrollable contents sizes. | |
| 986 int scrollbarWidth = 0; | |
| 987 if (m_visibleRows < numItems()) | |
| 988 scrollbarWidth = PlatformScrollbar::verticalScrollbarWidth(); | |
| 989 | |
| 990 int windowWidth = baseWidth + scrollbarWidth + paddingWidth; | |
| 991 int contentWidth = baseWidth; | |
| 992 | |
| 993 if (windowWidth < m_baseWidth) { | |
| 994 windowWidth = m_baseWidth; | |
| 995 contentWidth = m_baseWidth - scrollbarWidth - paddingWidth; | |
| 996 } else { | |
| 997 m_baseWidth = baseWidth; | |
| 998 } | |
| 999 | |
| 1000 resize(windowWidth, windowHeight); | |
| 1001 resizeContents(contentWidth, getRowBounds(numItems() - 1).bottom()); | |
| 1002 scrollToRevealSelection(); | |
| 1003 | |
| 1004 invalidate(); | |
| 1005 } | |
| 1006 | |
| 1007 void PopupListBox::clear() | |
| 1008 { | |
| 1009 for (Vector<ListItem*>::iterator it = m_items.begin(); it != m_items.end();
++it) | |
| 1010 delete *it; | |
| 1011 m_items.clear(); | |
| 1012 } | |
| 1013 | |
| 1014 bool PopupListBox::isPointInBounds(const IntPoint& point) | |
| 1015 { | |
| 1016 return numItems() != 0 && IntRect(0, 0, width(), height()).contains(point); | |
| 1017 } | |
| 1018 | |
| 1019 | |
| 1020 /////////////////////////////////////////////////////////////////////////////// | |
| 1021 // PopupMenu implementation | |
| 1022 // | |
| 1023 // Note: you cannot add methods to this class, since it is defined above the | |
| 1024 // portability layer. To access methods and properties on the | |
| 1025 // popup widgets, use |popupWindow| above. | |
| 1026 | |
| 1027 PopupMenu::PopupMenu(PopupMenuClient* client) | |
| 1028 : m_popupClient(client) | |
| 1029 , m_popup(0) | |
| 1030 , m_wasClicked(false) | |
| 1031 { | |
| 1032 } | |
| 1033 | |
| 1034 PopupMenu::~PopupMenu() | |
| 1035 { | |
| 1036 hide(); | |
| 1037 } | |
| 1038 | |
| 1039 void PopupMenu::show(const IntRect& r, FrameView* v, int index) | |
| 1040 { | |
| 1041 m_popup = PopupContainer::Create(client()); | |
| 1042 | |
| 1043 PopupContainer* popup = popupWindow(m_popup); | |
| 1044 // The rect is the size of the select box. It's usually larger than we need. | |
| 1045 // subtract border size so that usually the container will be displayed | |
| 1046 // exactly the same width as the select box. | |
| 1047 popup->listBox()->setBaseWidth(max(r.width() - kBorderSize * 2, 0)); | |
| 1048 | |
| 1049 updateFromElement(); | |
| 1050 | |
| 1051 // We set the selected item in updateFromElement(), and disregard the | |
| 1052 // index passed into this function (same as Webkit's PopupMenuWin.cpp) | |
| 1053 // TODO(ericroman): make sure this is correct, and add an assertion. | |
| 1054 // DCHECK(popupWindow(m_popup)->listBox()->selectedIndex() == index); | |
| 1055 | |
| 1056 // Convert point to main window coords. | |
| 1057 IntPoint location = v->contentsToWindow(r.location()); | |
| 1058 | |
| 1059 // Move it below the select widget. | |
| 1060 location.move(0, r.height()); | |
| 1061 | |
| 1062 IntRect popupRect(location, r.size()); | |
| 1063 popup->setFrameGeometry(popupRect); | |
| 1064 popup->showPopup(v); | |
| 1065 } | |
| 1066 | |
| 1067 void PopupMenu::hide() | |
| 1068 { | |
| 1069 if (m_popup) { | |
| 1070 PopupContainer* popup = popupWindow(m_popup); | |
| 1071 popup->hidePopup(); | |
| 1072 popup->deref(); | |
| 1073 m_popup = 0; | |
| 1074 } | |
| 1075 } | |
| 1076 | |
| 1077 void PopupMenu::updateFromElement() | |
| 1078 { | |
| 1079 popupWindow(m_popup)->listBox()->updateFromElement(); | |
| 1080 } | |
| 1081 | |
| 1082 bool PopupMenu::itemWritingDirectionIsNatural() | |
| 1083 { | |
| 1084 return false; | |
| 1085 } | |
| 1086 | |
| 1087 bool PopupMenu::up(unsigned lines) | |
| 1088 { | |
| 1089 popupWindow(m_popup)->listBox()->adjustSelectedIndex(-static_cast<int>(lines
)); | |
| 1090 return true; | |
| 1091 } | |
| 1092 | |
| 1093 bool PopupMenu::down(unsigned lines) | |
| 1094 { | |
| 1095 popupWindow(m_popup)->listBox()->adjustSelectedIndex(static_cast<int>(lines)
); | |
| 1096 return true; | |
| 1097 } | |
| 1098 | |
| 1099 int PopupMenu::focusedIndex() const | |
| 1100 { | |
| 1101 return popupWindow(m_popup)->listBox()->selectedIndex(); | |
| 1102 } | |
| 1103 | |
| 1104 void PopupMenu::valueChanged(Scrollbar*) | |
| 1105 { | |
| 1106 // FIXME | |
| 1107 } | |
| 1108 | |
| 1109 WebCore::IntRect PopupMenu::windowClipRect() const | |
| 1110 { | |
| 1111 // FIXME | |
| 1112 return WebCore::IntRect(); | |
| 1113 } | |
| 1114 | |
| 1115 } // namespace WebCore | |
| OLD | NEW |