// Copyright 2008, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "chrome/views/button_dropdown.h" #include "base/message_loop.h" #include "chrome/browser/back_forward_menu_model.h" #include "chrome/common/l10n_util.h" #include "chrome/views/view_menu_delegate.h" #include "chrome/views/view_container.h" #include "generated_resources.h" namespace ChromeViews { // How long to wait before showing the menu static const int kMenuTimerDelay = 500; //////////////////////////////////////////////////////////////////////////////// // // ButtonDropDown - constructors, destructors, initialization, cleanup // //////////////////////////////////////////////////////////////////////////////// ButtonDropDown::ButtonDropDown(Menu::Delegate* menu_delegate) : Button(), menu_delegate_(menu_delegate), y_position_on_lbuttondown_(0), show_menu_factory_(this) { } ButtonDropDown::~ButtonDropDown() { } //////////////////////////////////////////////////////////////////////////////// // // ButtonDropDown - Events // //////////////////////////////////////////////////////////////////////////////// bool ButtonDropDown::OnMousePressed(const ChromeViews::MouseEvent& e) { if (IsEnabled() && e.IsLeftMouseButton() && HitTest(WTL::CPoint(e.GetX(), e.GetY()))) { // Store the y pos of the mouse coordinates so we can use them later to // determine if the user dragged the mouse down (which should pop up the // drag down menu immediately, instead of waiting for the timer) y_position_on_lbuttondown_ = e.GetY(); // Schedule a task that will show the menu. MessageLoop::current()->PostDelayedTask(FROM_HERE, show_menu_factory_.NewRunnableMethod(&ButtonDropDown::ShowDropDownMenu, GetViewContainer()->GetHWND()), kMenuTimerDelay); } return Button::OnMousePressed(e); } void ButtonDropDown::OnMouseReleased(const ChromeViews::MouseEvent& e, bool canceled) { Button::OnMouseReleased(e, canceled); if (canceled) return; if (e.IsLeftMouseButton()) show_menu_factory_.RevokeAll(); if (IsEnabled() && e.IsRightMouseButton() && HitTest(WTL::CPoint(e.GetX(), e.GetY()))) { show_menu_factory_.RevokeAll(); // Make the button look depressed while the menu is open. // NOTE: SetState() schedules a paint, but it won't occur until after the // context menu message loop has terminated, so we PaintNow() to // update the appearance synchronously. SetState(BS_PUSHED); PaintNow(); ShowDropDownMenu(GetViewContainer()->GetHWND()); } } bool ButtonDropDown::OnMouseDragged(const ChromeViews::MouseEvent& e) { bool result = Button::OnMouseDragged(e); if (!show_menu_factory_.empty()) { // SM_CYDRAG is a pixel value for minimum dragging distance before operation // counts as a drag, and not just as a click and accidental move of a mouse. // See http://msdn2.microsoft.com/en-us/library/ms724385.aspx for details. int dragging_threshold = GetSystemMetrics(SM_CYDRAG); // If the mouse is dragged to a y position lower than where it was when // clicked then we should not wait for the menu to appear but show // it immediately. if (e.GetY() > y_position_on_lbuttondown_ + dragging_threshold) { show_menu_factory_.RevokeAll(); ShowDropDownMenu(GetViewContainer()->GetHWND()); } } return result; } //////////////////////////////////////////////////////////////////////////////// // // ButtonDropDown - Menu functions // //////////////////////////////////////////////////////////////////////////////// void ButtonDropDown::ShowDropDownMenu(HWND window) { if (menu_delegate_) { CRect lb; GetLocalBounds(&lb, true); // Both the menu position and the menu anchor type change if the UI layout // is right-to-left. CPoint menu_position = CPoint(lb.TopLeft()); menu_position.Offset(0, lb.Height() - 1); if (UILayoutIsRightToLeft()) menu_position.Offset(lb.Width() - 1, 0); Menu::AnchorPoint anchor = Menu::TOPLEFT; if (UILayoutIsRightToLeft()) anchor = Menu::TOPRIGHT; View::ConvertPointToScreen(this, &menu_position); Menu menu(menu_delegate_, anchor, window); // ID's for AppendMenu is 1-based because RunMenu will ignore the user // selection if id=0 is selected (0 = NO-OP) so we add 1 here and subtract 1 // in the handlers above to get the actual index int item_count = menu_delegate_->GetItemCount(); for (int i = 0; i < item_count; i++) { if (menu_delegate_->IsItemSeparator(i + 1)) { menu.AppendSeparator(); } else { if (menu_delegate_->HasIcon(i + 1)) menu.AppendMenuItemWithIcon(i + 1, L"", SkBitmap()); else menu.AppendMenuItem(i+1, L"", Menu::NORMAL); } } menu.RunMenuAt(menu_position.x, menu_position.y); // Need to explicitly clear mouse handler so that events get sent // properly after the menu finishes running. If we don't do this, then // the first click to other parts of the UI is eaten. SetMouseHandler(NULL); } } //////////////////////////////////////////////////////////////////////////////// // // ButtonDropDown - Accessibility // //////////////////////////////////////////////////////////////////////////////// bool ButtonDropDown::GetAccessibleDefaultAction(std::wstring* action) { DCHECK(action); action->assign(l10n_util::GetString(IDS_ACCACTION_PRESS)); return true; } bool ButtonDropDown::GetAccessibleRole(VARIANT* role) { DCHECK(role); role->vt = VT_I4; role->lVal = ROLE_SYSTEM_BUTTONDROPDOWN; return true; } bool ButtonDropDown::GetAccessibleState(VARIANT* state) { DCHECK(state); state->lVal |= STATE_SYSTEM_HASPOPUP; return true; } } // namespace ChromeViews