diff options
author | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
---|---|---|
committer | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
commit | 1b67dd855c41f5a0cda7ec2a68d98071986ca703 (patch) | |
tree | ab243607f74f78200787bba5b9b88f07ef1b966f /indra/llui/llmenugl.cpp | |
parent | 6d6eabca44d08d5b97bfe3e941d2b9687c2246ea (diff) | |
parent | e1623bb276f83a43ce7a197e388720c05bdefe61 (diff) |
Merge remote-tracking branch 'origin/main' into DRTVWR-600-maint-A
# Conflicts:
# autobuild.xml
# indra/cmake/CMakeLists.txt
# indra/cmake/GoogleMock.cmake
# indra/llaudio/llaudioengine_fmodstudio.cpp
# indra/llaudio/llaudioengine_fmodstudio.h
# indra/llaudio/lllistener_fmodstudio.cpp
# indra/llaudio/lllistener_fmodstudio.h
# indra/llaudio/llstreamingaudio_fmodstudio.cpp
# indra/llaudio/llstreamingaudio_fmodstudio.h
# indra/llcharacter/llmultigesture.cpp
# indra/llcharacter/llmultigesture.h
# indra/llimage/llimage.cpp
# indra/llimage/llimagepng.cpp
# indra/llimage/llimageworker.cpp
# indra/llimage/tests/llimageworker_test.cpp
# indra/llmessage/tests/llmockhttpclient.h
# indra/llprimitive/llgltfmaterial.h
# indra/llrender/llfontfreetype.cpp
# indra/llui/llcombobox.cpp
# indra/llui/llfolderview.cpp
# indra/llui/llfolderviewmodel.h
# indra/llui/lllineeditor.cpp
# indra/llui/lllineeditor.h
# indra/llui/lltextbase.cpp
# indra/llui/lltextbase.h
# indra/llui/lltexteditor.cpp
# indra/llui/lltextvalidate.cpp
# indra/llui/lltextvalidate.h
# indra/llui/lluictrl.h
# indra/llui/llview.cpp
# indra/llwindow/llwindowmacosx.cpp
# indra/newview/app_settings/settings.xml
# indra/newview/llappearancemgr.cpp
# indra/newview/llappearancemgr.h
# indra/newview/llavatarpropertiesprocessor.cpp
# indra/newview/llavatarpropertiesprocessor.h
# indra/newview/llbreadcrumbview.cpp
# indra/newview/llbreadcrumbview.h
# indra/newview/llbreastmotion.cpp
# indra/newview/llbreastmotion.h
# indra/newview/llconversationmodel.h
# indra/newview/lldensityctrl.cpp
# indra/newview/lldensityctrl.h
# indra/newview/llface.inl
# indra/newview/llfloatereditsky.cpp
# indra/newview/llfloatereditwater.cpp
# indra/newview/llfloateremojipicker.h
# indra/newview/llfloaterimsessiontab.cpp
# indra/newview/llfloaterprofiletexture.cpp
# indra/newview/llfloaterprofiletexture.h
# indra/newview/llgesturemgr.cpp
# indra/newview/llgesturemgr.h
# indra/newview/llimpanel.cpp
# indra/newview/llimpanel.h
# indra/newview/llinventorybridge.cpp
# indra/newview/llinventorybridge.h
# indra/newview/llinventoryclipboard.cpp
# indra/newview/llinventoryclipboard.h
# indra/newview/llinventoryfunctions.cpp
# indra/newview/llinventoryfunctions.h
# indra/newview/llinventorygallery.cpp
# indra/newview/lllistbrowser.cpp
# indra/newview/lllistbrowser.h
# indra/newview/llpanelobjectinventory.cpp
# indra/newview/llpanelprofile.cpp
# indra/newview/llpanelprofile.h
# indra/newview/llpreviewgesture.cpp
# indra/newview/llsavedsettingsglue.cpp
# indra/newview/llsavedsettingsglue.h
# indra/newview/lltooldraganddrop.cpp
# indra/newview/llurllineeditorctrl.cpp
# indra/newview/llvectorperfoptions.cpp
# indra/newview/llvectorperfoptions.h
# indra/newview/llviewerparceloverlay.cpp
# indra/newview/llviewertexlayer.cpp
# indra/newview/llviewertexturelist.cpp
# indra/newview/macmain.h
# indra/test/test.cpp
Diffstat (limited to 'indra/llui/llmenugl.cpp')
-rw-r--r-- | indra/llui/llmenugl.cpp | 8809 |
1 files changed, 4404 insertions, 4405 deletions
diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 5dc92e555a..9a058f420a 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -1,4405 +1,4404 @@ -/** - * @file llmenugl.cpp - * @brief LLMenuItemGL base class - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -//***************************************************************************** -// -// This file contains the opengl based menu implementation. -// -// NOTES: A menu label is split into 4 columns. The left column, the -// label colum, the accelerator column, and the right column. The left -// column is used for displaying boolean values for toggle and check -// controls. The right column is used for submenus. -// -//***************************************************************************** - -//#include "llviewerprecompiledheaders.h" -#include "linden_common.h" - -#include "llmenugl.h" - -#include "llgl.h" -#include "llmath.h" -#include "llrender.h" -#include "llfocusmgr.h" -#include "llcoord.h" -#include "llwindow.h" -#include "llcriticaldamp.h" -#include "lluictrlfactory.h" - -#include "llbutton.h" -#include "llfontgl.h" -#include "llresmgr.h" -#include "lltrans.h" -#include "llui.h" - -#include "llstl.h" - -#include "v2math.h" -#include <set> -#include <boost/tokenizer.hpp> - -// static -LLMenuHolderGL *LLMenuGL::sMenuContainer = NULL; -view_listener_t::listener_map_t view_listener_t::sListeners; - -S32 MENU_BAR_HEIGHT = 0; -S32 MENU_BAR_WIDTH = 0; - -///============================================================================ -/// Local function declarations, constants, enums, and typedefs -///============================================================================ - -const S32 LABEL_BOTTOM_PAD_PIXELS = 2; - -const U32 LEFT_PAD_PIXELS = 3; -const U32 LEFT_WIDTH_PIXELS = 15; -const U32 LEFT_PLAIN_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS; - -const U32 RIGHT_PAD_PIXELS = 7; -const U32 RIGHT_WIDTH_PIXELS = 15; -const U32 RIGHT_PLAIN_PIXELS = RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS; - -const U32 PLAIN_PAD_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS; - -const U32 BRIEF_PAD_PIXELS = 2; - -const U32 SEPARATOR_HEIGHT_PIXELS = 8; -const S32 TEAROFF_SEPARATOR_HEIGHT_PIXELS = 10; -const S32 MENU_ITEM_PADDING = 4; - -const std::string SEPARATOR_NAME("separator"); -const std::string VERTICAL_SEPARATOR_LABEL( "|" ); - -const std::string LLMenuGL::BOOLEAN_TRUE_PREFIX( "\xE2\x9C\x94" ); // U+2714 HEAVY CHECK MARK -const std::string LLMenuGL::BRANCH_SUFFIX( "\xe2\x96\xb8" ); // U+25B6 BLACK RIGHT-POINTING TRIANGLE -const std::string LLMenuGL::ARROW_UP ("^^^^^^^"); -const std::string LLMenuGL::ARROW_DOWN("vvvvvvv"); - -const F32 MAX_MOUSE_SLOPE_SUB_MENU = 0.9f; - -bool LLMenuGL::sKeyboardMode = false; - -LLHandle<LLView> LLMenuHolderGL::sItemLastSelectedHandle; -LLFrameTimer LLMenuHolderGL::sItemActivationTimer; - -const F32 ACTIVATE_HIGHLIGHT_TIME = 0.3f; - -static MenuRegistry::Register<LLMenuItemGL> register_menu_item("menu_item"); -static MenuRegistry::Register<LLMenuItemSeparatorGL> register_separator("menu_item_separator"); -static MenuRegistry::Register<LLMenuItemCallGL> register_menu_item_call("menu_item_call"); -static MenuRegistry::Register<LLMenuItemCheckGL> register_menu_item_check("menu_item_check"); -// Created programmatically but we need to specify custom colors in xml -static MenuRegistry::Register<LLMenuItemTearOffGL> register_menu_item_tear_off("menu_item_tear_off"); -static MenuRegistry::Register<LLMenuGL> register_menu("menu"); - -static LLDefaultChildRegistry::Register<LLMenuGL> register_menu_default("menu"); - - - -///============================================================================ -/// Class LLMenuItemGL -///============================================================================ - -LLMenuItemGL::Params::Params() -: shortcut("shortcut"), - jump_key("jump_key", KEY_NONE), - use_mac_ctrl("use_mac_ctrl", false), - allow_key_repeat("allow_key_repeat", false), - rect("rect"), - left("left"), - top("top"), - right("right"), - bottom("bottom"), - width("width"), - height("height"), - bottom_delta("bottom_delta"), - left_delta("left_delta"), - enabled_color("enabled_color"), - disabled_color("disabled_color"), - highlight_bg_color("highlight_bg_color"), - highlight_fg_color("highlight_fg_color") -{ - changeDefault(mouse_opaque, true); -} - -// Default constructor -LLMenuItemGL::LLMenuItemGL(const LLMenuItemGL::Params& p) -: LLUICtrl(p), - mJumpKey(p.jump_key), - mAllowKeyRepeat(p.allow_key_repeat), - mHighlight( false ), - mGotHover( false ), - mBriefItem( false ), - mDrawTextDisabled( false ), - mFont(p.font), - mAcceleratorKey(KEY_NONE), - mAcceleratorMask(MASK_NONE), - mLabel(p.label.isProvided() ? p.label() : p.name()), - mEnabledColor(p.enabled_color()), - mDisabledColor(p.disabled_color()), - mHighlightBackground(p.highlight_bg_color()), - mHighlightForeground(p.highlight_fg_color()) -{ -#ifdef LL_DARWIN - // See if this Mac accelerator should really use the ctrl key and not get mapped to cmd - bool useMacCtrl = p.use_mac_ctrl; -#endif // LL_DARWIN - - std::string shortcut = p.shortcut; - if (shortcut.find("control") != shortcut.npos) - { -#ifdef LL_DARWIN - if ( useMacCtrl ) - { - mAcceleratorMask |= MASK_MAC_CONTROL; - } -#endif // LL_DARWIN - mAcceleratorMask |= MASK_CONTROL; - } - if (shortcut.find("alt") != shortcut.npos) - { - mAcceleratorMask |= MASK_ALT; - } - if (shortcut.find("shift") != shortcut.npos) - { - mAcceleratorMask |= MASK_SHIFT; - } - S32 pipe_pos = shortcut.rfind("|"); - std::string key_str = shortcut.substr(pipe_pos+1); - - LLKeyboard::keyFromString(key_str, &mAcceleratorKey); - - LL_DEBUGS("HotKeys") << "Process short cut key: shortcut: " << shortcut - << ", key str: " << key_str - << ", accelerator mask: " << mAcceleratorMask - << ", accelerator key: " << mAcceleratorKey - << LL_ENDL; -} - -//virtual -void LLMenuItemGL::setValue(const LLSD& value) -{ - setLabel(value.asString()); -} - -//virtual -LLSD LLMenuItemGL::getValue() const -{ - return getLabel(); -} - -//virtual -bool LLMenuItemGL::hasAccelerator(const KEY &key, const MASK &mask) const -{ - return (mAcceleratorKey == key) && (mAcceleratorMask == mask); -} - -//virtual -bool LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask) -{ - if( getEnabled() && (!gKeyboard->getKeyRepeated(key) || mAllowKeyRepeat) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) ) - { - onCommit(); - return true; - } - return false; -} - -bool LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask) -{ - getWindow()->setCursor(UI_CURSOR_ARROW); - return true; -} - -//virtual -bool LLMenuItemGL::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - return LLUICtrl::handleRightMouseDown(x,y,mask); -} - -void LLMenuItemGL::onMouseEnter(S32 x, S32 y, MASK mask) -{ - setHover(true); - LLUICtrl::onMouseEnter(x,y,mask); -} - -void LLMenuItemGL::onMouseLeave(S32 x, S32 y, MASK mask) -{ - setHover(false); - LLUICtrl::onMouseLeave(x,y,mask); -} - -//virtual -bool LLMenuItemGL::handleRightMouseUp(S32 x, S32 y, MASK mask) -{ - // If this event came from a right-click context menu spawn, - // process as a left-click to allow menu items to be hit - if (LLMenuHolderGL::sContextMenuSpawnPos.mX != S32_MAX - || LLMenuHolderGL::sContextMenuSpawnPos.mY != S32_MAX) - { - bool handled = handleMouseUp(x, y, mask); - return handled; - } - return LLUICtrl::handleRightMouseUp(x,y,mask); -} - -// This function checks to see if the accelerator key is already in use; -// if not, it will be added to the list -bool LLMenuItemGL::addToAcceleratorList(std::list <LLMenuKeyboardBinding*> *listp) -{ - LLMenuKeyboardBinding *accelerator = NULL; - - if (mAcceleratorKey != KEY_NONE) - { - std::list<LLMenuKeyboardBinding*>::iterator list_it; - for (list_it = listp->begin(); list_it != listp->end(); ++list_it) - { - accelerator = *list_it; - if ((accelerator->mKey == mAcceleratorKey) && (accelerator->mMask == (mAcceleratorMask & MASK_NORMALKEYS))) - { - - // *NOTE: get calling code to throw up warning or route - // warning messages back to app-provided output - // std::string warning; - // warning.append("Duplicate key binding <"); - // appendAcceleratorString( warning ); - // warning.append("> for menu items:\n "); - // warning.append(accelerator->mName); - // warning.append("\n "); - // warning.append(mLabel); - - // LL_WARNS() << warning << LL_ENDL; - // LLAlertDialog::modalAlert(warning); - return false; - } - } - if (!accelerator) - { - accelerator = new LLMenuKeyboardBinding; - if (accelerator) - { - accelerator->mKey = mAcceleratorKey; - accelerator->mMask = (mAcceleratorMask & MASK_NORMALKEYS); -// accelerator->mName = mLabel; - } - listp->push_back(accelerator);//addData(accelerator); - } - } - return true; -} - -// This function appends the character string representation of -// the current accelerator key and mask to the provided string. -void LLMenuItemGL::appendAcceleratorString( std::string& st ) const -{ - st = LLKeyboard::stringFromAccelerator( mAcceleratorMask, mAcceleratorKey ); - LL_DEBUGS("HotKeys") << "appendAcceleratorString: " << st << LL_ENDL; -} - -void LLMenuItemGL::setJumpKey(KEY key) -{ - mJumpKey = LLStringOps::toUpper((char)key); -} - - -// virtual -U32 LLMenuItemGL::getNominalHeight( void ) const -{ - return mFont->getLineHeight() + MENU_ITEM_PADDING; -} - -//virtual -void LLMenuItemGL::setBriefItem(bool brief) -{ - mBriefItem = brief; -} - -//virtual -bool LLMenuItemGL::isBriefItem() const -{ - return mBriefItem; -} - -// Get the parent menu for this item -LLMenuGL* LLMenuItemGL::getMenu() const -{ - return (LLMenuGL*) getParent(); -} - - -// getNominalWidth() - returns the normal width of this control in -// pixels - this is used for calculating the widest item, as well as -// for horizontal arrangement. -U32 LLMenuItemGL::getNominalWidth( void ) const -{ - U32 width; - - if (mBriefItem) - { - width = BRIEF_PAD_PIXELS; - } - else - { - width = PLAIN_PAD_PIXELS; - } - - if( KEY_NONE != mAcceleratorKey ) - { - width += getMenu()->getShortcutPad(); - std::string temp; - appendAcceleratorString( temp ); - width += mFont->getWidth( temp ); - } - width += mFont->getWidth( mLabel.getWString().c_str() ); - return width; -} - -// called to rebuild the draw label -void LLMenuItemGL::buildDrawLabel( void ) -{ - mDrawAccelLabel.clear(); - std::string st = mDrawAccelLabel.getString(); - appendAcceleratorString( st ); - mDrawAccelLabel = st; -} - -void LLMenuItemGL::onCommit( void ) -{ - // Check torn-off status to allow left-arrow keyboard navigation back - // to parent menu. - // Also, don't hide if item triggered by keyboard shortcut (and hence - // parent not visible). - if (!getMenu()->getTornOff() - && getMenu()->getVisible()) - { - LLMenuGL::sMenuContainer->hideMenus(); - } - - LLUICtrl::onCommit(); -} - -// set the hover status (called by it's menu) - void LLMenuItemGL::setHighlight( bool highlight ) -{ - if (highlight) - { - getMenu()->clearHoverItem(); - } - - if (mHighlight != highlight) - { - dirtyRect(); - } - - mHighlight = highlight; -} - - -bool LLMenuItemGL::handleKeyHere( KEY key, MASK mask ) -{ - if (getHighlight() && - getMenu()->isOpen()) - { - if (key == KEY_UP) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(true); - - getMenu()->highlightPrevItem(this); - return true; - } - else if (key == KEY_DOWN) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(true); - - getMenu()->highlightNextItem(this); - return true; - } - else if (key == KEY_RETURN && mask == MASK_NONE) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(true); - - onCommit(); - return true; - } - } - - return false; -} - -bool LLMenuItemGL::handleMouseUp( S32 x, S32 y, MASK mask) -{ - // switch to mouse navigation mode - LLMenuGL::setKeyboardMode(false); - - onCommit(); - make_ui_sound("UISndClickRelease"); - return LLView::handleMouseUp(x, y, mask); -} - -bool LLMenuItemGL::handleMouseDown( S32 x, S32 y, MASK mask) -{ - // switch to mouse navigation mode - LLMenuGL::setKeyboardMode(false); - - setHighlight(true); - return LLView::handleMouseDown(x, y, mask); -} - -bool LLMenuItemGL::handleScrollWheel( S32 x, S32 y, S32 clicks ) -{ - // If the menu is scrollable let it handle the wheel event. - return !getMenu()->isScrollable(); -} - -void LLMenuItemGL::draw( void ) -{ - // *FIX: This can be optimized by using switches. Want to avoid - // that until the functionality is finalized. - - // HACK: Brief items don't highlight. Pie menu takes care of it. JC - // let disabled items be highlighted, just don't draw them as such - if( getEnabled() && getHighlight() && !mBriefItem) - { - gGL.color4fv( mHighlightBackground.get().mV ); - - gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 ); - } - - LLColor4 color; - - if ( getEnabled() && getHighlight() ) - { - color = mHighlightForeground.get(); - } - else if( getEnabled() && !mDrawTextDisabled ) - { - color = mEnabledColor.get(); - } - else - { - color = mDisabledColor.get(); - } - - // Highlight if needed - if( ll::ui::SearchableControl::getHighlighted() ) - color = ll::ui::SearchableControl::getHighlightColor(); - - // Draw the text on top. - if (mBriefItem) - { - mFont->render( mLabel, 0, BRIEF_PAD_PIXELS / 2, 0, color, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL); - } - else - { - if( !mDrawBoolLabel.empty() ) - { - mFont->render( mDrawBoolLabel.getWString(), 0, (F32)LEFT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false ); - } - mFont->render( mLabel.getWString(), 0, (F32)LEFT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false ); - if( !mDrawAccelLabel.empty() ) - { - mFont->render( mDrawAccelLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color, - LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false ); - } - if( !mDrawBranchLabel.empty() ) - { - mFont->render( mDrawBranchLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color, - LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false ); - } - } - - // underline "jump" key only when keyboard navigation has been initiated - if (getMenu()->jumpKeysActive() && LLMenuGL::getKeyboardMode()) - { - std::string upper_case_label = mLabel.getString(); - LLStringUtil::toUpper(upper_case_label); - std::string::size_type offset = upper_case_label.find(mJumpKey); - if (offset != std::string::npos) - { - S32 x_begin = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset); - S32 x_end = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset + 1); - gl_line_2d(x_begin, (MENU_ITEM_PADDING / 2) + 1, x_end, (MENU_ITEM_PADDING / 2) + 1); - } - } -} - -bool LLMenuItemGL::setLabelArg( const std::string& key, const LLStringExplicit& text ) -{ - mLabel.setArg(key, text); - return true; -} - -void LLMenuItemGL::onVisibilityChange(bool new_visibility) -{ - if (getMenu()) - { - getMenu()->needsArrange(); - } - LLView::onVisibilityChange(new_visibility); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLMenuItemSeparatorGL -// -// This class represents a separator. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LLMenuItemSeparatorGL::Params::Params() - : on_visible("on_visible") -{ -} - -LLMenuItemSeparatorGL::LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p) : - LLMenuItemGL( p ) -{ - if (p.on_visible.isProvided()) - { - mVisibleSignal.connect(initEnableCallback(p.on_visible)); - } -} - -//virtual -U32 LLMenuItemSeparatorGL::getNominalHeight( void ) const -{ - return SEPARATOR_HEIGHT_PIXELS; -} - -void LLMenuItemSeparatorGL::draw( void ) -{ - gGL.color4fv( mDisabledColor.get().mV ); - const S32 y = getRect().getHeight() / 2; - const S32 PAD = 6; - gl_line_2d( PAD, y, getRect().getWidth() - PAD, y ); -} - -void LLMenuItemSeparatorGL::buildDrawLabel( void ) -{ - if (mVisibleSignal.num_slots() > 0) - { - bool visible = mVisibleSignal(this, LLSD()); - setVisible(visible); - } -} - -bool LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask) -{ - LLMenuGL* parent_menu = getMenu(); - if (y > getRect().getHeight() / 2) - { - // the menu items are in the child list in bottom up order - LLView* prev_menu_item = parent_menu->findNextSibling(this); - return (prev_menu_item && prev_menu_item->getVisible() && prev_menu_item->getEnabled()) ? prev_menu_item->handleMouseDown(x, prev_menu_item->getRect().getHeight(), mask) : false; - } - else - { - LLView* next_menu_item = parent_menu->findPrevSibling(this); - return (next_menu_item && next_menu_item->getVisible() && next_menu_item->getEnabled()) ? next_menu_item->handleMouseDown(x, 0, mask) : false; - } -} - -bool LLMenuItemSeparatorGL::handleMouseUp(S32 x, S32 y, MASK mask) -{ - LLMenuGL* parent_menu = getMenu(); - if (y > getRect().getHeight() / 2) - { - LLView* prev_menu_item = parent_menu->findNextSibling(this); - return (prev_menu_item && prev_menu_item->getVisible() && prev_menu_item->getEnabled()) ? prev_menu_item->handleMouseUp(x, prev_menu_item->getRect().getHeight(), mask) : false; - } - else - { - LLView* next_menu_item = parent_menu->findPrevSibling(this); - return (next_menu_item && next_menu_item->getVisible() && next_menu_item->getEnabled()) ? next_menu_item->handleMouseUp(x, 0, mask) : false; - } -} - -bool LLMenuItemSeparatorGL::handleHover(S32 x, S32 y, MASK mask) -{ - LLMenuGL* parent_menu = getMenu(); - if (y > getRect().getHeight() / 2) - { - parent_menu->highlightPrevItem(this, false); - return false; - } - else - { - parent_menu->highlightNextItem(this, false); - return false; - } -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLMenuItemVerticalSeparatorGL -// -// This class represents a vertical separator. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLMenuItemVerticalSeparatorGL -: public LLMenuItemSeparatorGL -{ -public: - LLMenuItemVerticalSeparatorGL( void ); - - virtual bool handleMouseDown(S32 x, S32 y, MASK mask) { return false; } -}; - -LLMenuItemVerticalSeparatorGL::LLMenuItemVerticalSeparatorGL( void ) -{ - setLabel( VERTICAL_SEPARATOR_LABEL ); -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLMenuItemTearOffGL -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LLMenuItemTearOffGL::LLMenuItemTearOffGL(const LLMenuItemTearOffGL::Params& p) -: LLMenuItemGL(p) -{ -} - -// Returns the first floater ancestor if there is one -LLFloater* LLMenuItemTearOffGL::getParentFloater() -{ - LLView* parent_view = getMenu(); - - while (parent_view) - { - if (dynamic_cast<LLFloater*>(parent_view)) - { - return dynamic_cast<LLFloater*>(parent_view); - } - - bool parent_is_menu = dynamic_cast<LLMenuGL*>(parent_view) && !dynamic_cast<LLMenuBarGL*>(parent_view); - - if (parent_is_menu) - { - // use menu parent - parent_view = dynamic_cast<LLMenuGL*>(parent_view)->getParentMenuItem(); - } - else - { - // just use regular view parent - parent_view = parent_view->getParent(); - } - } - - return NULL; -} - -void LLMenuItemTearOffGL::onCommit() -{ - if (getMenu()->getTornOff()) - { - LLTearOffMenu * torn_off_menu = dynamic_cast<LLTearOffMenu*>(getMenu()->getParent()); - if (torn_off_menu) - { - torn_off_menu->closeFloater(); - } - } - else - { - // transfer keyboard focus and highlight to first real item in list - if (getHighlight()) - { - getMenu()->highlightNextItem(this); - } - - getMenu()->needsArrange(); - - LLFloater* parent_floater = getParentFloater(); - LLFloater* tear_off_menu = LLTearOffMenu::create(getMenu()); - - if (tear_off_menu) - { - if (parent_floater) - { - parent_floater->addDependentFloater(tear_off_menu, false); - } - - // give focus to torn off menu because it will have - // been taken away when parent menu closes - tear_off_menu->setFocus(true); - } - } - LLMenuItemGL::onCommit(); -} - -void LLMenuItemTearOffGL::draw() -{ - // disabled items can be highlighted, but shouldn't render as such - if( getEnabled() && getHighlight() && !isBriefItem()) - { - gGL.color4fv( mHighlightBackground.get().mV ); - gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 ); - } - - if (getEnabled()) - { - gGL.color4fv( mEnabledColor.get().mV ); - } - else - { - gGL.color4fv( mDisabledColor.get().mV ); - } - const S32 y = getRect().getHeight() / 3; - const S32 PAD = 6; - gl_line_2d( PAD, y, getRect().getWidth() - PAD, y ); - gl_line_2d( PAD, y * 2, getRect().getWidth() - PAD, y * 2 ); -} - -U32 LLMenuItemTearOffGL::getNominalHeight( void ) const -{ - return TEAROFF_SEPARATOR_HEIGHT_PIXELS; -} - -///============================================================================ -/// Class LLMenuItemCallGL -///============================================================================ - -LLMenuItemCallGL::LLMenuItemCallGL(const LLMenuItemCallGL::Params& p) -: LLMenuItemGL(p) -{ -} - -void LLMenuItemCallGL::initFromParams(const Params& p) -{ - if (p.on_visible.isProvided()) - { - mVisibleSignal.connect(initEnableCallback(p.on_visible)); - } - if (p.on_enable.isProvided()) - { - setEnableCallback(initEnableCallback(p.on_enable)); - // Set the enabled control variable (for backwards compatability) - if (p.on_enable.control_name.isProvided() && !p.on_enable.control_name().empty()) - { - LLControlVariable* control = findControl(p.on_enable.control_name()); - if (control) - { - setEnabledControlVariable(control); - } - else - { - LL_WARNS() << "Failed to assign 'enabled' control variable to menu " << getName() - << ": control " << p.on_enable.control_name() - << " does not exist." << LL_ENDL; - } - } - } - if (p.on_click.isProvided()) - { - setCommitCallback(initCommitCallback(p.on_click)); - } - - LLUICtrl::initFromParams(p); -} - -void LLMenuItemCallGL::onCommit( void ) -{ - // RN: menu item can be deleted in callback, so beware - getMenu()->setItemLastSelected( this ); - - LLMenuItemGL::onCommit(); -} - -void LLMenuItemCallGL::updateEnabled( void ) -{ - if (mEnableSignal.num_slots() > 0) - { - bool enabled = mEnableSignal(this, LLSD()); - if (mEnabledControlVariable) - { - if (!enabled) - { - // callback overrides control variable; this will call setEnabled() - mEnabledControlVariable->set(false); - } - } - else - { - setEnabled(enabled); - } - } -} - -void LLMenuItemCallGL::updateVisible( void ) -{ - if (mVisibleSignal.num_slots() > 0) - { - bool visible = mVisibleSignal(this, LLSD()); - setVisible(visible); - } -} - -void LLMenuItemCallGL::buildDrawLabel( void ) -{ - updateEnabled(); - updateVisible(); - LLMenuItemGL::buildDrawLabel(); -} - -bool LLMenuItemCallGL::handleKeyHere( KEY key, MASK mask ) -{ - return LLMenuItemGL::handleKeyHere(key, mask); -} - -bool LLMenuItemCallGL::handleAcceleratorKey( KEY key, MASK mask ) -{ - if( (!gKeyboard->getKeyRepeated(key) || getAllowKeyRepeat()) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) ) - { - updateEnabled(); - if (getEnabled()) - { - onCommit(); - return true; - } - } - return false; -} - -// handleRightMouseUp moved into base class LLMenuItemGL so clicks are -// handled for all menu item types - -///============================================================================ -/// Class LLMenuItemCheckGL -///============================================================================ -LLMenuItemCheckGL::LLMenuItemCheckGL (const LLMenuItemCheckGL::Params& p) -: LLMenuItemCallGL(p) -{ -} - -void LLMenuItemCheckGL::initFromParams(const Params& p) -{ - if (p.on_check.isProvided()) - { - setCheckCallback(initEnableCallback(p.on_check)); - // Set the control name (for backwards compatability) - if (p.on_check.control_name.isProvided() && !p.on_check.control_name().empty()) - { - setControlName(p.on_check.control_name()); - } - } - - LLMenuItemCallGL::initFromParams(p); -} - -void LLMenuItemCheckGL::onCommit( void ) -{ - LLMenuItemCallGL::onCommit(); -} - -//virtual -void LLMenuItemCheckGL::setValue(const LLSD& value) -{ - LLUICtrl::setValue(value); - if(value.asBoolean()) - { - mDrawBoolLabel = LLMenuGL::BOOLEAN_TRUE_PREFIX; - } - else - { - mDrawBoolLabel.clear(); - } -} - -//virtual -LLSD LLMenuItemCheckGL::getValue() const -{ - // Get our boolean value from the view model. - // If we don't override this method then the implementation from - // LLMenuItemGL will return a string. (EXT-8501) - return LLUICtrl::getValue(); -} - -// called to rebuild the draw label -void LLMenuItemCheckGL::buildDrawLabel( void ) -{ - // Note: mCheckSignal() returns true if no callbacks are set - bool checked = mCheckSignal(this, LLSD()); - if (mControlVariable) - { - if (!checked) - setControlValue(false); // callback overrides control variable; this will call setValue() - } - else - { - setValue(checked); - } - if(getValue().asBoolean()) - { - mDrawBoolLabel = LLMenuGL::BOOLEAN_TRUE_PREFIX; - } - else - { - mDrawBoolLabel.clear(); - } - LLMenuItemCallGL::buildDrawLabel(); -} - -///============================================================================ -/// Class LLMenuItemBranchGL -///============================================================================ -LLMenuItemBranchGL::LLMenuItemBranchGL(const LLMenuItemBranchGL::Params& p) - : LLMenuItemGL(p) -{ - LLMenuGL* branch = p.branch; - if (branch) - { - mBranchHandle = branch->getHandle(); - branch->setVisible(false); - branch->setParentMenuItem(this); - } -} - -LLMenuItemBranchGL::~LLMenuItemBranchGL() -{ - if (mBranchHandle.get()) - { - mBranchHandle.get()->die(); - } -} - - - -// virtual -LLView* LLMenuItemBranchGL::getChildView(const std::string& name, bool recurse) const -{ - LLMenuGL* branch = getBranch(); - if (branch) - { - if (branch->getName() == name) - { - return branch; - } - - // Always recurse on branches - return branch->getChildView(name, recurse); - } - - return LLView::getChildView(name, recurse); -} - -LLView* LLMenuItemBranchGL::findChildView(const std::string& name, bool recurse) const -{ - LLMenuGL* branch = getBranch(); - if (branch) - { - if (branch->getName() == name) - { - return branch; - } - - // Always recurse on branches - return branch->findChildView(name, recurse); - } - - return LLView::findChildView(name, recurse); -} - -// virtual -bool LLMenuItemBranchGL::handleMouseUp(S32 x, S32 y, MASK mask) -{ - // switch to mouse navigation mode - LLMenuGL::setKeyboardMode(false); - - onCommit(); - make_ui_sound("UISndClickRelease"); - return true; -} - -bool LLMenuItemBranchGL::hasAccelerator(const KEY &key, const MASK &mask) const -{ - return getBranch() && getBranch()->hasAccelerator(key, mask); -} - -bool LLMenuItemBranchGL::handleAcceleratorKey(KEY key, MASK mask) -{ - return getBranch() && getBranch()->handleAcceleratorKey(key, mask); -} - -// This function checks to see if the accelerator key is already in use; -// if not, it will be added to the list -bool LLMenuItemBranchGL::addToAcceleratorList(std::list<LLMenuKeyboardBinding*> *listp) -{ - LLMenuGL* branch = getBranch(); - if (!branch) - return false; - - U32 item_count = branch->getItemCount(); - LLMenuItemGL *item; - - while (item_count--) - { - if ((item = branch->getItem(item_count))) - { - return item->addToAcceleratorList(listp); - } - } - - return false; -} - - -// called to rebuild the draw label -void LLMenuItemBranchGL::buildDrawLabel( void ) -{ - mDrawAccelLabel.clear(); - std::string st = mDrawAccelLabel; - appendAcceleratorString( st ); - mDrawAccelLabel = st; - mDrawBranchLabel = LLMenuGL::BRANCH_SUFFIX; -} - -void LLMenuItemBranchGL::onCommit( void ) -{ - openMenu(); - - // keyboard navigation automatically propagates highlight to sub-menu - // to facilitate fast menu control via jump keys - if (LLMenuGL::getKeyboardMode() && getBranch() && !getBranch()->getHighlightedItem()) - { - getBranch()->highlightNextItem(NULL); - } - - LLUICtrl::onCommit(); -} - -bool LLMenuItemBranchGL::handleKey(KEY key, MASK mask, bool called_from_parent) -{ - bool handled = false; - if (getBranch() && called_from_parent) - { - handled = getBranch()->handleKey(key, mask, called_from_parent); - } - - if (!handled) - { - handled = LLMenuItemGL::handleKey(key, mask, called_from_parent); - } - - return handled; -} - -bool LLMenuItemBranchGL::handleUnicodeChar(llwchar uni_char, bool called_from_parent) -{ - bool handled = false; - if (getBranch() && called_from_parent) - { - handled = getBranch()->handleUnicodeChar(uni_char, true); - } - - if (!handled) - { - handled = LLMenuItemGL::handleUnicodeChar(uni_char, called_from_parent); - } - - return handled; -} - - -void LLMenuItemBranchGL::setHighlight( bool highlight ) -{ - if (highlight == getHighlight()) - return; - - LLMenuGL* branch = getBranch(); - if (!branch) - return; - - bool auto_open = getEnabled() && (!branch->getVisible() || branch->getTornOff()); - // torn off menus don't open sub menus on hover unless they have focus - LLFloater * menu_parent = dynamic_cast<LLFloater *>(getMenu()->getParent()); - if (getMenu()->getTornOff() && menu_parent && !menu_parent->hasFocus()) - { - auto_open = false; - } - // don't auto open torn off sub-menus (need to explicitly active menu item to give them focus) - if (branch->getTornOff()) - { - auto_open = false; - } - LLMenuItemGL::setHighlight(highlight); - if( highlight ) - { - if(auto_open) - { - openMenu(); - } - } - else - { - if (branch->getTornOff()) - { - LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent()); - if (branch_parent) - { - branch_parent->setFocus(false); - } - branch->clearHoverItem(); - } - else - { - branch->setVisible( false ); - } - } -} - -void LLMenuItemBranchGL::draw() -{ - LLMenuItemGL::draw(); - if (getBranch() && getBranch()->getVisible() && !getBranch()->getTornOff()) - { - setHighlight(true); - } -} - -void LLMenuItemBranchGL::updateBranchParent(LLView* parentp) -{ - if (getBranch() && getBranch()->getParent() == NULL) - { - // make the branch menu a sibling of my parent menu - getBranch()->updateParent(parentp); - } -} - -void LLMenuItemBranchGL::onVisibilityChange(bool new_visibility) -{ - if (!new_visibility && getBranch() && !getBranch()->getTornOff()) - { - getBranch()->setVisible(false); - } - LLMenuItemGL::onVisibilityChange(new_visibility); -} - -bool LLMenuItemBranchGL::handleKeyHere(KEY key, MASK mask) -{ - LLMenuGL* branch = getBranch(); - if (!branch) - return LLMenuItemGL::handleKeyHere(key, mask); - - // an item is highlighted, my menu is open, and I have an active sub menu or we are in - // keyboard navigation mode - if (getHighlight() - && getMenu()->isOpen() - && (isActive() || LLMenuGL::getKeyboardMode())) - { - if (branch->getVisible() && key == KEY_LEFT) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(true); - - bool handled = branch->clearHoverItem(); - if (branch->getTornOff()) - { - LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent()); - if (branch_parent) - { - branch_parent->setFocus(false); - } - } - if (handled && getMenu()->getTornOff()) - { - LLFloater * menu_parent = dynamic_cast<LLFloater *>(getMenu()->getParent()); - if (menu_parent) - { - menu_parent->setFocus(true); - } - } - return handled; - } - - if (key == KEY_RIGHT && !branch->getHighlightedItem()) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(true); - - LLMenuItemGL* itemp = branch->highlightNextItem(NULL); - if (itemp) - { - return true; - } - } - } - return LLMenuItemGL::handleKeyHere(key, mask); -} - -//virtual -bool LLMenuItemBranchGL::isActive() const -{ - return isOpen() && getBranch() && getBranch()->getHighlightedItem(); -} - -//virtual -bool LLMenuItemBranchGL::isOpen() const -{ - return getBranch() && getBranch()->isOpen(); -} - -void LLMenuItemBranchGL::openMenu() -{ - LLMenuGL* branch = getBranch(); - if (!branch) - return; - - if (branch->getTornOff()) - { - LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent()); - if (branch_parent) - { - gFloaterView->bringToFront(branch_parent); - // this might not be necessary, as torn off branches don't get focus and hence no highligth - branch->highlightNextItem(NULL); - } - } - else if( !branch->getVisible() ) - { - // get valid rectangle for menus - const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect(); - - branch->arrange(); - - LLRect branch_rect = branch->getRect(); - // calculate root-view relative position for branch menu - S32 left = getRect().mRight; - S32 top = getRect().mTop - getRect().mBottom; - - localPointToOtherView(left, top, &left, &top, branch->getParent()); - - branch_rect.setLeftTopAndSize( left, top, - branch_rect.getWidth(), branch_rect.getHeight() ); - - if (branch->getCanTearOff()) - { - branch_rect.translate(0, TEAROFF_SEPARATOR_HEIGHT_PIXELS); - } - branch->setRect( branch_rect ); - - // if branch extends outside of menu region change the direction it opens in - S32 x, y; - S32 delta_x = 0; - S32 delta_y = 0; - branch->localPointToOtherView( 0, 0, &x, &y, branch->getParent() ); - if( y < menu_region_rect.mBottom ) - { - // open upwards if menu extends past bottom - // adjust by the height of the menu item branch since it is a submenu - if (y + 2 * branch_rect.getHeight() - getRect().getHeight() > menu_region_rect.mTop) - { - // overlaps with top border, align with top - delta_y = menu_region_rect.mTop - y - branch_rect.getHeight(); - } - else - { - delta_y = branch_rect.getHeight() - getRect().getHeight(); - } - } - - if( x + branch_rect.getWidth() > menu_region_rect.mRight ) - { - // move sub-menu over to left side - delta_x = llmax(-x, ( -(branch_rect.getWidth() + getRect().getWidth()))); - } - branch->translate( delta_x, delta_y ); - - branch->setVisible( true ); - branch->getParent()->sendChildToFront(branch); - - dirtyRect(); - } -} - - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLMenuItemBranchDownGL -// -// The LLMenuItemBranchDownGL represents a menu item that has a -// sub-menu. This is used to make menu bar menus. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLMenuItemBranchDownGL : public LLMenuItemBranchGL -{ -protected: - -public: - LLMenuItemBranchDownGL( const Params& ); - - // returns the normal width of this control in pixels - this is - // used for calculating the widest item, as well as for horizontal - // arrangement. - virtual U32 getNominalWidth( void ) const; - - // called to rebuild the draw label - virtual void buildDrawLabel( void ); - - // handles opening, positioning, and arranging the menu branch associated with this item - virtual void openMenu( void ); - - // set the hover status (called by it's menu) and if the object is - // active. This is used for behavior transfer. - virtual void setHighlight( bool highlight ); - - virtual bool isActive( void ) const; - - // LLView functionality - virtual bool handleMouseDown( S32 x, S32 y, MASK mask ); - virtual bool handleMouseUp( S32 x, S32 y, MASK mask ); - virtual void draw( void ); - virtual bool handleKeyHere(KEY key, MASK mask); - - virtual bool handleAcceleratorKey(KEY key, MASK mask); - - virtual void onFocusLost(); - virtual void setFocus(bool b); -}; - -LLMenuItemBranchDownGL::LLMenuItemBranchDownGL( const Params& p) : - LLMenuItemBranchGL(p) -{ -} - -// returns the normal width of this control in pixels - this is used -// for calculating the widest item, as well as for horizontal -// arrangement. -U32 LLMenuItemBranchDownGL::getNominalWidth( void ) const -{ - U32 width = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS; - width += getFont()->getWidth( mLabel.getWString().c_str() ); - return width; -} - -// called to rebuild the draw label -void LLMenuItemBranchDownGL::buildDrawLabel( void ) -{ - mDrawAccelLabel.clear(); - std::string st = mDrawAccelLabel; - appendAcceleratorString( st ); - mDrawAccelLabel = st; -} - -void LLMenuItemBranchDownGL::openMenu( void ) -{ - LLMenuGL* branch = getBranch(); - if( branch->getVisible() && !branch->getTornOff() ) - { - branch->setVisible( false ); - } - else - { - if (branch->getTornOff()) - { - LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent()); - if (branch_parent) - { - gFloaterView->bringToFront(branch_parent); - } - } - else - { - // We're showing the drop-down menu, so patch up its labels/rects - branch->arrange(); - - LLRect rect = branch->getRect(); - S32 left = 0; - S32 top = getRect().mBottom; - localPointToOtherView(left, top, &left, &top, branch->getParent()); - - rect.setLeftTopAndSize( left, top, - rect.getWidth(), rect.getHeight() ); - branch->setRect( rect ); - S32 x = 0; - S32 y = 0; - branch->localPointToScreen( 0, 0, &x, &y ); - S32 delta_x = 0; - - LLCoordScreen window_size; - LLWindow* windowp = getWindow(); - windowp->getSize(&window_size); - - S32 window_width = window_size.mX; - if( x > window_width - rect.getWidth() ) - { - delta_x = (window_width - rect.getWidth()) - x; - } - branch->translate( delta_x, 0 ); - - setHighlight(true); - branch->setVisible( true ); - branch->getParent()->sendChildToFront(branch); - } - } -} - -// set the hover status (called by it's menu) -void LLMenuItemBranchDownGL::setHighlight( bool highlight ) -{ - if (highlight == getHighlight()) - return; - - //NOTE: Purposely calling all the way to the base to bypass auto-open. - LLMenuItemGL::setHighlight(highlight); - - LLMenuGL* branch = getBranch(); - if (!branch) - return; - - if( !highlight) - { - if (branch->getTornOff()) - { - LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent()); - if (branch_parent) - { - branch_parent->setFocus(false); - } - branch->clearHoverItem(); - } - else - { - branch->setVisible( false ); - } - } -} - -bool LLMenuItemBranchDownGL::isActive() const -{ - // for top level menus, being open is sufficient to be considered - // active, because clicking on them with the mouse will open - // them, without moving keyboard focus to them - return isOpen(); -} - -bool LLMenuItemBranchDownGL::handleMouseDown( S32 x, S32 y, MASK mask ) -{ - // switch to mouse control mode - LLMenuGL::setKeyboardMode(false); - - if (getVisible() && isOpen()) - { - LLMenuGL::sMenuContainer->hideMenus(); - } - else - { - onCommit(); - } - - make_ui_sound("UISndClick"); - return true; -} - -bool LLMenuItemBranchDownGL::handleMouseUp( S32 x, S32 y, MASK mask ) -{ - return true; -} - - -bool LLMenuItemBranchDownGL::handleAcceleratorKey(KEY key, MASK mask) -{ - bool branch_visible = getBranch()->getVisible(); - bool handled = getBranch()->handleAcceleratorKey(key, mask); - if (handled && !branch_visible && isInVisibleChain()) - { - // flash this menu entry because we triggered an invisible menu item - LLMenuHolderGL::setActivatedItem(this); - } - - return handled; -} -void LLMenuItemBranchDownGL::onFocusLost() -{ - // needed for tab-based selection - LLMenuItemBranchGL::onFocusLost(); - LLMenuGL::setKeyboardMode(false); - setHighlight(false); -} - -void LLMenuItemBranchDownGL::setFocus(bool b) -{ - // needed for tab-based selection - LLMenuItemBranchGL::setFocus(b); - LLMenuGL::setKeyboardMode(b); - setHighlight(b); -} - -bool LLMenuItemBranchDownGL::handleKeyHere(KEY key, MASK mask) -{ - bool menu_open = getBranch()->getVisible(); - // don't do keyboard navigation of top-level menus unless in keyboard mode, or menu expanded - if (getHighlight() && getMenu()->isOpen() && (isActive() || LLMenuGL::getKeyboardMode())) - { - if (key == KEY_LEFT) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(true); - - LLMenuItemGL* itemp = getMenu()->highlightPrevItem(this); - // open new menu only if previous menu was open - if (itemp && itemp->getEnabled() && menu_open) - { - itemp->onCommit(); - } - - return true; - } - else if (key == KEY_RIGHT) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(true); - - LLMenuItemGL* itemp = getMenu()->highlightNextItem(this); - // open new menu only if previous menu was open - if (itemp && itemp->getEnabled() && menu_open) - { - itemp->onCommit(); - } - - return true; - } - else if (key == KEY_DOWN) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(true); - - if (!isActive()) - { - onCommit(); - } - getBranch()->highlightNextItem(NULL); - return true; - } - else if (key == KEY_UP) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(true); - - if (!isActive()) - { - onCommit(); - } - getBranch()->highlightPrevItem(NULL); - return true; - } - } - - return false; -} - -void LLMenuItemBranchDownGL::draw( void ) -{ - //FIXME: try removing this - if (getBranch()->getVisible() && !getBranch()->getTornOff()) - { - setHighlight(true); - } - - if( getHighlight() ) - { - gGL.color4fv( mHighlightBackground.get().mV ); - gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 ); - } - - LLColor4 color; - if (getHighlight()) - { - color = mHighlightForeground.get(); - } - else if( getEnabled() ) - { - color = mEnabledColor.get(); - } - else - { - color = mDisabledColor.get(); - } - getFont()->render( mLabel.getWString(), 0, (F32)getRect().getWidth() / 2.f, (F32)LABEL_BOTTOM_PAD_PIXELS, color, - LLFontGL::HCENTER, LLFontGL::BOTTOM, LLFontGL::NORMAL); - - - // underline navigation key only when keyboard navigation has been initiated - if (getMenu()->jumpKeysActive() && LLMenuGL::getKeyboardMode()) - { - std::string upper_case_label = mLabel.getString(); - LLStringUtil::toUpper(upper_case_label); - std::string::size_type offset = upper_case_label.find(getJumpKey()); - if (offset != std::string::npos) - { - S32 x_offset = ll_round((F32)getRect().getWidth() / 2.f - getFont()->getWidthF32(mLabel.getString(), 0, S32_MAX) / 2.f); - S32 x_begin = x_offset + getFont()->getWidth(mLabel, 0, offset); - S32 x_end = x_offset + getFont()->getWidth(mLabel, 0, offset + 1); - gl_line_2d(x_begin, LABEL_BOTTOM_PAD_PIXELS, x_end, LABEL_BOTTOM_PAD_PIXELS); - } - } -} - - -class LLMenuScrollItem : public LLMenuItemCallGL -{ -public: - enum EArrowType - { - ARROW_DOWN, - ARROW_UP - }; - struct ArrowTypes : public LLInitParam::TypeValuesHelper<EArrowType, ArrowTypes> - { - static void declareValues() - { - declare("up", ARROW_UP); - declare("down", ARROW_DOWN); - } - }; - - struct Params : public LLInitParam::Block<Params, LLMenuItemCallGL::Params> - { - Optional<EArrowType, ArrowTypes> arrow_type; - Optional<CommitCallbackParam> scroll_callback; - }; - -protected: - LLMenuScrollItem(const Params&); - friend class LLUICtrlFactory; - -public: - /*virtual*/ void draw(); - /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent); - /*virtual*/ void setEnabled(bool enabled); - virtual void onCommit( void ); - -private: - LLButton* mArrowBtn; -}; - -LLMenuScrollItem::LLMenuScrollItem(const Params& p) -: LLMenuItemCallGL(p) -{ - std::string icon; - if (p.arrow_type.isProvided() && p.arrow_type == ARROW_UP) - { - icon = "arrow_up.tga"; - } - else - { - icon = "arrow_down.tga"; - } - - LLButton::Params bparams; - - // Disabled the Return key handling by LLMenuScrollItem instead of - // passing the key press to the currently selected menu item. See STORM-385. - bparams.commit_on_return(false); - bparams.mouse_opaque(true); - bparams.scale_image(false); - bparams.click_callback(p.scroll_callback); - bparams.mouse_held_callback(p.scroll_callback); - bparams.follows.flags(FOLLOWS_ALL); - std::string background = "transparent.j2c"; - bparams.image_unselected.name(background); - bparams.image_disabled.name(background); - bparams.image_selected.name(background); - bparams.image_hover_selected.name(background); - bparams.image_disabled_selected.name(background); - bparams.image_hover_unselected.name(background); - bparams.image_overlay.name(icon); - - mArrowBtn = LLUICtrlFactory::create<LLButton>(bparams); - addChild(mArrowBtn); -} - -/*virtual*/ -void LLMenuScrollItem::draw() -{ - LLUICtrl::draw(); -} - -/*virtual*/ -void LLMenuScrollItem::reshape(S32 width, S32 height, bool called_from_parent) -{ - mArrowBtn->reshape(width, height, called_from_parent); - LLView::reshape(width, height, called_from_parent); -} - -/*virtual*/ -void LLMenuScrollItem::setEnabled(bool enabled) -{ - mArrowBtn->setEnabled(enabled); - LLView::setEnabled(enabled); -} - -void LLMenuScrollItem::onCommit( void ) -{ - LLUICtrl::onCommit(); -} - -///============================================================================ -/// Class LLMenuGL -///============================================================================ - -LLMenuGL::LLMenuGL(const LLMenuGL::Params& p) -: LLUICtrl(p), - mBackgroundColor( p.bg_color() ), - mBgVisible( p.bg_visible ), - mDropShadowed( p.drop_shadow ), - mHasSelection(false), - mHorizontalLayout( p.horizontal_layout ), - mScrollable(mHorizontalLayout ? false : p.scrollable), // Scrolling is supported only for vertical layout - mMaxScrollableItems(p.max_scrollable_items), - mPreferredWidth(p.preferred_width), - mKeepFixedSize( p.keep_fixed_size ), - mLabel (p.label), - mLastMouseX(0), - mLastMouseY(0), - mMouseVelX(0), - mMouseVelY(0), - mTornOff(false), - mTearOffItem(NULL), - mSpilloverBranch(NULL), - mFirstVisibleItem(NULL), - mArrowUpItem(NULL), - mArrowDownItem(NULL), - mSpilloverMenu(NULL), - mJumpKey(p.jump_key), - mCreateJumpKeys(p.create_jump_keys), - mNeedsArrange(false), - mAlwaysShowMenu(false), - mResetScrollPositionOnShow(true), - mShortcutPad(p.shortcut_pad), - mFont(p.font) -{ - typedef boost::tokenizer<boost::char_separator<char> > tokenizer; - boost::char_separator<char> sep("_"); - tokenizer tokens(p.label(), sep); - tokenizer::iterator token_iter; - - S32 token_count = 0; - std::string new_menu_label; - for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) - { - new_menu_label += (*token_iter); - if (token_count > 0) - { - setJumpKey((*token_iter).c_str()[0]); - } - ++token_count; - } - setLabel(new_menu_label); - - mFadeTimer.stop(); -} - -void LLMenuGL::initFromParams(const LLMenuGL::Params& p) -{ - LLUICtrl::initFromParams(p); - setCanTearOff(p.can_tear_off); -} - -// Destroys the object -LLMenuGL::~LLMenuGL( void ) -{ - // delete the branch, as it might not be in view hierarchy - // leave the menu, because it is always in view hierarchy - delete mSpilloverBranch; - mJumpKeys.clear(); -} - -void LLMenuGL::setCanTearOff(bool tear_off) -{ - if (tear_off && mTearOffItem == NULL) - { - LLMenuItemTearOffGL::Params p; - mTearOffItem = LLUICtrlFactory::create<LLMenuItemTearOffGL>(p); - addChild(mTearOffItem); - } - else if (!tear_off && mTearOffItem != NULL) - { - mItems.remove(mTearOffItem); - removeChild(mTearOffItem); - delete mTearOffItem; - mTearOffItem = NULL; - needsArrange(); - } -} - -bool LLMenuGL::addChild(LLView* view, S32 tab_group) -{ - LLMenuGL* menup = dynamic_cast<LLMenuGL*>(view); - if (menup) - { - return appendMenu(menup); - } - - LLMenuItemGL* itemp = dynamic_cast<LLMenuItemGL*>(view); - if (itemp) - { - return append(itemp); - } - - return false; -} - -// Used in LLContextMenu and in LLTogleableMenu - -// Add an item to the context menu branch -bool LLMenuGL::addContextChild(LLView* view, S32 tab_group) -{ - LLContextMenu* context = dynamic_cast<LLContextMenu*>(view); - if (context) - { - return appendContextSubMenu(context); - } - - LLMenuItemSeparatorGL* separator = dynamic_cast<LLMenuItemSeparatorGL*>(view); - if (separator) - { - return append(separator); - } - - LLMenuItemGL* item = dynamic_cast<LLMenuItemGL*>(view); - if (item) - { - return append(item); - } - - LLMenuGL* menup = dynamic_cast<LLMenuGL*>(view); - if (menup) - { - return appendMenu(menup); - } - - return false; -} - - -void LLMenuGL::deleteAllChildren() -{ - mItems.clear(); - LLUICtrl::deleteAllChildren(); -} - -void LLMenuGL::removeChild( LLView* ctrl) -{ - // previously a dynamic_cast with if statement to check validity - // unfortunately removeChild is called by ~LLView, and at that point the - // object being deleted is no longer a LLMenuItemGL so a dynamic_cast will fail - LLMenuItemGL* itemp = static_cast<LLMenuItemGL*>(ctrl); - - item_list_t::iterator found_it = std::find(mItems.begin(), mItems.end(), (itemp)); - if (found_it != mItems.end()) - { - mItems.erase(found_it); - } - - return LLUICtrl::removeChild(ctrl); -} - -bool LLMenuGL::postBuild() -{ - createJumpKeys(); - return LLUICtrl::postBuild(); -} - -// are we the childmost active menu and hence our jump keys should be enabled? -// or are we a free-standing torn-off menu (which uses jump keys too) -bool LLMenuGL::jumpKeysActive() -{ - LLMenuItemGL* highlighted_item = getHighlightedItem(); - bool active = getVisible() && getEnabled(); - - if (active) - { - if (getTornOff()) - { - // activation of jump keys on torn off menus controlled by keyboard focus - LLFloater * parent = dynamic_cast<LLFloater *>(getParent()); - if (parent) - { - active = parent->hasFocus(); - } - } - else - { - // Are we the terminal active menu? - // Yes, if parent menu item deems us to be active (just being visible is sufficient for top-level menus) - // and we don't have a highlighted menu item pointing to an active sub-menu - active = (!getParentMenuItem() || getParentMenuItem()->isActive()) // I have a parent that is active... - && (!highlighted_item || !highlighted_item->isActive()); //... but no child that is active - } - } - - return active; -} - -bool LLMenuGL::isOpen() -{ - if (getTornOff()) - { - LLMenuItemGL* itemp = getHighlightedItem(); - // if we have an open sub-menu, then we are considered part of - // the open menu chain even if we don't have focus - if (itemp && itemp->isOpen()) - { - return true; - } - // otherwise we are only active if we have keyboard focus - LLFloater * parent = dynamic_cast<LLFloater *>(getParent()); - if (parent) - { - return parent->hasFocus(); - } - return false; - } - else - { - // normally, menus are hidden as soon as the user focuses - // on another menu, so just use the visibility criterion - return getVisible(); - } -} - - - -bool LLMenuGL::scrollItems(EScrollingDirection direction) -{ - // Slowing down items scrolling when arrow button is held - if (mScrollItemsTimer.hasExpired() && NULL != mFirstVisibleItem) - { - mScrollItemsTimer.setTimerExpirySec(.033f); - } - else - { - return false; - } - - switch (direction) - { - case SD_UP: - { - item_list_t::iterator cur_item_iter; - item_list_t::iterator prev_item_iter; - for (cur_item_iter = mItems.begin(), prev_item_iter = mItems.begin(); cur_item_iter != mItems.end(); cur_item_iter++) - { - if( (*cur_item_iter) == mFirstVisibleItem) - { - break; - } - if ((*cur_item_iter)->getVisible()) - { - prev_item_iter = cur_item_iter; - } - } - - if ((*prev_item_iter)->getVisible()) - { - mFirstVisibleItem = *prev_item_iter; - } - break; - } - case SD_DOWN: - { - if (NULL == mFirstVisibleItem) - { - mFirstVisibleItem = *mItems.begin(); - } - - item_list_t::iterator cur_item_iter; - - for (cur_item_iter = mItems.begin(); cur_item_iter != mItems.end(); cur_item_iter++) - { - if( (*cur_item_iter) == mFirstVisibleItem) - { - break; - } - } - - item_list_t::iterator next_item_iter; - - if (cur_item_iter != mItems.end()) - { - for (next_item_iter = ++cur_item_iter; next_item_iter != mItems.end(); next_item_iter++) - { - if( (*next_item_iter)->getVisible()) - { - break; - } - } - - if (next_item_iter != mItems.end() && - (*next_item_iter)->getVisible()) - { - mFirstVisibleItem = *next_item_iter; - } - } - break; - } - case SD_BEGIN: - { - mFirstVisibleItem = *mItems.begin(); - break; - } - case SD_END: - { - item_list_t::reverse_iterator first_visible_item_iter = mItems.rend(); - - // Need to scroll through number of actual existing items in menu. - // Otherwise viewer will hang for a time needed to scroll U32_MAX - // times in std::advance(). STORM-659. - size_t nitems = mItems.size(); - U32 scrollable_items = nitems < mMaxScrollableItems ? nitems : mMaxScrollableItems; - - // Advance by mMaxScrollableItems back from the end of the list - // to make the last item visible. - std::advance(first_visible_item_iter, scrollable_items); - mFirstVisibleItem = *first_visible_item_iter; - break; - } - default: - LL_WARNS() << "Unknown scrolling direction: " << direction << LL_ENDL; - } - - mNeedsArrange = true; - arrangeAndClear(); - - return true; -} - -// rearrange the child rects so they fit the shape of the menu. -void LLMenuGL::arrange( void ) -{ - // calculate the height & width, and set our rect based on that - // information. - const LLRect& initial_rect = getRect(); - - U32 width = 0, height = MENU_ITEM_PADDING; - - cleanupSpilloverBranch(); - - if( mItems.size() ) - { - const LLRect menu_region_rect = LLMenuGL::sMenuContainer ? LLMenuGL::sMenuContainer->getMenuRect() : LLRect(0, S32_MAX, S32_MAX, 0); - - // torn off menus are not constrained to the size of the screen - U32 max_width = getTornOff() ? U32_MAX : menu_region_rect.getWidth(); - U32 max_height = getTornOff() ? U32_MAX: menu_region_rect.getHeight(); - - // *FIX: create the item first and then ask for its dimensions? - S32 spillover_item_width = PLAIN_PAD_PIXELS + LLFontGL::getFontSansSerif()->getWidth( std::string("More") ); // *TODO: Translate - S32 spillover_item_height = LLFontGL::getFontSansSerif()->getLineHeight() + MENU_ITEM_PADDING; - - // Scrolling support - item_list_t::iterator first_visible_item_iter; - item_list_t::iterator first_hidden_item_iter = mItems.end(); - S32 height_before_first_visible_item = -1; - S32 visible_items_height = 0; - U32 scrollable_items_cnt = 0; - - if (mHorizontalLayout) - { - item_list_t::iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - // do first so LLMenuGLItemCall can call on_visible to determine if visible - (*item_iter)->buildDrawLabel(); - - if ((*item_iter)->getVisible()) - { - if (!getTornOff() - && *item_iter != mSpilloverBranch - && width + (*item_iter)->getNominalWidth() > max_width - spillover_item_width) - { - // no room for any more items - createSpilloverBranch(); - - std::vector<LLMenuItemGL*> items_to_remove; - std::copy(item_iter, mItems.end(), std::back_inserter(items_to_remove)); - std::vector<LLMenuItemGL*>::iterator spillover_iter; - for (spillover_iter= items_to_remove.begin(); spillover_iter != items_to_remove.end(); ++spillover_iter) - { - LLMenuItemGL* itemp = (*spillover_iter); - removeChild(itemp); - mSpilloverMenu->addChild(itemp); - } - - addChild(mSpilloverBranch); - - height = llmax(height, mSpilloverBranch->getNominalHeight()); - width += mSpilloverBranch->getNominalWidth(); - - break; - } - else - { - // track our rect - height = llmax(height, (*item_iter)->getNominalHeight()); - width += (*item_iter)->getNominalWidth(); - } - } - } - } - else - { - for (LLMenuItemGL* itemp : mItems) - { - // do first so LLMenuGLItemCall can call on_visible to determine if visible - itemp->buildDrawLabel(); - } - item_list_t::iterator item_iter; - - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - if ((*item_iter)->getVisible()) - { - if (!getTornOff() - && !mScrollable - && *item_iter != mSpilloverBranch - && height + (*item_iter)->getNominalHeight() > max_height - spillover_item_height) - { - // don't show only one item - int visible_items = 0; - item_list_t::iterator count_iter; - for (count_iter = item_iter; count_iter != mItems.end(); ++count_iter) - { - if((*count_iter)->getVisible()) - visible_items++; - } - if (visible_items>1) - { - // no room for any more items - createSpilloverBranch(); - - std::vector<LLMenuItemGL*> items_to_remove; - std::copy(item_iter, mItems.end(), std::back_inserter(items_to_remove)); - std::vector<LLMenuItemGL*>::iterator spillover_iter; - for (spillover_iter= items_to_remove.begin(); spillover_iter != items_to_remove.end(); ++spillover_iter) - { - LLMenuItemGL* itemp = (*spillover_iter); - removeChild(itemp); - mSpilloverMenu->addChild(itemp); - } - - - addChild(mSpilloverBranch); - - height += mSpilloverBranch->getNominalHeight(); - width = llmax( width, mSpilloverBranch->getNominalWidth() ); - - break; - } - } - - // track our rect - height += (*item_iter)->getNominalHeight(); - width = llmax( width, (*item_iter)->getNominalWidth() ); - - if (mScrollable) - { - // Determining visible items boundaries - if (NULL == mFirstVisibleItem) - { - mFirstVisibleItem = *item_iter; - } - - if (*item_iter == mFirstVisibleItem) - { - height_before_first_visible_item = height - (*item_iter)->getNominalHeight(); - first_visible_item_iter = item_iter; - scrollable_items_cnt = 0; - } - - if (-1 != height_before_first_visible_item && 0 == visible_items_height && - (++scrollable_items_cnt > mMaxScrollableItems || - height - height_before_first_visible_item > max_height - spillover_item_height * 2 )) - { - first_hidden_item_iter = item_iter; - visible_items_height = height - height_before_first_visible_item - (*item_iter)->getNominalHeight(); - scrollable_items_cnt--; - } - } - } - } - - if (mPreferredWidth < U32_MAX) - width = llmin(mPreferredWidth, max_width); - - if (mScrollable) - { - S32 max_items_height = max_height - spillover_item_height * 2; - - if (visible_items_height == 0) - visible_items_height = height - height_before_first_visible_item; - - // Fix mFirstVisibleItem value, if it doesn't allow to display all items, that can fit - if (visible_items_height < max_items_height && scrollable_items_cnt < mMaxScrollableItems) - { - item_list_t::iterator tmp_iter(first_visible_item_iter); - while (visible_items_height < max_items_height && - scrollable_items_cnt < mMaxScrollableItems && - first_visible_item_iter != mItems.begin()) - { - if ((*first_visible_item_iter)->getVisible()) - { - // It keeps visible item, after first_visible_item_iter - tmp_iter = first_visible_item_iter; - } - - first_visible_item_iter--; - - if ((*first_visible_item_iter)->getVisible()) - { - visible_items_height += (*first_visible_item_iter)->getNominalHeight(); - height_before_first_visible_item -= (*first_visible_item_iter)->getNominalHeight(); - scrollable_items_cnt++; - } - } - - // Roll back one item, that doesn't fit - if (visible_items_height > max_items_height) - { - visible_items_height -= (*first_visible_item_iter)->getNominalHeight(); - height_before_first_visible_item += (*first_visible_item_iter)->getNominalHeight(); - scrollable_items_cnt--; - first_visible_item_iter = tmp_iter; - } - if (!(*first_visible_item_iter)->getVisible()) - { - first_visible_item_iter = tmp_iter; - } - - mFirstVisibleItem = *first_visible_item_iter; - } - } - } - - S32 cur_height = (S32)llmin(max_height, height); - - if (mScrollable && - (height_before_first_visible_item > MENU_ITEM_PADDING || - height_before_first_visible_item + visible_items_height < (S32)height)) - { - // Reserving 2 extra slots for arrow items - cur_height = visible_items_height + spillover_item_height * 2; - } - - setRect(LLRect(getRect().mLeft, getRect().mTop, getRect().mLeft + width, getRect().mTop - cur_height)); - - S32 cur_width = 0; - S32 offset = 0; - if (mScrollable) - { - // No space for all items, creating arrow items - if (height_before_first_visible_item > MENU_ITEM_PADDING || - height_before_first_visible_item + visible_items_height < (S32)height) - { - if (NULL == mArrowUpItem) - { - LLMenuScrollItem::Params item_params; - item_params.name(ARROW_UP); - item_params.arrow_type(LLMenuScrollItem::ARROW_UP); - item_params.scroll_callback.function(boost::bind(&LLMenuGL::scrollItems, this, SD_UP)); - - mArrowUpItem = LLUICtrlFactory::create<LLMenuScrollItem>(item_params); - LLUICtrl::addChild(mArrowUpItem); - - } - if (NULL == mArrowDownItem) - { - LLMenuScrollItem::Params item_params; - item_params.name(ARROW_DOWN); - item_params.arrow_type(LLMenuScrollItem::ARROW_DOWN); - item_params.scroll_callback.function(boost::bind(&LLMenuGL::scrollItems, this, SD_DOWN)); - - mArrowDownItem = LLUICtrlFactory::create<LLMenuScrollItem>(item_params); - LLUICtrl::addChild(mArrowDownItem); - } - - LLRect rect; - mArrowUpItem->setRect(rect.setLeftTopAndSize( 0, cur_height, width, mArrowUpItem->getNominalHeight())); - mArrowUpItem->setVisible(true); - mArrowUpItem->setEnabled(height_before_first_visible_item > MENU_ITEM_PADDING); - mArrowUpItem->reshape(width, mArrowUpItem->getNominalHeight()); - mArrowDownItem->setRect(rect.setLeftTopAndSize( 0, mArrowDownItem->getNominalHeight(), width, mArrowDownItem->getNominalHeight())); - mArrowDownItem->setVisible(true); - mArrowDownItem->setEnabled(height_before_first_visible_item + visible_items_height < (S32)height); - mArrowDownItem->reshape(width, mArrowDownItem->getNominalHeight()); - - cur_height -= mArrowUpItem->getNominalHeight(); - - offset = menu_region_rect.mRight; // This moves items behind visible area - } - else - { - if (NULL != mArrowUpItem) - { - mArrowUpItem->setVisible(false); - } - if (NULL != mArrowDownItem) - { - mArrowDownItem->setVisible(false); - } - } - - } - - item_list_t::iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - if ((*item_iter)->getVisible()) - { - if (mScrollable) - { - if (item_iter == first_visible_item_iter) - { - offset = 0; - } - else if (item_iter == first_hidden_item_iter) - { - offset = menu_region_rect.mRight; // This moves items behind visible area - } - } - - // setup item rect to hold label - LLRect rect; - if (mHorizontalLayout) - { - rect.setLeftTopAndSize( cur_width, height, (*item_iter)->getNominalWidth(), height); - cur_width += (*item_iter)->getNominalWidth(); - } - else - { - rect.setLeftTopAndSize( 0 + offset, cur_height, width, (*item_iter)->getNominalHeight()); - if (offset == 0) - { - cur_height -= (*item_iter)->getNominalHeight(); - } - } - (*item_iter)->setRect( rect ); - } - } - - - if (getTornOff()) - { - LLTearOffMenu * torn_off_menu = dynamic_cast<LLTearOffMenu*>(getParent()); - if (torn_off_menu) - { - torn_off_menu->updateSize(); - } - } - } - if (mKeepFixedSize) - { - reshape(initial_rect.getWidth(), initial_rect.getHeight()); - } -} - -void LLMenuGL::arrangeAndClear( void ) -{ - if (mNeedsArrange) - { - arrange(); - mNeedsArrange = false; - } -} - -void LLMenuGL::createSpilloverBranch() -{ - if (!mSpilloverBranch) - { - // should be NULL but delete anyway - delete mSpilloverMenu; - // technically, you can't tear off spillover menus, but we're passing the handle - // along just to be safe - LLMenuGL::Params p; - std::string label = LLTrans::getString("More"); - p.name("More"); - p.label(label); - p.bg_color(mBackgroundColor); - p.bg_visible(true); - p.can_tear_off(false); - mSpilloverMenu = new LLMenuGL(p); - mSpilloverMenu->updateParent(LLMenuGL::sMenuContainer); - - LLMenuItemBranchGL::Params branch_params; - branch_params.name = "More"; - branch_params.label = label; - branch_params.branch = mSpilloverMenu; - branch_params.font.style = "italic"; - branch_params.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor"); - branch_params.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor"); - branch_params.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor"); - - mSpilloverBranch = LLUICtrlFactory::create<LLMenuItemBranchGL>(branch_params); - } -} - -void LLMenuGL::cleanupSpilloverBranch() -{ - if (mSpilloverBranch && mSpilloverBranch->getParent() == this) - { - // head-recursion to propagate items back up to root menu - mSpilloverMenu->cleanupSpilloverBranch(); - - // pop off spillover items - while (mSpilloverMenu->getItemCount()) - { - LLMenuItemGL* itemp = mSpilloverMenu->getItem(0); - mSpilloverMenu->removeChild(itemp); - // put them at the end of our own list - addChild(itemp); - } - - // Delete the branch, and since the branch will delete the menu, - // set the menu* to null. - delete mSpilloverBranch; - mSpilloverBranch = NULL; - mSpilloverMenu = NULL; - } -} - -void LLMenuGL::createJumpKeys() -{ - if (!mCreateJumpKeys) return; - mCreateJumpKeys = false; - - mJumpKeys.clear(); - - std::set<std::string> unique_words; - std::set<std::string> shared_words; - - item_list_t::iterator item_it; - typedef boost::tokenizer<boost::char_separator<char> > tokenizer; - boost::char_separator<char> sep(" "); - - for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it) - { - std::string uppercase_label = (*item_it)->getLabel(); - LLStringUtil::toUpper(uppercase_label); - - tokenizer tokens(uppercase_label, sep); - tokenizer::iterator token_iter; - for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) - { - if (unique_words.find(*token_iter) != unique_words.end()) - { - // this word exists in more than one menu instance - shared_words.insert(*token_iter); - } - else - { - // we have a new word, keep track of it - unique_words.insert(*token_iter); - } - } - } - - // pre-assign specified jump keys - for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it) - { - KEY jump_key = (*item_it)->getJumpKey(); - if(jump_key != KEY_NONE) - { - if (mJumpKeys.find(jump_key) == mJumpKeys.end()) - { - mJumpKeys.insert(std::pair<KEY, LLMenuItemGL*>(jump_key, (*item_it))); - } - else - { - // this key is already spoken for, - // so we need to reassign it below - (*item_it)->setJumpKey(KEY_NONE); - } - } - } - - for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it) - { - // skip over items that already have assigned jump keys - if ((*item_it)->getJumpKey() != KEY_NONE) - { - continue; - } - std::string uppercase_label = (*item_it)->getLabel(); - LLStringUtil::toUpper(uppercase_label); - - tokenizer tokens(uppercase_label, sep); - tokenizer::iterator token_iter; - - bool found_key = false; - for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) - { - std::string uppercase_word = *token_iter; - - // this word is not shared with other menu entries... - if (shared_words.find(*token_iter) == shared_words.end()) - { - S32 i; - for(i = 0; i < (S32)uppercase_word.size(); i++) - { - char jump_key = uppercase_word[i]; - - if (LLStringOps::isDigit(jump_key) || (LLStringOps::isUpper(jump_key) && - mJumpKeys.find(jump_key) == mJumpKeys.end())) - { - mJumpKeys.insert(std::pair<KEY, LLMenuItemGL*>(jump_key, (*item_it))); - (*item_it)->setJumpKey(jump_key); - found_key = true; - break; - } - } - } - if (found_key) - { - break; - } - } - } -} - -// remove all items on the menu -void LLMenuGL::empty( void ) -{ - cleanupSpilloverBranch(); - - mItems.clear(); - mFirstVisibleItem = NULL; - mArrowUpItem = NULL; - mArrowDownItem = NULL; - - deleteAllChildren(); -} - -// erase group of items from menu -void LLMenuGL::erase( S32 begin, S32 end, bool arrange/* = true*/) -{ - S32 items = mItems.size(); - - if ( items == 0 || begin >= end || begin < 0 || end > items ) - { - return; - } - - item_list_t::iterator start_position = mItems.begin(); - std::advance(start_position, begin); - - item_list_t::iterator end_position = mItems.begin(); - std::advance(end_position, end); - - for (item_list_t::iterator position_iter = start_position; position_iter != end_position; position_iter++) - { - LLUICtrl::removeChild(*position_iter); - } - - mItems.erase(start_position, end_position); - - if (arrange) - { - needsArrange(); - } -} - -// add new item at position -void LLMenuGL::insert( S32 position, LLView * ctrl, bool arrange /*= true*/ ) -{ - LLMenuItemGL * item = dynamic_cast<LLMenuItemGL *>(ctrl); - - if (NULL == item || position < 0 || position >= mItems.size()) - { - return; - } - - item_list_t::iterator position_iter = mItems.begin(); - std::advance(position_iter, position); - mItems.insert(position_iter, item); - LLUICtrl::addChild(item); - - if (arrange) - { - needsArrange(); - } -} - -// Adjust rectangle of the menu -void LLMenuGL::setLeftAndBottom(S32 left, S32 bottom) -{ - setRect(LLRect(left, getRect().mTop, getRect().mRight, bottom)); - needsArrange(); -} - -bool LLMenuGL::handleJumpKey(KEY key) -{ - // must perform case-insensitive comparison, so just switch to uppercase input key - key = toupper(key); - navigation_key_map_t::iterator found_it = mJumpKeys.find(key); - if(found_it != mJumpKeys.end() && found_it->second->getEnabled()) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(true); - - // force highlight to close old menus and open and sub-menus - found_it->second->setHighlight(true); - found_it->second->onCommit(); - - } - // if we are navigating the menus, we need to eat the keystroke - // so rest of UI doesn't handle it - return true; -} - - -// Add the menu item to this menu. -bool LLMenuGL::append( LLMenuItemGL* item ) -{ - if (!item) return false; - mItems.push_back( item ); - LLUICtrl::addChild(item); - needsArrange(); - return true; -} - -// add a separator to this menu -bool LLMenuGL::addSeparator() -{ - LLMenuItemSeparatorGL::Params p; - LLMenuItemGL* separator = LLUICtrlFactory::create<LLMenuItemSeparatorGL>(p); - return addChild(separator); -} - -// add a menu - this will create a cascading menu -bool LLMenuGL::appendMenu( LLMenuGL* menu ) -{ - if( menu == this ) - { - LL_ERRS() << "** Attempt to attach menu to itself. This is certainly " - << "a logic error." << LL_ENDL; - } - bool success = true; - - LLMenuItemBranchGL::Params p; - p.name = menu->getName(); - p.label = menu->getLabel(); - p.branch = menu; - p.jump_key = menu->getJumpKey(); - p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor"); - p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor"); - p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor"); - p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor"); - - LLMenuItemBranchGL* branch = LLUICtrlFactory::create<LLMenuItemBranchGL>(p); - success &= append( branch ); - - // Inherit colors - menu->setBackgroundColor( mBackgroundColor ); - menu->updateParent(LLMenuGL::sMenuContainer); - return success; -} - -// add a context menu branch -bool LLMenuGL::appendContextSubMenu(LLMenuGL *menu) -{ - if (menu == this) - { - LL_ERRS() << "Can't attach a context menu to itself" << LL_ENDL; - } - - LLContextMenuBranch *item; - LLContextMenuBranch::Params p; - p.name = menu->getName(); - p.label = menu->getLabel(); - p.branch = (LLContextMenu *)menu; - p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor"); - p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor"); - p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor"); - p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor"); - - item = LLUICtrlFactory::create<LLContextMenuBranch>(p); - LLMenuGL::sMenuContainer->addChild(item->getBranch()); - - return append( item ); -} - -void LLMenuGL::setEnabledSubMenus(bool enable) -{ - setEnabled(enable); - item_list_t::iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - (*item_iter)->setEnabledSubMenus( enable ); - } -} - -// setItemEnabled() - pass the label and the enable flag for a menu -// item. true will make sure it's enabled, false will disable it. -void LLMenuGL::setItemEnabled( const std::string& name, bool enable ) -{ - item_list_t::iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - if( (*item_iter)->getName() == name ) - { - (*item_iter)->setEnabled( enable ); - (*item_iter)->setEnabledSubMenus( enable ); - break; - } - } -} - -void LLMenuGL::setItemVisible( const std::string& name, bool visible ) -{ - item_list_t::iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - if( (*item_iter)->getName() == name ) - { - (*item_iter)->setVisible( visible ); - needsArrange(); - break; - } - } -} - - -void LLMenuGL::setItemLabel(const std::string &name, const std::string &label) -{ - LLMenuItemGL *item = getItem(name); - - if (item) - item->setLabel(label); -} - -void LLMenuGL::setItemLastSelected(LLMenuItemGL* item) -{ - if (getVisible()) - { - LLMenuHolderGL::setActivatedItem(item); - } - - // update enabled and checkmark status - item->buildDrawLabel(); -} - -// Set whether drop shadowed -void LLMenuGL::setDropShadowed( const bool shadowed ) -{ - mDropShadowed = shadowed; -} - -void LLMenuGL::setTornOff(bool torn_off) -{ - mTornOff = torn_off; -} - -U32 LLMenuGL::getItemCount() -{ - return mItems.size(); -} - -LLMenuItemGL* LLMenuGL::getItem(S32 number) -{ - if (number >= 0 && number < (S32)mItems.size()) - { - item_list_t::iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - if (number == 0) - { - return (*item_iter); - } - number--; - } - } - return NULL; -} - -LLMenuItemGL* LLMenuGL::getItem(std::string name) -{ - item_list_t::iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - if ((*item_iter)->getName() == name) - { - return (*item_iter); - } - } - return NULL; -} - -LLMenuItemGL* LLMenuGL::getHighlightedItem() -{ - item_list_t::iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - if ((*item_iter)->getHighlight()) - { - return (*item_iter); - } - } - return NULL; -} - -LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, bool skip_disabled) -{ - if (mItems.empty()) return NULL; - // highlighting first item on a torn off menu is the - // same as giving focus to it - if (!cur_item && getTornOff()) - { - LLFloater * parent = dynamic_cast<LLFloater *>(getParent()); - if (parent) - { - parent->setFocus(true); - } - } - - // Current item position in the items list - item_list_t::iterator cur_item_iter = std::find(mItems.begin(), mItems.end(), cur_item); - - item_list_t::iterator next_item_iter; - if (cur_item_iter == mItems.end()) - { - next_item_iter = mItems.begin(); - } - else - { - next_item_iter = cur_item_iter; - next_item_iter++; - - // First visible item position in the items list - item_list_t::iterator first_visible_item_iter = std::find(mItems.begin(), mItems.end(), mFirstVisibleItem); - - if (next_item_iter == mItems.end()) - { - next_item_iter = mItems.begin(); - - // If current item is the last in the list, the menu is scrolled to the beginning - // and the first item is highlighted. - if (mScrollable && !scrollItems(SD_BEGIN)) - { - return NULL; - } - } - // If current item is the last visible, the menu is scrolled one item down - // and the next item is highlighted. - else if (mScrollable && - (U32)std::abs(std::distance(first_visible_item_iter, next_item_iter)) >= mMaxScrollableItems) - { - // Call highlightNextItem() recursively only if the menu was successfully scrolled down. - // If scroll timer hasn't expired yet the menu won't be scrolled and calling - // highlightNextItem() will result in an endless recursion. - if (scrollItems(SD_DOWN)) - { - return highlightNextItem(cur_item, skip_disabled); - } - else - { - return NULL; - } - } - } - - // when first highlighting a menu, skip over tear off menu item - if (mTearOffItem && !cur_item) - { - // we know the first item is the tear off menu item - cur_item_iter = mItems.begin(); - next_item_iter++; - if (next_item_iter == mItems.end()) - { - next_item_iter = mItems.begin(); - } - } - - while(1) - { - // skip separators and disabled/invisible items - if ((*next_item_iter)->getEnabled() && (*next_item_iter)->getVisible() && !dynamic_cast<LLMenuItemSeparatorGL*>(*next_item_iter)) - { - if (cur_item) - { - cur_item->setHighlight(false); - } - (*next_item_iter)->setHighlight(true); - return (*next_item_iter); - } - - - if (!skip_disabled || next_item_iter == cur_item_iter) - { - break; - } - - next_item_iter++; - if (next_item_iter == mItems.end()) - { - if (cur_item_iter == mItems.end()) - { - break; - } - next_item_iter = mItems.begin(); - } - } - - return NULL; -} - -LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, bool skip_disabled) -{ - if (mItems.empty()) return NULL; - - // highlighting first item on a torn off menu is the - // same as giving focus to it - if (!cur_item && getTornOff()) - { - LLFloater * parent = dynamic_cast<LLFloater *>(getParent()); - if (parent) - { - parent->setFocus(true); - } - } - - // Current item reverse position from the end of the list - item_list_t::reverse_iterator cur_item_iter = std::find(mItems.rbegin(), mItems.rend(), cur_item); - - item_list_t::reverse_iterator prev_item_iter; - if (cur_item_iter == mItems.rend()) - { - prev_item_iter = mItems.rbegin(); - } - else - { - prev_item_iter = cur_item_iter; - prev_item_iter++; - - // First visible item reverse position in the items list - item_list_t::reverse_iterator first_visible_item_iter = std::find(mItems.rbegin(), mItems.rend(), mFirstVisibleItem); - - if (prev_item_iter == mItems.rend()) - { - prev_item_iter = mItems.rbegin(); - - // If current item is the first in the list, the menu is scrolled to the end - // and the last item is highlighted. - if (mScrollable && !scrollItems(SD_END)) - { - return NULL; - } - } - // If current item is the first visible, the menu is scrolled one item up - // and the previous item is highlighted. - else if (mScrollable && - std::distance(first_visible_item_iter, cur_item_iter) <= 0) - { - // Call highlightNextItem() only if the menu was successfully scrolled up. - // If scroll timer hasn't expired yet the menu won't be scrolled and calling - // highlightNextItem() will result in an endless recursion. - if (scrollItems(SD_UP)) - { - return highlightPrevItem(cur_item, skip_disabled); - } - else - { - return NULL; - } - } - } - - while(1) - { - // skip separators and disabled/invisible items - if ((*prev_item_iter)->getEnabled() && (*prev_item_iter)->getVisible() && (*prev_item_iter)->getName() != SEPARATOR_NAME) - { - (*prev_item_iter)->setHighlight(true); - return (*prev_item_iter); - } - - if (!skip_disabled || prev_item_iter == cur_item_iter) - { - break; - } - - prev_item_iter++; - if (prev_item_iter == mItems.rend()) - { - if (cur_item_iter == mItems.rend()) - { - break; - } - - prev_item_iter = mItems.rbegin(); - } - } - - return NULL; -} - -void LLMenuGL::buildDrawLabels() -{ - item_list_t::iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - (*item_iter)->buildDrawLabel(); - } -} - -void LLMenuGL::updateParent(LLView* parentp) -{ - if (getParent()) - { - getParent()->removeChild(this); - } - if (parentp) - { - parentp->addChild(this); - } - item_list_t::iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - (*item_iter)->updateBranchParent(parentp); - } -} - -bool LLMenuGL::hasAccelerator(const KEY &key, const MASK &mask) const -{ - if (key == KEY_NONE) - { - return false; - } - // Note: checking this way because mAccelerators seems to be broken - // mAccelerators probably needs to be cleaned up or fixed - // It was used for dupplicate accelerator avoidance. - item_list_t::const_iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - LLMenuItemGL* itemp = *item_iter; - if (itemp->hasAccelerator(key, mask)) - { - return true; - } - } - return false; -} - -bool LLMenuGL::handleAcceleratorKey(KEY key, MASK mask) -{ - // don't handle if not enabled - if(!getEnabled()) - { - return false; - } - - // Pass down even if not visible - item_list_t::iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - LLMenuItemGL* itemp = *item_iter; - if (itemp->handleAcceleratorKey(key, mask)) - { - return true; - } - } - - return false; -} - -bool LLMenuGL::handleUnicodeCharHere( llwchar uni_char ) -{ - if (jumpKeysActive()) - { - return handleJumpKey((KEY)uni_char); - } - return false; -} - -bool LLMenuGL::handleHover( S32 x, S32 y, MASK mask ) -{ - // leave submenu in place if slope of mouse < MAX_MOUSE_SLOPE_SUB_MENU - bool no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0; - S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX; - S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY; - LLVector2 mouse_dir((F32)mouse_delta_x, (F32)mouse_delta_y); - mouse_dir.normVec(); - LLVector2 mouse_avg_dir((F32)mMouseVelX, (F32)mMouseVelY); - mouse_avg_dir.normVec(); - F32 interp = 0.5f * (llclamp(mouse_dir * mouse_avg_dir, 0.f, 1.f)); - mMouseVelX = ll_round(lerp((F32)mouse_delta_x, (F32)mMouseVelX, interp)); - mMouseVelY = ll_round(lerp((F32)mouse_delta_y, (F32)mMouseVelY, interp)); - mLastMouseX = x; - mLastMouseY = y; - - // don't change menu focus unless mouse is moving or alt key is not held down - if ((llabs(mMouseVelX) > 0 || - llabs(mMouseVelY) > 0) && - (!mHasSelection || - //(mouse_delta_x == 0 && mouse_delta_y == 0) || - (mMouseVelX < 0) || - llabs((F32)mMouseVelY) / llabs((F32)mMouseVelX) > MAX_MOUSE_SLOPE_SUB_MENU)) - { - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - LLView* viewp = *child_it; - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - if (!viewp->pointInView(local_x, local_y) && ((LLMenuItemGL*)viewp)->getHighlight()) - { - // moving mouse always highlights new item - if (mouse_delta_x != 0 || mouse_delta_y != 0) - { - ((LLMenuItemGL*)viewp)->setHighlight(false); - } - } - } - - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - LLView* viewp = *child_it; - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - //RN: always call handleHover to track mGotHover status - // but only set highlight when mouse is moving - if( viewp->getVisible() && - //RN: allow disabled items to be highlighted to preserve "active" menus when - // moving mouse through them - //viewp->getEnabled() && - viewp->pointInView(local_x, local_y) && - viewp->handleHover(local_x, local_y, mask)) - { - // moving mouse always highlights new item - if (mouse_delta_x != 0 || mouse_delta_y != 0) - { - ((LLMenuItemGL*)viewp)->setHighlight(true); - LLMenuGL::setKeyboardMode(false); - } - mHasSelection = true; - } - } - } - getWindow()->setCursor(UI_CURSOR_ARROW); - - // *HACK Release the mouse capture - // This is done to release the mouse after the Navigation Bar "Back" or "Forward" button - // drop-down menu is shown. Otherwise any other view won't be able to handle mouse events - // until the user chooses one of the drop-down menu items. - - return true; -} - -bool LLMenuGL::handleScrollWheel( S32 x, S32 y, S32 clicks ) -{ - if (!mScrollable) - return blockMouseEvent(x, y); - - if( clicks > 0 ) - { - while( clicks-- ) - scrollItems(SD_DOWN); - } - else - { - while( clicks++ ) - scrollItems(SD_UP); - } - - return true; -} - - -void LLMenuGL::draw( void ) -{ - if (mNeedsArrange) - { - arrange(); - mNeedsArrange = false; - } - if (mDropShadowed && !mTornOff) - { - static LLUICachedControl<S32> drop_shadow_floater ("DropShadowFloater", 0); - static LLUIColor color_drop_shadow = LLUIColorTable::instance().getColor("ColorDropShadow"); - gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0, - color_drop_shadow, drop_shadow_floater ); - } - - if( mBgVisible ) - { - gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0, mBackgroundColor.get() ); - } - LLView::draw(); -} - -void LLMenuGL::drawBackground(LLMenuItemGL* itemp, F32 alpha) -{ - LLColor4 color = itemp->getHighlightBgColor() % alpha; - gGL.color4fv( color.mV ); - LLRect item_rect = itemp->getRect(); - gl_rect_2d( 0, item_rect.getHeight(), item_rect.getWidth(), 0); -} - -void LLMenuGL::setVisible(bool visible) -{ - if (visible != getVisible()) - { - if (!visible) - { - mFadeTimer.start(); - clearHoverItem(); - // reset last known mouse coordinates so - // we don't spoof a mouse move next time we're opened - mLastMouseX = 0; - mLastMouseY = 0; - } - else - { - mHasSelection = true; - mFadeTimer.stop(); - } - - LLView::setVisible(visible); - } -} - -LLMenuGL* LLMenuGL::findChildMenuByName(const std::string& name, bool recurse) const -{ - LLView* view = findChildView(name, recurse); - if (view) - { - LLMenuItemBranchGL* branch = dynamic_cast<LLMenuItemBranchGL*>(view); - if (branch) - { - return branch->getBranch(); - } - - LLMenuGL* menup = dynamic_cast<LLMenuGL*>(view); - if (menup) - { - return menup; - } - } - LL_WARNS() << "Child Menu " << name << " not found in menu " << getName() << LL_ENDL; - return NULL; -} - -bool LLMenuGL::clearHoverItem() -{ - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - LLMenuItemGL* itemp = (LLMenuItemGL*)*child_it; - if (itemp->getHighlight()) - { - itemp->setHighlight(false); - return true; - } - } - return false; -} - -void hide_top_view( LLView* view ) -{ - if( view ) view->setVisible( false ); -} - - -// x and y are the desired location for the popup, in the spawning_view's -// coordinate frame, NOT necessarily the mouse location -// static -void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y, S32 mouse_x, S32 mouse_y) -{ - const S32 CURSOR_HEIGHT = 22; // Approximate "normal" cursor size - const S32 CURSOR_WIDTH = 12; - - if (menu->getChildList()->empty()) - { - return; - } - - menu->setVisible( true ); - - if(!menu->getAlwaysShowMenu()) - { - //Do not show menu if all menu items are disabled - bool item_enabled = false; - for (LLView::child_list_t::const_iterator itor = menu->getChildList()->begin(); - itor != menu->getChildList()->end(); - ++itor) - { - LLView *menu_item = (*itor); - item_enabled = item_enabled || menu_item->getEnabled(); - } - - if(!item_enabled) - { - menu->setVisible( false ); - return; - } - } - - // Resetting scrolling position - if (menu->isScrollable() && menu->isScrollPositionOnShowReset()) - { - menu->mFirstVisibleItem = NULL; - } - - // Fix menu rect if needed. - menu->needsArrange(); - menu->arrangeAndClear(); - - if ((mouse_x == 0) || (mouse_y == 0)) - - { - // Save click point for detecting cursor moves before mouse-up. - // Must be in local coords to compare with mouseUp events. - // If the mouse doesn't move, the menu will stay open ala the Mac. - // See also LLContextMenu::show() - - LLUI::getInstance()->getMousePositionLocal(menu->getParent(), &mouse_x, &mouse_y); - } - - - LLMenuHolderGL::sContextMenuSpawnPos.set(mouse_x,mouse_y); - - const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getRect(); - - const S32 HPAD = 2; - LLRect rect = menu->getRect(); - S32 left = x + HPAD; - S32 top = y; - spawning_view->localPointToOtherView(left, top, &left, &top, menu->getParent()); - rect.setLeftTopAndSize( left, top, - rect.getWidth(), rect.getHeight() ); - menu->setRect( rect ); - - - // Adjust context menu to fit onscreen - LLRect mouse_rect; - const S32 MOUSE_CURSOR_PADDING = 5; - mouse_rect.setLeftTopAndSize(mouse_x - MOUSE_CURSOR_PADDING, - mouse_y + MOUSE_CURSOR_PADDING, - CURSOR_WIDTH + MOUSE_CURSOR_PADDING * 2, - CURSOR_HEIGHT + MOUSE_CURSOR_PADDING * 2); - menu->translateIntoRectWithExclusion( menu_region_rect, mouse_rect ); - if (menu->getRect().mTop > menu_region_rect.mTop) - { - // not enough space: align with top, ignore exclusion - menu->translateIntoRect( menu_region_rect ); - } - menu->getParent()->sendChildToFront(menu); -} - -///============================================================================ -/// Class LLMenuBarGL -///============================================================================ - -static LLDefaultChildRegistry::Register<LLMenuBarGL> r2("menu_bar"); - -LLMenuBarGL::LLMenuBarGL( const Params& p ) -: LLMenuGL(p), - mAltKeyTrigger(false) -{} - -// Default destructor -LLMenuBarGL::~LLMenuBarGL() -{ - std::for_each(mAccelerators.begin(), mAccelerators.end(), DeletePointer()); - mAccelerators.clear(); -} - -bool LLMenuBarGL::handleAcceleratorKey(KEY key, MASK mask) -{ - if (getHighlightedItem() && mask == MASK_NONE) - { - // unmodified key accelerators are ignored when navigating menu - // (but are used as jump keys so will still work when appropriate menu is up) - return false; - } - bool result = LLMenuGL::handleAcceleratorKey(key, mask); - if (result && mask & MASK_ALT) - { - // ALT key used to trigger hotkey, don't use as shortcut to open menu - mAltKeyTrigger = false; - } - - if(!result - && (key == KEY_F10 && mask == MASK_CONTROL) - && !gKeyboard->getKeyRepeated(key) - && isInVisibleChain()) - { - if (getHighlightedItem()) - { - clearHoverItem(); - LLMenuGL::setKeyboardMode(false); - } - else - { - // close menus originating from other menu bars when first opening menu via keyboard - LLMenuGL::sMenuContainer->hideMenus(); - highlightNextItem(NULL); - LLMenuGL::setKeyboardMode(true); - } - return true; - } - - if (result && !getHighlightedItem() && LLMenuGL::sMenuContainer->hasVisibleMenu()) - { - // close menus originating from other menu bars - LLMenuGL::sMenuContainer->hideMenus(); - } - - return result; -} - -bool LLMenuBarGL::handleKeyHere(KEY key, MASK mask) -{ - static LLUICachedControl<bool> use_altkey_for_menus ("UseAltKeyForMenus", 0); - if(key == KEY_ALT && !gKeyboard->getKeyRepeated(key) && use_altkey_for_menus) - { - mAltKeyTrigger = true; - } - else // if any key other than ALT hit, clear out waiting for Alt key mode - { - mAltKeyTrigger = false; - } - - if (key == KEY_ESCAPE && mask == MASK_NONE) - { - LLMenuGL::setKeyboardMode(false); - // if any menus are visible, this will return true, stopping further processing of ESCAPE key - return LLMenuGL::sMenuContainer->hideMenus(); - } - - // before processing any other key, check to see if ALT key has triggered menu access - checkMenuTrigger(); - - return LLMenuGL::handleKeyHere(key, mask); -} - -bool LLMenuBarGL::handleJumpKey(KEY key) -{ - // perform case-insensitive comparison - key = toupper(key); - navigation_key_map_t::iterator found_it = mJumpKeys.find(key); - if(found_it != mJumpKeys.end() && found_it->second->getEnabled()) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(true); - - found_it->second->setHighlight(true); - found_it->second->onCommit(); - } - return true; -} - -bool LLMenuBarGL::handleMouseDown(S32 x, S32 y, MASK mask) -{ - // clicks on menu bar closes existing menus from other contexts but leave - // own menu open so that we get toggle behavior - if (!getHighlightedItem() || !getHighlightedItem()->isActive()) - { - LLMenuGL::sMenuContainer->hideMenus(); - } - - return LLMenuGL::handleMouseDown(x, y, mask); -} - -bool LLMenuBarGL::handleDoubleClick(S32 x, S32 y, MASK mask) -{ - return LLMenuGL::handleMouseDown(x, y, mask); -} - -void LLMenuBarGL::draw() -{ - LLMenuItemGL* itemp = getHighlightedItem(); - // If we are in mouse-control mode and the mouse cursor is not hovering over - // the current highlighted menu item and it isn't open, then remove the - // highlight. This is done via a polling mechanism here, as we don't receive - // notifications when the mouse cursor moves off of us - if (itemp && !itemp->isOpen() && !itemp->getHover() && !LLMenuGL::getKeyboardMode()) - { - clearHoverItem(); - } - - checkMenuTrigger(); - - LLMenuGL::draw(); -} - - -void LLMenuBarGL::checkMenuTrigger() -{ - // has the ALT key been pressed and subsequently released? - if (mAltKeyTrigger && !gKeyboard->getKeyDown(KEY_ALT)) - { - // if alt key was released quickly, treat it as a menu access key - // otherwise it was probably an Alt-zoom or similar action - static LLUICachedControl<F32> menu_access_key_time ("MenuAccessKeyTime", 0); - if (gKeyboard->getKeyElapsedTime(KEY_ALT) <= menu_access_key_time || - gKeyboard->getKeyElapsedFrameCount(KEY_ALT) < 2) - { - if (getHighlightedItem()) - { - clearHoverItem(); - } - else - { - // close menus originating from other menu bars - LLMenuGL::sMenuContainer->hideMenus(); - - highlightNextItem(NULL); - LLMenuGL::setKeyboardMode(true); - } - } - mAltKeyTrigger = false; - } -} - -bool LLMenuBarGL::jumpKeysActive() -{ - // require user to be in keyboard navigation mode to activate key triggers - // as menu bars are always visible and it is easy to leave the mouse cursor over them - return LLMenuGL::getKeyboardMode() && getHighlightedItem() && LLMenuGL::jumpKeysActive(); -} - -// rearrange the child rects so they fit the shape of the menu bar. -void LLMenuBarGL::arrange( void ) -{ - U32 pos = 0; - LLRect rect( 0, getRect().getHeight(), 0, 0 ); - item_list_t::const_iterator item_iter; - for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { - LLMenuItemGL* item = *item_iter; - if (item->getVisible()) - { - rect.mLeft = pos; - pos += item->getNominalWidth(); - rect.mRight = pos; - item->setRect( rect ); - item->buildDrawLabel(); - } - } - reshape(rect.mRight, rect.getHeight()); -} - - -S32 LLMenuBarGL::getRightmostMenuEdge() -{ - // Find the last visible menu - item_list_t::reverse_iterator item_iter; - for (item_iter = mItems.rbegin(); item_iter != mItems.rend(); ++item_iter) - { - if ((*item_iter)->getVisible()) - { - break; - } - } - - if (item_iter == mItems.rend()) - { - return 0; - } - return (*item_iter)->getRect().mRight; -} - -// add a vertical separator to this menu -bool LLMenuBarGL::addSeparator() -{ - LLMenuItemGL* separator = new LLMenuItemVerticalSeparatorGL(); - return append( separator ); -} - -// add a menu - this will create a drop down menu. -bool LLMenuBarGL::appendMenu( LLMenuGL* menu ) -{ - if( menu == this ) - { - LL_ERRS() << "** Attempt to attach menu to itself. This is certainly " - << "a logic error." << LL_ENDL; - } - - bool success = true; - - // *TODO: Hack! Fix this - LLMenuItemBranchDownGL::Params p; - p.name = menu->getName(); - p.label = menu->getLabel(); - p.visible = menu->getVisible(); - p.branch = menu; - p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor"); - p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor"); - p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor"); - p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor"); - p.font = menu->getFont(); - - LLMenuItemBranchDownGL* branch = LLUICtrlFactory::create<LLMenuItemBranchDownGL>(p); - success &= branch->addToAcceleratorList(&mAccelerators); - success &= append( branch ); - branch->setJumpKey(branch->getJumpKey()); - menu->updateParent(LLMenuGL::sMenuContainer); - - return success; -} - -bool LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask ) -{ - bool handled = false; - LLView* active_menu = NULL; - - bool no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0; - S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX; - S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY; - mMouseVelX = (mMouseVelX / 2) + (mouse_delta_x / 2); - mMouseVelY = (mMouseVelY / 2) + (mouse_delta_y / 2); - mLastMouseX = x; - mLastMouseY = y; - - // if nothing currently selected or mouse has moved since last call, pick menu item via mouse - // otherwise let keyboard control it - if (!getHighlightedItem() || !LLMenuGL::getKeyboardMode() || llabs(mMouseVelX) > 0 || llabs(mMouseVelY) > 0) - { - // find current active menu - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - LLView* viewp = *child_it; - if (((LLMenuItemGL*)viewp)->isOpen()) - { - active_menu = viewp; - } - } - - // check for new active menu - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - LLView* viewp = *child_it; - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - if( viewp->getVisible() && - viewp->getEnabled() && - viewp->pointInView(local_x, local_y) && - viewp->handleHover(local_x, local_y, mask)) - { - ((LLMenuItemGL*)viewp)->setHighlight(true); - handled = true; - if (active_menu && active_menu != viewp) - { - ((LLMenuItemGL*)viewp)->onCommit(); - LLMenuGL::setKeyboardMode(false); - } - LLMenuGL::setKeyboardMode(false); - } - } - - if (handled) - { - // set hover false on inactive menus - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - LLView* viewp = *child_it; - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - if (!viewp->pointInView(local_x, local_y) && ((LLMenuItemGL*)viewp)->getHighlight()) - { - ((LLMenuItemGL*)viewp)->setHighlight(false); - } - } - } - } - - getWindow()->setCursor(UI_CURSOR_ARROW); - - return true; -} - -///============================================================================ -/// Class LLMenuHolderGL -///============================================================================ -LLCoordGL LLMenuHolderGL::sContextMenuSpawnPos(S32_MAX, S32_MAX); - -LLMenuHolderGL::LLMenuHolderGL(const LLMenuHolderGL::Params& p) - : LLPanel(p) -{ - sItemActivationTimer.stop(); - mCanHide = true; -} - -void LLMenuHolderGL::draw() -{ - LLView::draw(); - // now draw last selected item as overlay - LLMenuItemGL* selecteditem = (LLMenuItemGL*)sItemLastSelectedHandle.get(); - if (selecteditem && selecteditem->getVisible() && sItemActivationTimer.getStarted() && sItemActivationTimer.getElapsedTimeF32() < ACTIVATE_HIGHLIGHT_TIME) - { - // make sure toggle items, for example, show the proper state when fading out - selecteditem->buildDrawLabel(); - - LLRect item_rect; - selecteditem->localRectToOtherView(selecteditem->getLocalRect(), &item_rect, this); - - F32 interpolant = sItemActivationTimer.getElapsedTimeF32() / ACTIVATE_HIGHLIGHT_TIME; - - LLUI::pushMatrix(); - { - LLUI::translate((F32)item_rect.mLeft, (F32)item_rect.mBottom); - selecteditem->getMenu()->drawBackground(selecteditem, interpolant); - selecteditem->draw(); - } - LLUI::popMatrix(); - } -} - -bool LLMenuHolderGL::handleMouseDown( S32 x, S32 y, MASK mask ) -{ - bool handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL; - if (!handled) - { - LLMenuGL* visible_menu = (LLMenuGL*)getVisibleMenu(); - LLMenuItemGL* parent_menu = visible_menu ? visible_menu->getParentMenuItem() : NULL; - if (parent_menu && parent_menu->getVisible()) - { - // don't hide menu if parent was hit - LLRect parent_rect; - parent_menu->localRectToOtherView(parent_menu->getLocalRect(), &parent_rect, this); - if (!parent_rect.pointInRect(x, y)) - { - // clicked off of menu and parent, hide them all - hideMenus(); - } - } - else - { - // no visible parent, clicked off of menu, hide them all - hideMenus(); - } - } - return handled; -} - -bool LLMenuHolderGL::handleRightMouseDown( S32 x, S32 y, MASK mask ) -{ - bool handled = LLView::childrenHandleRightMouseDown(x, y, mask) != NULL; - if (!handled) - { - // clicked off of menu, hide them all - hideMenus(); - } - return handled; -} - -// This occurs when you mouse-down to spawn a context menu, hold the button -// down, move off the menu, then mouse-up. We want this to close the menu. -bool LLMenuHolderGL::handleRightMouseUp( S32 x, S32 y, MASK mask ) -{ - const S32 SLOP = 2; - S32 spawn_dx = (x - sContextMenuSpawnPos.mX); - S32 spawn_dy = (y - sContextMenuSpawnPos.mY); - if (-SLOP <= spawn_dx && spawn_dx <= SLOP - && -SLOP <= spawn_dy && spawn_dy <= SLOP) - { - // we're still inside the slop region from spawning this menu - // so interpret the mouse-up as a single-click to show and leave on - // screen - sContextMenuSpawnPos.set(S32_MAX, S32_MAX); - return true; - } - - bool handled = LLView::childrenHandleRightMouseUp(x, y, mask) != NULL; - if (!handled) - { - // clicked off of menu, hide them all - hideMenus(); - } - return handled; -} - -bool LLMenuHolderGL::handleKey(KEY key, MASK mask, bool called_from_parent) -{ - bool handled = false; - LLMenuGL* const pMenu = dynamic_cast<LLMenuGL*>(getVisibleMenu()); - - if (pMenu) - { - //eat TAB key - EXT-7000 - if (key == KEY_TAB && mask == MASK_NONE) - { - return true; - } - - //handle ESCAPE and RETURN key - handled = LLPanel::handleKey(key, mask, called_from_parent); - if (!handled) - { - if (pMenu->getHighlightedItem()) - { - handled = pMenu->handleKey(key, mask, true); - } - else if (mask == MASK_NONE || (key >= KEY_LEFT && key <= KEY_DOWN)) - { - //highlight first enabled one - if(pMenu->highlightNextItem(NULL)) - { - handled = true; - } - } - } - } - - return handled; - -} - -void LLMenuHolderGL::reshape(S32 width, S32 height, bool called_from_parent) -{ - if (width != getRect().getWidth() || height != getRect().getHeight()) - { - hideMenus(); - } - LLView::reshape(width, height, called_from_parent); -} - -LLView* const LLMenuHolderGL::getVisibleMenu() const -{ - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - LLView* viewp = *child_it; - if (viewp->getVisible() && dynamic_cast<LLMenuGL*>(viewp) != NULL) - { - return viewp; - } - } - return NULL; -} - - -bool LLMenuHolderGL::hideMenus() -{ - if (!mCanHide) - { - return false; - } - LLMenuGL::setKeyboardMode(false); - bool menu_visible = hasVisibleMenu(); - if (menu_visible) - { - // clicked off of menu, hide them all - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) - { - LLView* viewp = *child_it; - if (dynamic_cast<LLMenuGL*>(viewp) != NULL && viewp->getVisible()) - { - viewp->setVisible(false); - } - } - } - //if (gFocusMgr.childHasKeyboardFocus(this)) - //{ - // gFocusMgr.setKeyboardFocus(NULL); - //} - - return menu_visible; -} - -void LLMenuHolderGL::setActivatedItem(LLMenuItemGL* item) -{ - sItemLastSelectedHandle = item->getHandle(); - sItemActivationTimer.start(); -} - -///============================================================================ -/// Class LLTearOffMenu -///============================================================================ -LLTearOffMenu::LLTearOffMenu(LLMenuGL* menup) : - LLFloater(LLSD()), - mQuitRequested(false) -{ - S32 floater_header_size = getHeaderHeight(); - - setName(menup->getName()); - setTitle(menup->getLabel()); - setCanMinimize(false); - // flag menu as being torn off - menup->setTornOff(true); - // update menu layout as torn off menu (no spillover menus) - menup->needsArrange(); - - LLRect rect; - menup->localRectToOtherView(LLRect(-1, menup->getRect().getHeight(), menup->getRect().getWidth() + 3, 0), &rect, gFloaterView); - // make sure this floater is big enough for menu - mTargetHeight = rect.getHeight() + floater_header_size; - reshape(rect.getWidth(), rect.getHeight()); - setRect(rect); - - // attach menu to floater - menup->setFollows( FOLLOWS_LEFT | FOLLOWS_BOTTOM ); - mOldParent = menup->getParent(); - addChild(menup); - menup->setVisible(true); - LLRect menu_rect = menup->getRect(); - menu_rect.setOriginAndSize( 1, 1, - menu_rect.getWidth(), menu_rect.getHeight()); - menup->setRect(menu_rect); - menup->setDropShadowed(false); - - mMenu = menup; - - // highlight first item (tear off item will be disabled) - mMenu->highlightNextItem(NULL); - - // Can't do this in postBuild() because that is only called for floaters - // constructed from XML. - mCloseSignal.connect(boost::bind(&LLTearOffMenu::closeTearOff, this)); -} - -LLTearOffMenu::~LLTearOffMenu() -{ -} - -void LLTearOffMenu::draw() -{ - mMenu->setBackgroundVisible(isBackgroundOpaque()); - - if (getRect().getHeight() != mTargetHeight) - { - // animate towards target height - reshape(getRect().getWidth(), llceil(lerp((F32)getRect().getHeight(), (F32)mTargetHeight, LLSmoothInterpolation::getInterpolant(0.05f)))); - } - mMenu->needsArrange(); - LLFloater::draw(); -} - -void LLTearOffMenu::onFocusReceived() -{ - if (mQuitRequested) - { - return; - } - - // if nothing is highlighted, just highlight first item - if (!mMenu->getHighlightedItem()) - { - mMenu->highlightNextItem(NULL); - } - - // parent menu items get highlights so navigation logic keeps working - LLMenuItemGL* parent_menu_item = mMenu->getParentMenuItem(); - while(parent_menu_item) - { - if (parent_menu_item->getMenu()->getVisible()) - { - parent_menu_item->setHighlight(true); - parent_menu_item = parent_menu_item->getMenu()->getParentMenuItem(); - } - else - { - break; - } - } - LLFloater::onFocusReceived(); -} - -void LLTearOffMenu::onFocusLost() -{ - // remove highlight from parent item and our own menu - mMenu->clearHoverItem(); - LLFloater::onFocusLost(); -} - -bool LLTearOffMenu::handleUnicodeChar(llwchar uni_char, bool called_from_parent) -{ - // pass keystrokes down to menu - return mMenu->handleUnicodeChar(uni_char, true); -} - -bool LLTearOffMenu::handleKeyHere(KEY key, MASK mask) -{ - if (!mMenu->getHighlightedItem()) - { - if (key == KEY_UP) - { - mMenu->highlightPrevItem(NULL); - return true; - } - else if (key == KEY_DOWN) - { - mMenu->highlightNextItem(NULL); - return true; - } - } - // pass keystrokes down to menu - return mMenu->handleKey(key, mask, true); -} - -void LLTearOffMenu::translate(S32 x, S32 y) -{ - if (x != 0 && y != 0) - { - // hide open sub-menus by clearing current hover item - mMenu->clearHoverItem(); - } - LLFloater::translate(x, y); -} - -//static -LLTearOffMenu* LLTearOffMenu::create(LLMenuGL* menup) -{ - LLTearOffMenu* tearoffp = new LLTearOffMenu(menup); - // keep onscreen - gFloaterView->adjustToFitScreen(tearoffp, false); - tearoffp->openFloater(LLSD()); - - return tearoffp; -} - -void LLTearOffMenu::updateSize() -{ - if (mMenu) - { - S32 floater_header_size = getHeaderHeight(); - const LLRect &floater_rect = getRect(); - LLRect new_rect; - mMenu->localRectToOtherView(LLRect(-1, mMenu->getRect().getHeight() + floater_header_size, mMenu->getRect().getWidth() + 3, 0), &new_rect, gFloaterView); - - if (floater_rect.getWidth() != new_rect.getWidth() - || mTargetHeight != new_rect.getHeight()) - { - // make sure this floater is big enough for menu - mTargetHeight = new_rect.getHeight(); - reshape(new_rect.getWidth(), mTargetHeight); - - // Restore menu position - LLRect menu_rect = mMenu->getRect(); - menu_rect.setOriginAndSize(1, 1, - menu_rect.getWidth(), menu_rect.getHeight()); - mMenu->setRect(menu_rect); - } - } -} - -void LLTearOffMenu::closeTearOff() -{ - removeChild(mMenu); - mOldParent->addChild(mMenu); - mMenu->clearHoverItem(); - mMenu->setFollowsNone(); - mMenu->setBackgroundVisible(true); - mMenu->setVisible(false); - mMenu->setTornOff(false); - mMenu->setDropShadowed(true); - mQuitRequested = true; -} - -LLContextMenuBranch::LLContextMenuBranch(const LLContextMenuBranch::Params& p) -: LLMenuItemGL(p) -{ - LLContextMenu* branch = static_cast<LLContextMenu*>(p.branch); - if (branch) - { - mBranch = branch->getHandle(); - branch->hide(); - branch->setParentMenuItem(this); - } -} - -LLContextMenuBranch::~LLContextMenuBranch() -{ - if (mBranch.get()) - { - mBranch.get()->die(); - } -} - -// called to rebuild the draw label -void LLContextMenuBranch::buildDrawLabel( void ) -{ - auto menu = getBranch(); - if (menu) - { - // default enablement is this -- if any of the subitems are - // enabled, this item is enabled. JC - U32 sub_count = menu->getItemCount(); - U32 i; - bool any_enabled = false; - for (i = 0; i < sub_count; i++) - { - LLMenuItemGL* item = menu->getItem(i); - item->buildDrawLabel(); - if (item->getEnabled() && !item->getDrawTextDisabled() ) - { - any_enabled = true; - break; - } - } - setDrawTextDisabled(!any_enabled); - setEnabled(true); - } - - mDrawAccelLabel.clear(); - std::string st = mDrawAccelLabel; - appendAcceleratorString( st ); - mDrawAccelLabel = st; - - mDrawBranchLabel = LLMenuGL::BRANCH_SUFFIX; -} - -void LLContextMenuBranch::showSubMenu() -{ - auto menu = getBranch(); - if(menu) - { - LLMenuItemGL* menu_item = menu->getParentMenuItem(); - if (menu_item != NULL && menu_item->getVisible()) - { - S32 center_x; - S32 center_y; - localPointToScreen(getRect().getWidth(), getRect().getHeight(), ¢er_x, ¢er_y); - menu->show(center_x, center_y); - } - } -} - -// onCommit() - do the primary funcationality of the menu item. -void LLContextMenuBranch::onCommit( void ) -{ - showSubMenu(); - -} -void LLContextMenuBranch::setHighlight( bool highlight ) -{ - if (highlight == getHighlight()) return; - LLMenuItemGL::setHighlight(highlight); - auto menu = getBranch(); - if (menu) - { - if (highlight) - { - showSubMenu(); - } - else - { - menu->hide(); - } - } -} - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -//----------------------------------------------------------------------------- -// class LLContextMenu -// A context menu -//----------------------------------------------------------------------------- -static LLDefaultChildRegistry::Register<LLContextMenu> context_menu_register("context_menu"); -static MenuRegistry::Register<LLContextMenu> context_menu_register2("context_menu"); - - -LLContextMenu::LLContextMenu(const Params& p) -: LLMenuGL(p), - mHoveredAnyItem(false), - mHoverItem(NULL) -{ - //setBackgroundVisible(true); -} - -void LLContextMenu::setVisible(bool visible) -{ - if (!visible) - hide(); -} - -// Takes cursor position in screen space? -void LLContextMenu::show(S32 x, S32 y, LLView* spawning_view) -{ - if (getChildList()->empty()) - { - // nothing to show, so abort - return; - } - // Save click point for detecting cursor moves before mouse-up. - // Must be in local coords to compare with mouseUp events. - // If the mouse doesn't move, the menu will stay open ala the Mac. - // See also LLMenuGL::showPopup() - LLMenuHolderGL::sContextMenuSpawnPos.set(x,y); - - arrangeAndClear(); - - S32 width = getRect().getWidth(); - S32 height = getRect().getHeight(); - const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect(); - LLView* parent_view = getParent(); - - // Open upwards if menu extends past bottom - if (y - height < menu_region_rect.mBottom) - { - if (getParentMenuItem()) // Adjust if this is a submenu - { - y += height - getParentMenuItem()->getNominalHeight(); - } - else - { - y += height; - } - } - - // Open out to the left if menu extends past right edge - if (x + width > menu_region_rect.mRight) - { - if (getParentMenuItem()) - { - x -= getParentMenuItem()->getRect().getWidth() + width; - } - else - { - x -= width; - } - } - - S32 local_x, local_y; - parent_view->screenPointToLocal(x, y, &local_x, &local_y); - - LLRect rect; - rect.setLeftTopAndSize(local_x, local_y, width, height); - setRect(rect); - arrange(); - - if (spawning_view) - { - mSpawningViewHandle = spawning_view->getHandle(); - } - else - { - mSpawningViewHandle.markDead(); - } - LLView::setVisible(true); -} - -void LLContextMenu::hide() -{ - if (!getVisible()) return; - - LLView::setVisible(false); - - if (mHoverItem) - { - mHoverItem->setHighlight( false ); - } - mHoverItem = NULL; -} - - -bool LLContextMenu::handleHover( S32 x, S32 y, MASK mask ) -{ - LLMenuGL::handleHover(x,y,mask); - - bool handled = false; - - LLMenuItemGL *item = getHighlightedItem(); - - if (item && item->getEnabled()) - { - getWindow()->setCursor(UI_CURSOR_ARROW); - handled = true; - - if (item != mHoverItem) - { - if (mHoverItem) - { - mHoverItem->setHighlight( false ); - } - mHoverItem = item; - mHoverItem->setHighlight( true ); - } - mHoveredAnyItem = true; - } - else - { - // clear out our selection - if (mHoverItem) - { - mHoverItem->setHighlight(false); - mHoverItem = NULL; - } - } - - if( !handled && pointInView( x, y ) ) - { - getWindow()->setCursor(UI_CURSOR_ARROW); - handled = true; - } - - return handled; -} - -// handleMouseDown and handleMouseUp are handled by LLMenuGL - - -bool LLContextMenu::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - bool handled = false; - - // The click was somewhere within our rectangle - LLMenuItemGL *item = getHighlightedItem(); - - S32 local_x = x - getRect().mLeft; - S32 local_y = y - getRect().mBottom; - - bool clicked_in_menu = pointInView(local_x, local_y) ; - - // grab mouse if right clicking anywhere within pie (even deadzone in middle), to detect drag outside of pie - if (clicked_in_menu) - { - // capture mouse cursor as if on initial menu show - handled = true; - } - - if (item) - { - // lie to the item about where the click happened - // to make sure it's within the item's rectangle - if (item->handleMouseDown( 0, 0, mask )) - { - handled = true; - } - } - - return handled; -} - -bool LLContextMenu::handleRightMouseUp( S32 x, S32 y, MASK mask ) -{ - S32 local_x = x - getRect().mLeft; - S32 local_y = y - getRect().mBottom; - - if (!mHoveredAnyItem && !pointInView(local_x, local_y)) - { - sMenuContainer->hideMenus(); - return true; - } - - - bool result = handleMouseUp( x, y, mask ); - mHoveredAnyItem = false; - - return result; -} - -bool LLContextMenu::addChild(LLView* view, S32 tab_group) -{ - return addContextChild(view, tab_group); -} - +/**
+ * @file llmenugl.cpp
+ * @brief LLMenuItemGL base class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+//*****************************************************************************
+//
+// This file contains the opengl based menu implementation.
+//
+// NOTES: A menu label is split into 4 columns. The left column, the
+// label colum, the accelerator column, and the right column. The left
+// column is used for displaying boolean values for toggle and check
+// controls. The right column is used for submenus.
+//
+//*****************************************************************************
+
+//#include "llviewerprecompiledheaders.h"
+#include "linden_common.h"
+
+#include "llmenugl.h"
+
+#include "llgl.h"
+#include "llmath.h"
+#include "llrender.h"
+#include "llfocusmgr.h"
+#include "llcoord.h"
+#include "llwindow.h"
+#include "llcriticaldamp.h"
+#include "lluictrlfactory.h"
+
+#include "llbutton.h"
+#include "llfontgl.h"
+#include "llresmgr.h"
+#include "lltrans.h"
+#include "llui.h"
+
+#include "llstl.h"
+
+#include "v2math.h"
+#include <set>
+#include <boost/tokenizer.hpp>
+
+// static
+LLMenuHolderGL *LLMenuGL::sMenuContainer = NULL;
+view_listener_t::listener_map_t view_listener_t::sListeners;
+
+S32 MENU_BAR_HEIGHT = 18;
+S32 MENU_BAR_WIDTH = 410;
+
+///============================================================================
+/// Local function declarations, constants, enums, and typedefs
+///============================================================================
+
+const S32 LABEL_BOTTOM_PAD_PIXELS = 2;
+
+const U32 LEFT_PAD_PIXELS = 3;
+const U32 LEFT_WIDTH_PIXELS = 15;
+const U32 LEFT_PLAIN_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS;
+
+const U32 RIGHT_PAD_PIXELS = 7;
+const U32 RIGHT_WIDTH_PIXELS = 15;
+const U32 RIGHT_PLAIN_PIXELS = RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS;
+
+const U32 PLAIN_PAD_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS;
+
+const U32 BRIEF_PAD_PIXELS = 2;
+
+const U32 SEPARATOR_HEIGHT_PIXELS = 8;
+const S32 TEAROFF_SEPARATOR_HEIGHT_PIXELS = 10;
+const S32 MENU_ITEM_PADDING = 4;
+
+const std::string SEPARATOR_NAME("separator");
+const std::string VERTICAL_SEPARATOR_LABEL( "|" );
+
+const std::string LLMenuGL::BOOLEAN_TRUE_PREFIX( "\xE2\x9C\x94" ); // U+2714 HEAVY CHECK MARK
+const std::string LLMenuGL::BRANCH_SUFFIX( "\xe2\x96\xb8" ); // U+25B6 BLACK RIGHT-POINTING TRIANGLE
+const std::string LLMenuGL::ARROW_UP ("^^^^^^^");
+const std::string LLMenuGL::ARROW_DOWN("vvvvvvv");
+
+const F32 MAX_MOUSE_SLOPE_SUB_MENU = 0.9f;
+
+bool LLMenuGL::sKeyboardMode = false;
+
+LLHandle<LLView> LLMenuHolderGL::sItemLastSelectedHandle;
+LLFrameTimer LLMenuHolderGL::sItemActivationTimer;
+
+const F32 ACTIVATE_HIGHLIGHT_TIME = 0.3f;
+
+static MenuRegistry::Register<LLMenuItemGL> register_menu_item("menu_item");
+static MenuRegistry::Register<LLMenuItemSeparatorGL> register_separator("menu_item_separator");
+static MenuRegistry::Register<LLMenuItemCallGL> register_menu_item_call("menu_item_call");
+static MenuRegistry::Register<LLMenuItemCheckGL> register_menu_item_check("menu_item_check");
+// Created programmatically but we need to specify custom colors in xml
+static MenuRegistry::Register<LLMenuItemTearOffGL> register_menu_item_tear_off("menu_item_tear_off");
+static MenuRegistry::Register<LLMenuGL> register_menu("menu");
+
+static LLDefaultChildRegistry::Register<LLMenuGL> register_menu_default("menu");
+
+
+
+///============================================================================
+/// Class LLMenuItemGL
+///============================================================================
+
+LLMenuItemGL::Params::Params()
+: shortcut("shortcut"),
+ jump_key("jump_key", KEY_NONE),
+ use_mac_ctrl("use_mac_ctrl", false),
+ allow_key_repeat("allow_key_repeat", false),
+ rect("rect"),
+ left("left"),
+ top("top"),
+ right("right"),
+ bottom("bottom"),
+ width("width"),
+ height("height"),
+ bottom_delta("bottom_delta"),
+ left_delta("left_delta"),
+ enabled_color("enabled_color"),
+ disabled_color("disabled_color"),
+ highlight_bg_color("highlight_bg_color"),
+ highlight_fg_color("highlight_fg_color")
+{
+ changeDefault(mouse_opaque, true);
+}
+
+// Default constructor
+LLMenuItemGL::LLMenuItemGL(const LLMenuItemGL::Params& p)
+: LLUICtrl(p),
+ mJumpKey(p.jump_key),
+ mAllowKeyRepeat(p.allow_key_repeat),
+ mHighlight( false ),
+ mGotHover( false ),
+ mBriefItem( false ),
+ mDrawTextDisabled( false ),
+ mFont(p.font),
+ mAcceleratorKey(KEY_NONE),
+ mAcceleratorMask(MASK_NONE),
+ mLabel(p.label.isProvided() ? p.label() : p.name()),
+ mEnabledColor(p.enabled_color()),
+ mDisabledColor(p.disabled_color()),
+ mHighlightBackground(p.highlight_bg_color()),
+ mHighlightForeground(p.highlight_fg_color())
+{
+#ifdef LL_DARWIN
+ // See if this Mac accelerator should really use the ctrl key and not get mapped to cmd
+ bool useMacCtrl = p.use_mac_ctrl;
+#endif // LL_DARWIN
+
+ std::string shortcut = p.shortcut;
+ if (shortcut.find("control") != shortcut.npos)
+ {
+#ifdef LL_DARWIN
+ if ( useMacCtrl )
+ {
+ mAcceleratorMask |= MASK_MAC_CONTROL;
+ }
+#endif // LL_DARWIN
+ mAcceleratorMask |= MASK_CONTROL;
+ }
+ if (shortcut.find("alt") != shortcut.npos)
+ {
+ mAcceleratorMask |= MASK_ALT;
+ }
+ if (shortcut.find("shift") != shortcut.npos)
+ {
+ mAcceleratorMask |= MASK_SHIFT;
+ }
+ S32 pipe_pos = shortcut.rfind("|");
+ std::string key_str = shortcut.substr(pipe_pos+1);
+
+ LLKeyboard::keyFromString(key_str, &mAcceleratorKey);
+
+ LL_DEBUGS("HotKeys") << "Process short cut key: shortcut: " << shortcut
+ << ", key str: " << key_str
+ << ", accelerator mask: " << mAcceleratorMask
+ << ", accelerator key: " << mAcceleratorKey
+ << LL_ENDL;
+}
+
+//virtual
+void LLMenuItemGL::setValue(const LLSD& value)
+{
+ setLabel(value.asString());
+}
+
+//virtual
+LLSD LLMenuItemGL::getValue() const
+{
+ return getLabel();
+}
+
+//virtual
+bool LLMenuItemGL::hasAccelerator(const KEY &key, const MASK &mask) const
+{
+ return (mAcceleratorKey == key) && (mAcceleratorMask == mask);
+}
+
+//virtual
+bool LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask)
+{
+ if( getEnabled() && (!gKeyboard->getKeyRepeated(key) || mAllowKeyRepeat) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) )
+ {
+ onCommit();
+ return true;
+ }
+ return false;
+}
+
+bool LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask)
+{
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ return true;
+}
+
+//virtual
+bool LLMenuItemGL::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ return LLUICtrl::handleRightMouseDown(x,y,mask);
+}
+
+void LLMenuItemGL::onMouseEnter(S32 x, S32 y, MASK mask)
+{
+ setHover(true);
+ LLUICtrl::onMouseEnter(x,y,mask);
+}
+
+void LLMenuItemGL::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+ setHover(false);
+ LLUICtrl::onMouseLeave(x,y,mask);
+}
+
+//virtual
+bool LLMenuItemGL::handleRightMouseUp(S32 x, S32 y, MASK mask)
+{
+ // If this event came from a right-click context menu spawn,
+ // process as a left-click to allow menu items to be hit
+ if (LLMenuHolderGL::sContextMenuSpawnPos.mX != S32_MAX
+ || LLMenuHolderGL::sContextMenuSpawnPos.mY != S32_MAX)
+ {
+ bool handled = handleMouseUp(x, y, mask);
+ return handled;
+ }
+ return LLUICtrl::handleRightMouseUp(x,y,mask);
+}
+
+// This function checks to see if the accelerator key is already in use;
+// if not, it will be added to the list
+bool LLMenuItemGL::addToAcceleratorList(std::list <LLMenuKeyboardBinding*> *listp)
+{
+ LLMenuKeyboardBinding *accelerator = NULL;
+
+ if (mAcceleratorKey != KEY_NONE)
+ {
+ std::list<LLMenuKeyboardBinding*>::iterator list_it;
+ for (list_it = listp->begin(); list_it != listp->end(); ++list_it)
+ {
+ accelerator = *list_it;
+ if ((accelerator->mKey == mAcceleratorKey) && (accelerator->mMask == (mAcceleratorMask & MASK_NORMALKEYS)))
+ {
+
+ // *NOTE: get calling code to throw up warning or route
+ // warning messages back to app-provided output
+ // std::string warning;
+ // warning.append("Duplicate key binding <");
+ // appendAcceleratorString( warning );
+ // warning.append("> for menu items:\n ");
+ // warning.append(accelerator->mName);
+ // warning.append("\n ");
+ // warning.append(mLabel);
+
+ // LL_WARNS() << warning << LL_ENDL;
+ // LLAlertDialog::modalAlert(warning);
+ return false;
+ }
+ }
+ if (!accelerator)
+ {
+ accelerator = new LLMenuKeyboardBinding;
+ if (accelerator)
+ {
+ accelerator->mKey = mAcceleratorKey;
+ accelerator->mMask = (mAcceleratorMask & MASK_NORMALKEYS);
+// accelerator->mName = mLabel;
+ }
+ listp->push_back(accelerator);//addData(accelerator);
+ }
+ }
+ return true;
+}
+
+// This function appends the character string representation of
+// the current accelerator key and mask to the provided string.
+void LLMenuItemGL::appendAcceleratorString( std::string& st ) const
+{
+ st = LLKeyboard::stringFromAccelerator( mAcceleratorMask, mAcceleratorKey );
+ LL_DEBUGS("HotKeys") << "appendAcceleratorString: " << st << LL_ENDL;
+}
+
+void LLMenuItemGL::setJumpKey(KEY key)
+{
+ mJumpKey = LLStringOps::toUpper((char)key);
+}
+
+
+// virtual
+U32 LLMenuItemGL::getNominalHeight( void ) const
+{
+ return mFont->getLineHeight() + MENU_ITEM_PADDING;
+}
+
+//virtual
+void LLMenuItemGL::setBriefItem(bool brief)
+{
+ mBriefItem = brief;
+}
+
+//virtual
+bool LLMenuItemGL::isBriefItem() const
+{
+ return mBriefItem;
+}
+
+// Get the parent menu for this item
+LLMenuGL* LLMenuItemGL::getMenu() const
+{
+ return (LLMenuGL*) getParent();
+}
+
+
+// getNominalWidth() - returns the normal width of this control in
+// pixels - this is used for calculating the widest item, as well as
+// for horizontal arrangement.
+U32 LLMenuItemGL::getNominalWidth( void ) const
+{
+ U32 width;
+
+ if (mBriefItem)
+ {
+ width = BRIEF_PAD_PIXELS;
+ }
+ else
+ {
+ width = PLAIN_PAD_PIXELS;
+ }
+
+ if( KEY_NONE != mAcceleratorKey )
+ {
+ width += getMenu()->getShortcutPad();
+ std::string temp;
+ appendAcceleratorString( temp );
+ width += mFont->getWidth( temp );
+ }
+ width += mFont->getWidth( mLabel.getWString().c_str() );
+ return width;
+}
+
+// called to rebuild the draw label
+void LLMenuItemGL::buildDrawLabel( void )
+{
+ mDrawAccelLabel.clear();
+ std::string st = mDrawAccelLabel.getString();
+ appendAcceleratorString( st );
+ mDrawAccelLabel = st;
+}
+
+void LLMenuItemGL::onCommit( void )
+{
+ // Check torn-off status to allow left-arrow keyboard navigation back
+ // to parent menu.
+ // Also, don't hide if item triggered by keyboard shortcut (and hence
+ // parent not visible).
+ if (!getMenu()->getTornOff()
+ && getMenu()->getVisible())
+ {
+ LLMenuGL::sMenuContainer->hideMenus();
+ }
+
+ LLUICtrl::onCommit();
+}
+
+// set the hover status (called by it's menu)
+ void LLMenuItemGL::setHighlight( bool highlight )
+{
+ if (highlight)
+ {
+ getMenu()->clearHoverItem();
+ }
+
+ if (mHighlight != highlight)
+ {
+ dirtyRect();
+ }
+
+ mHighlight = highlight;
+}
+
+
+bool LLMenuItemGL::handleKeyHere( KEY key, MASK mask )
+{
+ if (getHighlight() &&
+ getMenu()->isOpen())
+ {
+ if (key == KEY_UP)
+ {
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(true);
+
+ getMenu()->highlightPrevItem(this);
+ return true;
+ }
+ else if (key == KEY_DOWN)
+ {
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(true);
+
+ getMenu()->highlightNextItem(this);
+ return true;
+ }
+ else if (key == KEY_RETURN && mask == MASK_NONE)
+ {
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(true);
+
+ onCommit();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool LLMenuItemGL::handleMouseUp( S32 x, S32 y, MASK mask)
+{
+ // switch to mouse navigation mode
+ LLMenuGL::setKeyboardMode(false);
+
+ onCommit();
+ make_ui_sound("UISndClickRelease");
+ return LLView::handleMouseUp(x, y, mask);
+}
+
+bool LLMenuItemGL::handleMouseDown( S32 x, S32 y, MASK mask)
+{
+ // switch to mouse navigation mode
+ LLMenuGL::setKeyboardMode(false);
+
+ setHighlight(true);
+ return LLView::handleMouseDown(x, y, mask);
+}
+
+bool LLMenuItemGL::handleScrollWheel( S32 x, S32 y, S32 clicks )
+{
+ // If the menu is scrollable let it handle the wheel event.
+ return !getMenu()->isScrollable();
+}
+
+void LLMenuItemGL::draw( void )
+{
+ // *FIX: This can be optimized by using switches. Want to avoid
+ // that until the functionality is finalized.
+
+ // HACK: Brief items don't highlight. Pie menu takes care of it. JC
+ // let disabled items be highlighted, just don't draw them as such
+ if( getEnabled() && getHighlight() && !mBriefItem)
+ {
+ gGL.color4fv( mHighlightBackground.get().mV );
+
+ gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 );
+ }
+
+ LLColor4 color;
+
+ if ( getEnabled() && getHighlight() )
+ {
+ color = mHighlightForeground.get();
+ }
+ else if( getEnabled() && !mDrawTextDisabled )
+ {
+ color = mEnabledColor.get();
+ }
+ else
+ {
+ color = mDisabledColor.get();
+ }
+
+ // Highlight if needed
+ if( ll::ui::SearchableControl::getHighlighted() )
+ color = ll::ui::SearchableControl::getHighlightColor();
+
+ // Draw the text on top.
+ if (mBriefItem)
+ {
+ mFont->render( mLabel, 0, BRIEF_PAD_PIXELS / 2, 0, color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL);
+ }
+ else
+ {
+ if( !mDrawBoolLabel.empty() )
+ {
+ mFont->render( mDrawBoolLabel.getWString(), 0, (F32)LEFT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false );
+ }
+ mFont->render( mLabel.getWString(), 0, (F32)LEFT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false );
+ if( !mDrawAccelLabel.empty() )
+ {
+ mFont->render( mDrawAccelLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color,
+ LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false );
+ }
+ if( !mDrawBranchLabel.empty() )
+ {
+ mFont->render( mDrawBranchLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color,
+ LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false );
+ }
+ }
+
+ // underline "jump" key only when keyboard navigation has been initiated
+ if (getMenu()->jumpKeysActive() && LLMenuGL::getKeyboardMode())
+ {
+ std::string upper_case_label = mLabel.getString();
+ LLStringUtil::toUpper(upper_case_label);
+ std::string::size_type offset = upper_case_label.find(mJumpKey);
+ if (offset != std::string::npos)
+ {
+ S32 x_begin = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset);
+ S32 x_end = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset + 1);
+ gl_line_2d(x_begin, (MENU_ITEM_PADDING / 2) + 1, x_end, (MENU_ITEM_PADDING / 2) + 1);
+ }
+ }
+}
+
+bool LLMenuItemGL::setLabelArg( const std::string& key, const LLStringExplicit& text )
+{
+ mLabel.setArg(key, text);
+ return true;
+}
+
+void LLMenuItemGL::onVisibilityChange(bool new_visibility)
+{
+ if (getMenu())
+ {
+ getMenu()->needsArrange();
+ }
+ LLView::onVisibilityChange(new_visibility);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemSeparatorGL
+//
+// This class represents a separator.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LLMenuItemSeparatorGL::Params::Params()
+ : on_visible("on_visible")
+{
+}
+
+LLMenuItemSeparatorGL::LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p) :
+ LLMenuItemGL( p )
+{
+ if (p.on_visible.isProvided())
+ {
+ mVisibleSignal.connect(initEnableCallback(p.on_visible));
+ }
+}
+
+//virtual
+U32 LLMenuItemSeparatorGL::getNominalHeight( void ) const
+{
+ return SEPARATOR_HEIGHT_PIXELS;
+}
+
+void LLMenuItemSeparatorGL::draw( void )
+{
+ gGL.color4fv( mDisabledColor.get().mV );
+ const S32 y = getRect().getHeight() / 2;
+ const S32 PAD = 6;
+ gl_line_2d( PAD, y, getRect().getWidth() - PAD, y );
+}
+
+void LLMenuItemSeparatorGL::buildDrawLabel( void )
+{
+ if (mVisibleSignal.num_slots() > 0)
+ {
+ bool visible = mVisibleSignal(this, LLSD());
+ setVisible(visible);
+ }
+}
+
+bool LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLMenuGL* parent_menu = getMenu();
+ if (y > getRect().getHeight() / 2)
+ {
+ // the menu items are in the child list in bottom up order
+ LLView* prev_menu_item = parent_menu->findNextSibling(this);
+ return (prev_menu_item && prev_menu_item->getVisible() && prev_menu_item->getEnabled()) ? prev_menu_item->handleMouseDown(x, prev_menu_item->getRect().getHeight(), mask) : false;
+ }
+ else
+ {
+ LLView* next_menu_item = parent_menu->findPrevSibling(this);
+ return (next_menu_item && next_menu_item->getVisible() && next_menu_item->getEnabled()) ? next_menu_item->handleMouseDown(x, 0, mask) : false;
+ }
+}
+
+bool LLMenuItemSeparatorGL::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ LLMenuGL* parent_menu = getMenu();
+ if (y > getRect().getHeight() / 2)
+ {
+ LLView* prev_menu_item = parent_menu->findNextSibling(this);
+ return (prev_menu_item && prev_menu_item->getVisible() && prev_menu_item->getEnabled()) ? prev_menu_item->handleMouseUp(x, prev_menu_item->getRect().getHeight(), mask) : false;
+ }
+ else
+ {
+ LLView* next_menu_item = parent_menu->findPrevSibling(this);
+ return (next_menu_item && next_menu_item->getVisible() && next_menu_item->getEnabled()) ? next_menu_item->handleMouseUp(x, 0, mask) : false;
+ }
+}
+
+bool LLMenuItemSeparatorGL::handleHover(S32 x, S32 y, MASK mask)
+{
+ LLMenuGL* parent_menu = getMenu();
+ if (y > getRect().getHeight() / 2)
+ {
+ parent_menu->highlightPrevItem(this, false);
+ return false;
+ }
+ else
+ {
+ parent_menu->highlightNextItem(this, false);
+ return false;
+ }
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemVerticalSeparatorGL
+//
+// This class represents a vertical separator.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMenuItemVerticalSeparatorGL
+: public LLMenuItemSeparatorGL
+{
+public:
+ LLMenuItemVerticalSeparatorGL( void );
+
+ virtual bool handleMouseDown(S32 x, S32 y, MASK mask) { return false; }
+};
+
+LLMenuItemVerticalSeparatorGL::LLMenuItemVerticalSeparatorGL( void )
+{
+ setLabel( VERTICAL_SEPARATOR_LABEL );
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemTearOffGL
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LLMenuItemTearOffGL::LLMenuItemTearOffGL(const LLMenuItemTearOffGL::Params& p)
+: LLMenuItemGL(p)
+{
+}
+
+// Returns the first floater ancestor if there is one
+LLFloater* LLMenuItemTearOffGL::getParentFloater()
+{
+ LLView* parent_view = getMenu();
+
+ while (parent_view)
+ {
+ if (dynamic_cast<LLFloater*>(parent_view))
+ {
+ return dynamic_cast<LLFloater*>(parent_view);
+ }
+
+ bool parent_is_menu = dynamic_cast<LLMenuGL*>(parent_view) && !dynamic_cast<LLMenuBarGL*>(parent_view);
+
+ if (parent_is_menu)
+ {
+ // use menu parent
+ parent_view = dynamic_cast<LLMenuGL*>(parent_view)->getParentMenuItem();
+ }
+ else
+ {
+ // just use regular view parent
+ parent_view = parent_view->getParent();
+ }
+ }
+
+ return NULL;
+}
+
+void LLMenuItemTearOffGL::onCommit()
+{
+ if (getMenu()->getTornOff())
+ {
+ LLTearOffMenu * torn_off_menu = dynamic_cast<LLTearOffMenu*>(getMenu()->getParent());
+ if (torn_off_menu)
+ {
+ torn_off_menu->closeFloater();
+ }
+ }
+ else
+ {
+ // transfer keyboard focus and highlight to first real item in list
+ if (getHighlight())
+ {
+ getMenu()->highlightNextItem(this);
+ }
+
+ getMenu()->needsArrange();
+
+ LLFloater* parent_floater = getParentFloater();
+ LLFloater* tear_off_menu = LLTearOffMenu::create(getMenu());
+
+ if (tear_off_menu)
+ {
+ if (parent_floater)
+ {
+ parent_floater->addDependentFloater(tear_off_menu, false);
+ }
+
+ // give focus to torn off menu because it will have
+ // been taken away when parent menu closes
+ tear_off_menu->setFocus(true);
+ }
+ }
+ LLMenuItemGL::onCommit();
+}
+
+void LLMenuItemTearOffGL::draw()
+{
+ // disabled items can be highlighted, but shouldn't render as such
+ if( getEnabled() && getHighlight() && !isBriefItem())
+ {
+ gGL.color4fv( mHighlightBackground.get().mV );
+ gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 );
+ }
+
+ if (getEnabled())
+ {
+ gGL.color4fv( mEnabledColor.get().mV );
+ }
+ else
+ {
+ gGL.color4fv( mDisabledColor.get().mV );
+ }
+ const S32 y = getRect().getHeight() / 3;
+ const S32 PAD = 6;
+ gl_line_2d( PAD, y, getRect().getWidth() - PAD, y );
+ gl_line_2d( PAD, y * 2, getRect().getWidth() - PAD, y * 2 );
+}
+
+U32 LLMenuItemTearOffGL::getNominalHeight( void ) const
+{
+ return TEAROFF_SEPARATOR_HEIGHT_PIXELS;
+}
+
+///============================================================================
+/// Class LLMenuItemCallGL
+///============================================================================
+
+LLMenuItemCallGL::LLMenuItemCallGL(const LLMenuItemCallGL::Params& p)
+: LLMenuItemGL(p)
+{
+}
+
+void LLMenuItemCallGL::initFromParams(const Params& p)
+{
+ if (p.on_visible.isProvided())
+ {
+ mVisibleSignal.connect(initEnableCallback(p.on_visible));
+ }
+ if (p.on_enable.isProvided())
+ {
+ setEnableCallback(initEnableCallback(p.on_enable));
+ // Set the enabled control variable (for backwards compatability)
+ if (p.on_enable.control_name.isProvided() && !p.on_enable.control_name().empty())
+ {
+ LLControlVariable* control = findControl(p.on_enable.control_name());
+ if (control)
+ {
+ setEnabledControlVariable(control);
+ }
+ else
+ {
+ LL_WARNS() << "Failed to assign 'enabled' control variable to menu " << getName()
+ << ": control " << p.on_enable.control_name()
+ << " does not exist." << LL_ENDL;
+ }
+ }
+ }
+ if (p.on_click.isProvided())
+ {
+ setCommitCallback(initCommitCallback(p.on_click));
+ }
+
+ LLUICtrl::initFromParams(p);
+}
+
+void LLMenuItemCallGL::onCommit( void )
+{
+ // RN: menu item can be deleted in callback, so beware
+ getMenu()->setItemLastSelected( this );
+
+ LLMenuItemGL::onCommit();
+}
+
+void LLMenuItemCallGL::updateEnabled( void )
+{
+ if (mEnableSignal.num_slots() > 0)
+ {
+ bool enabled = mEnableSignal(this, LLSD());
+ if (mEnabledControlVariable)
+ {
+ if (!enabled)
+ {
+ // callback overrides control variable; this will call setEnabled()
+ mEnabledControlVariable->set(false);
+ }
+ }
+ else
+ {
+ setEnabled(enabled);
+ }
+ }
+}
+
+void LLMenuItemCallGL::updateVisible( void )
+{
+ if (mVisibleSignal.num_slots() > 0)
+ {
+ bool visible = mVisibleSignal(this, LLSD());
+ setVisible(visible);
+ }
+}
+
+void LLMenuItemCallGL::buildDrawLabel( void )
+{
+ updateEnabled();
+ updateVisible();
+ LLMenuItemGL::buildDrawLabel();
+}
+
+bool LLMenuItemCallGL::handleKeyHere( KEY key, MASK mask )
+{
+ return LLMenuItemGL::handleKeyHere(key, mask);
+}
+
+bool LLMenuItemCallGL::handleAcceleratorKey( KEY key, MASK mask )
+{
+ if( (!gKeyboard->getKeyRepeated(key) || getAllowKeyRepeat()) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) )
+ {
+ updateEnabled();
+ if (getEnabled())
+ {
+ onCommit();
+ return true;
+ }
+ }
+ return false;
+}
+
+// handleRightMouseUp moved into base class LLMenuItemGL so clicks are
+// handled for all menu item types
+
+///============================================================================
+/// Class LLMenuItemCheckGL
+///============================================================================
+LLMenuItemCheckGL::LLMenuItemCheckGL (const LLMenuItemCheckGL::Params& p)
+: LLMenuItemCallGL(p)
+{
+}
+
+void LLMenuItemCheckGL::initFromParams(const Params& p)
+{
+ if (p.on_check.isProvided())
+ {
+ setCheckCallback(initEnableCallback(p.on_check));
+ // Set the control name (for backwards compatability)
+ if (p.on_check.control_name.isProvided() && !p.on_check.control_name().empty())
+ {
+ setControlName(p.on_check.control_name());
+ }
+ }
+
+ LLMenuItemCallGL::initFromParams(p);
+}
+
+void LLMenuItemCheckGL::onCommit( void )
+{
+ LLMenuItemCallGL::onCommit();
+}
+
+//virtual
+void LLMenuItemCheckGL::setValue(const LLSD& value)
+{
+ LLUICtrl::setValue(value);
+ if(value.asBoolean())
+ {
+ mDrawBoolLabel = LLMenuGL::BOOLEAN_TRUE_PREFIX;
+ }
+ else
+ {
+ mDrawBoolLabel.clear();
+ }
+}
+
+//virtual
+LLSD LLMenuItemCheckGL::getValue() const
+{
+ // Get our boolean value from the view model.
+ // If we don't override this method then the implementation from
+ // LLMenuItemGL will return a string. (EXT-8501)
+ return LLUICtrl::getValue();
+}
+
+// called to rebuild the draw label
+void LLMenuItemCheckGL::buildDrawLabel( void )
+{
+ // Note: mCheckSignal() returns true if no callbacks are set
+ bool checked = mCheckSignal(this, LLSD());
+ if (mControlVariable)
+ {
+ if (!checked)
+ setControlValue(false); // callback overrides control variable; this will call setValue()
+ }
+ else
+ {
+ setValue(checked);
+ }
+ if(getValue().asBoolean())
+ {
+ mDrawBoolLabel = LLMenuGL::BOOLEAN_TRUE_PREFIX;
+ }
+ else
+ {
+ mDrawBoolLabel.clear();
+ }
+ LLMenuItemCallGL::buildDrawLabel();
+}
+
+///============================================================================
+/// Class LLMenuItemBranchGL
+///============================================================================
+LLMenuItemBranchGL::LLMenuItemBranchGL(const LLMenuItemBranchGL::Params& p)
+ : LLMenuItemGL(p)
+{
+ LLMenuGL* branch = p.branch;
+ if (branch)
+ {
+ mBranchHandle = branch->getHandle();
+ branch->setVisible(false);
+ branch->setParentMenuItem(this);
+ }
+}
+
+LLMenuItemBranchGL::~LLMenuItemBranchGL()
+{
+ if (mBranchHandle.get())
+ {
+ mBranchHandle.get()->die();
+ }
+}
+
+
+
+// virtual
+LLView* LLMenuItemBranchGL::getChildView(const std::string& name, bool recurse) const
+{
+ LLMenuGL* branch = getBranch();
+ if (branch)
+ {
+ if (branch->getName() == name)
+ {
+ return branch;
+ }
+
+ // Always recurse on branches
+ return branch->getChildView(name, recurse);
+ }
+
+ return LLView::getChildView(name, recurse);
+}
+
+LLView* LLMenuItemBranchGL::findChildView(const std::string& name, bool recurse) const
+{
+ LLMenuGL* branch = getBranch();
+ if (branch)
+ {
+ if (branch->getName() == name)
+ {
+ return branch;
+ }
+
+ // Always recurse on branches
+ return branch->findChildView(name, recurse);
+ }
+
+ return LLView::findChildView(name, recurse);
+}
+
+// virtual
+bool LLMenuItemBranchGL::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ // switch to mouse navigation mode
+ LLMenuGL::setKeyboardMode(false);
+
+ onCommit();
+ make_ui_sound("UISndClickRelease");
+ return true;
+}
+
+bool LLMenuItemBranchGL::hasAccelerator(const KEY &key, const MASK &mask) const
+{
+ return getBranch() && getBranch()->hasAccelerator(key, mask);
+}
+
+bool LLMenuItemBranchGL::handleAcceleratorKey(KEY key, MASK mask)
+{
+ return getBranch() && getBranch()->handleAcceleratorKey(key, mask);
+}
+
+// This function checks to see if the accelerator key is already in use;
+// if not, it will be added to the list
+bool LLMenuItemBranchGL::addToAcceleratorList(std::list<LLMenuKeyboardBinding*> *listp)
+{
+ LLMenuGL* branch = getBranch();
+ if (!branch)
+ return false;
+
+ U32 item_count = branch->getItemCount();
+ LLMenuItemGL *item;
+
+ while (item_count--)
+ {
+ if ((item = branch->getItem(item_count)))
+ {
+ return item->addToAcceleratorList(listp);
+ }
+ }
+
+ return false;
+}
+
+
+// called to rebuild the draw label
+void LLMenuItemBranchGL::buildDrawLabel( void )
+{
+ mDrawAccelLabel.clear();
+ std::string st = mDrawAccelLabel;
+ appendAcceleratorString( st );
+ mDrawAccelLabel = st;
+ mDrawBranchLabel = LLMenuGL::BRANCH_SUFFIX;
+}
+
+void LLMenuItemBranchGL::onCommit( void )
+{
+ openMenu();
+
+ // keyboard navigation automatically propagates highlight to sub-menu
+ // to facilitate fast menu control via jump keys
+ if (LLMenuGL::getKeyboardMode() && getBranch() && !getBranch()->getHighlightedItem())
+ {
+ getBranch()->highlightNextItem(NULL);
+ }
+
+ LLUICtrl::onCommit();
+}
+
+bool LLMenuItemBranchGL::handleKey(KEY key, MASK mask, bool called_from_parent)
+{
+ bool handled = false;
+ if (getBranch() && called_from_parent)
+ {
+ handled = getBranch()->handleKey(key, mask, called_from_parent);
+ }
+
+ if (!handled)
+ {
+ handled = LLMenuItemGL::handleKey(key, mask, called_from_parent);
+ }
+
+ return handled;
+}
+
+bool LLMenuItemBranchGL::handleUnicodeChar(llwchar uni_char, bool called_from_parent)
+{
+ bool handled = false;
+ if (getBranch() && called_from_parent)
+ {
+ handled = getBranch()->handleUnicodeChar(uni_char, true);
+ }
+
+ if (!handled)
+ {
+ handled = LLMenuItemGL::handleUnicodeChar(uni_char, called_from_parent);
+ }
+
+ return handled;
+}
+
+
+void LLMenuItemBranchGL::setHighlight( bool highlight )
+{
+ if (highlight == getHighlight())
+ return;
+
+ LLMenuGL* branch = getBranch();
+ if (!branch)
+ return;
+
+ bool auto_open = getEnabled() && (!branch->getVisible() || branch->getTornOff());
+ // torn off menus don't open sub menus on hover unless they have focus
+ LLFloater * menu_parent = dynamic_cast<LLFloater *>(getMenu()->getParent());
+ if (getMenu()->getTornOff() && menu_parent && !menu_parent->hasFocus())
+ {
+ auto_open = false;
+ }
+ // don't auto open torn off sub-menus (need to explicitly active menu item to give them focus)
+ if (branch->getTornOff())
+ {
+ auto_open = false;
+ }
+ LLMenuItemGL::setHighlight(highlight);
+ if( highlight )
+ {
+ if(auto_open)
+ {
+ openMenu();
+ }
+ }
+ else
+ {
+ if (branch->getTornOff())
+ {
+ LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent());
+ if (branch_parent)
+ {
+ branch_parent->setFocus(false);
+ }
+ branch->clearHoverItem();
+ }
+ else
+ {
+ branch->setVisible( false );
+ }
+ }
+}
+
+void LLMenuItemBranchGL::draw()
+{
+ LLMenuItemGL::draw();
+ if (getBranch() && getBranch()->getVisible() && !getBranch()->getTornOff())
+ {
+ setHighlight(true);
+ }
+}
+
+void LLMenuItemBranchGL::updateBranchParent(LLView* parentp)
+{
+ if (getBranch() && getBranch()->getParent() == NULL)
+ {
+ // make the branch menu a sibling of my parent menu
+ getBranch()->updateParent(parentp);
+ }
+}
+
+void LLMenuItemBranchGL::onVisibilityChange(bool new_visibility)
+{
+ if (!new_visibility && getBranch() && !getBranch()->getTornOff())
+ {
+ getBranch()->setVisible(false);
+ }
+ LLMenuItemGL::onVisibilityChange(new_visibility);
+}
+
+bool LLMenuItemBranchGL::handleKeyHere(KEY key, MASK mask)
+{
+ LLMenuGL* branch = getBranch();
+ if (!branch)
+ return LLMenuItemGL::handleKeyHere(key, mask);
+
+ // an item is highlighted, my menu is open, and I have an active sub menu or we are in
+ // keyboard navigation mode
+ if (getHighlight()
+ && getMenu()->isOpen()
+ && (isActive() || LLMenuGL::getKeyboardMode()))
+ {
+ if (branch->getVisible() && key == KEY_LEFT)
+ {
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(true);
+
+ bool handled = branch->clearHoverItem();
+ if (branch->getTornOff())
+ {
+ LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent());
+ if (branch_parent)
+ {
+ branch_parent->setFocus(false);
+ }
+ }
+ if (handled && getMenu()->getTornOff())
+ {
+ LLFloater * menu_parent = dynamic_cast<LLFloater *>(getMenu()->getParent());
+ if (menu_parent)
+ {
+ menu_parent->setFocus(true);
+ }
+ }
+ return handled;
+ }
+
+ if (key == KEY_RIGHT && !branch->getHighlightedItem())
+ {
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(true);
+
+ LLMenuItemGL* itemp = branch->highlightNextItem(NULL);
+ if (itemp)
+ {
+ return true;
+ }
+ }
+ }
+ return LLMenuItemGL::handleKeyHere(key, mask);
+}
+
+//virtual
+bool LLMenuItemBranchGL::isActive() const
+{
+ return isOpen() && getBranch() && getBranch()->getHighlightedItem();
+}
+
+//virtual
+bool LLMenuItemBranchGL::isOpen() const
+{
+ return getBranch() && getBranch()->isOpen();
+}
+
+void LLMenuItemBranchGL::openMenu()
+{
+ LLMenuGL* branch = getBranch();
+ if (!branch)
+ return;
+
+ if (branch->getTornOff())
+ {
+ LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent());
+ if (branch_parent)
+ {
+ gFloaterView->bringToFront(branch_parent);
+ // this might not be necessary, as torn off branches don't get focus and hence no highligth
+ branch->highlightNextItem(NULL);
+ }
+ }
+ else if( !branch->getVisible() )
+ {
+ // get valid rectangle for menus
+ const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect();
+
+ branch->arrange();
+
+ LLRect branch_rect = branch->getRect();
+ // calculate root-view relative position for branch menu
+ S32 left = getRect().mRight;
+ S32 top = getRect().mTop - getRect().mBottom;
+
+ localPointToOtherView(left, top, &left, &top, branch->getParent());
+
+ branch_rect.setLeftTopAndSize( left, top,
+ branch_rect.getWidth(), branch_rect.getHeight() );
+
+ if (branch->getCanTearOff())
+ {
+ branch_rect.translate(0, TEAROFF_SEPARATOR_HEIGHT_PIXELS);
+ }
+ branch->setRect( branch_rect );
+
+ // if branch extends outside of menu region change the direction it opens in
+ S32 x, y;
+ S32 delta_x = 0;
+ S32 delta_y = 0;
+ branch->localPointToOtherView( 0, 0, &x, &y, branch->getParent() );
+ if( y < menu_region_rect.mBottom )
+ {
+ // open upwards if menu extends past bottom
+ // adjust by the height of the menu item branch since it is a submenu
+ if (y + 2 * branch_rect.getHeight() - getRect().getHeight() > menu_region_rect.mTop)
+ {
+ // overlaps with top border, align with top
+ delta_y = menu_region_rect.mTop - y - branch_rect.getHeight();
+ }
+ else
+ {
+ delta_y = branch_rect.getHeight() - getRect().getHeight();
+ }
+ }
+
+ if( x + branch_rect.getWidth() > menu_region_rect.mRight )
+ {
+ // move sub-menu over to left side
+ delta_x = llmax(-x, ( -(branch_rect.getWidth() + getRect().getWidth())));
+ }
+ branch->translate( delta_x, delta_y );
+
+ branch->setVisible( true );
+ branch->getParent()->sendChildToFront(branch);
+
+ dirtyRect();
+ }
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemBranchDownGL
+//
+// The LLMenuItemBranchDownGL represents a menu item that has a
+// sub-menu. This is used to make menu bar menus.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMenuItemBranchDownGL : public LLMenuItemBranchGL
+{
+protected:
+
+public:
+ LLMenuItemBranchDownGL( const Params& );
+
+ // returns the normal width of this control in pixels - this is
+ // used for calculating the widest item, as well as for horizontal
+ // arrangement.
+ virtual U32 getNominalWidth( void ) const;
+
+ // called to rebuild the draw label
+ virtual void buildDrawLabel( void );
+
+ // handles opening, positioning, and arranging the menu branch associated with this item
+ virtual void openMenu( void );
+
+ // set the hover status (called by it's menu) and if the object is
+ // active. This is used for behavior transfer.
+ virtual void setHighlight( bool highlight );
+
+ virtual bool isActive( void ) const;
+
+ // LLView functionality
+ virtual bool handleMouseDown( S32 x, S32 y, MASK mask );
+ virtual bool handleMouseUp( S32 x, S32 y, MASK mask );
+ virtual void draw( void );
+ virtual bool handleKeyHere(KEY key, MASK mask);
+
+ virtual bool handleAcceleratorKey(KEY key, MASK mask);
+
+ virtual void onFocusLost();
+ virtual void setFocus(bool b);
+};
+
+LLMenuItemBranchDownGL::LLMenuItemBranchDownGL( const Params& p) :
+ LLMenuItemBranchGL(p)
+{
+}
+
+// returns the normal width of this control in pixels - this is used
+// for calculating the widest item, as well as for horizontal
+// arrangement.
+U32 LLMenuItemBranchDownGL::getNominalWidth( void ) const
+{
+ U32 width = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS;
+ width += getFont()->getWidth( mLabel.getWString().c_str() );
+ return width;
+}
+
+// called to rebuild the draw label
+void LLMenuItemBranchDownGL::buildDrawLabel( void )
+{
+ mDrawAccelLabel.clear();
+ std::string st = mDrawAccelLabel;
+ appendAcceleratorString( st );
+ mDrawAccelLabel = st;
+}
+
+void LLMenuItemBranchDownGL::openMenu( void )
+{
+ LLMenuGL* branch = getBranch();
+ if( branch->getVisible() && !branch->getTornOff() )
+ {
+ branch->setVisible( false );
+ }
+ else
+ {
+ if (branch->getTornOff())
+ {
+ LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent());
+ if (branch_parent)
+ {
+ gFloaterView->bringToFront(branch_parent);
+ }
+ }
+ else
+ {
+ // We're showing the drop-down menu, so patch up its labels/rects
+ branch->arrange();
+
+ LLRect rect = branch->getRect();
+ S32 left = 0;
+ S32 top = getRect().mBottom;
+ localPointToOtherView(left, top, &left, &top, branch->getParent());
+
+ rect.setLeftTopAndSize( left, top,
+ rect.getWidth(), rect.getHeight() );
+ branch->setRect( rect );
+ S32 x = 0;
+ S32 y = 0;
+ branch->localPointToScreen( 0, 0, &x, &y );
+ S32 delta_x = 0;
+
+ LLCoordScreen window_size;
+ LLWindow* windowp = getWindow();
+ windowp->getSize(&window_size);
+
+ S32 window_width = window_size.mX;
+ if( x > window_width - rect.getWidth() )
+ {
+ delta_x = (window_width - rect.getWidth()) - x;
+ }
+ branch->translate( delta_x, 0 );
+
+ setHighlight(true);
+ branch->setVisible( true );
+ branch->getParent()->sendChildToFront(branch);
+ }
+ }
+}
+
+// set the hover status (called by it's menu)
+void LLMenuItemBranchDownGL::setHighlight( bool highlight )
+{
+ if (highlight == getHighlight())
+ return;
+
+ //NOTE: Purposely calling all the way to the base to bypass auto-open.
+ LLMenuItemGL::setHighlight(highlight);
+
+ LLMenuGL* branch = getBranch();
+ if (!branch)
+ return;
+
+ if( !highlight)
+ {
+ if (branch->getTornOff())
+ {
+ LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent());
+ if (branch_parent)
+ {
+ branch_parent->setFocus(false);
+ }
+ branch->clearHoverItem();
+ }
+ else
+ {
+ branch->setVisible( false );
+ }
+ }
+}
+
+bool LLMenuItemBranchDownGL::isActive() const
+{
+ // for top level menus, being open is sufficient to be considered
+ // active, because clicking on them with the mouse will open
+ // them, without moving keyboard focus to them
+ return isOpen();
+}
+
+bool LLMenuItemBranchDownGL::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ // switch to mouse control mode
+ LLMenuGL::setKeyboardMode(false);
+
+ if (getVisible() && isOpen())
+ {
+ LLMenuGL::sMenuContainer->hideMenus();
+ }
+ else
+ {
+ onCommit();
+ }
+
+ make_ui_sound("UISndClick");
+ return true;
+}
+
+bool LLMenuItemBranchDownGL::handleMouseUp( S32 x, S32 y, MASK mask )
+{
+ return true;
+}
+
+
+bool LLMenuItemBranchDownGL::handleAcceleratorKey(KEY key, MASK mask)
+{
+ bool branch_visible = getBranch()->getVisible();
+ bool handled = getBranch()->handleAcceleratorKey(key, mask);
+ if (handled && !branch_visible && isInVisibleChain())
+ {
+ // flash this menu entry because we triggered an invisible menu item
+ LLMenuHolderGL::setActivatedItem(this);
+ }
+
+ return handled;
+}
+void LLMenuItemBranchDownGL::onFocusLost()
+{
+ // needed for tab-based selection
+ LLMenuItemBranchGL::onFocusLost();
+ LLMenuGL::setKeyboardMode(false);
+ setHighlight(false);
+}
+
+void LLMenuItemBranchDownGL::setFocus(bool b)
+{
+ // needed for tab-based selection
+ LLMenuItemBranchGL::setFocus(b);
+ LLMenuGL::setKeyboardMode(b);
+ setHighlight(b);
+}
+
+bool LLMenuItemBranchDownGL::handleKeyHere(KEY key, MASK mask)
+{
+ bool menu_open = getBranch()->getVisible();
+ // don't do keyboard navigation of top-level menus unless in keyboard mode, or menu expanded
+ if (getHighlight() && getMenu()->isOpen() && (isActive() || LLMenuGL::getKeyboardMode()))
+ {
+ if (key == KEY_LEFT)
+ {
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(true);
+
+ LLMenuItemGL* itemp = getMenu()->highlightPrevItem(this);
+ // open new menu only if previous menu was open
+ if (itemp && itemp->getEnabled() && menu_open)
+ {
+ itemp->onCommit();
+ }
+
+ return true;
+ }
+ else if (key == KEY_RIGHT)
+ {
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(true);
+
+ LLMenuItemGL* itemp = getMenu()->highlightNextItem(this);
+ // open new menu only if previous menu was open
+ if (itemp && itemp->getEnabled() && menu_open)
+ {
+ itemp->onCommit();
+ }
+
+ return true;
+ }
+ else if (key == KEY_DOWN)
+ {
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(true);
+
+ if (!isActive())
+ {
+ onCommit();
+ }
+ getBranch()->highlightNextItem(NULL);
+ return true;
+ }
+ else if (key == KEY_UP)
+ {
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(true);
+
+ if (!isActive())
+ {
+ onCommit();
+ }
+ getBranch()->highlightPrevItem(NULL);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void LLMenuItemBranchDownGL::draw( void )
+{
+ //FIXME: try removing this
+ if (getBranch()->getVisible() && !getBranch()->getTornOff())
+ {
+ setHighlight(true);
+ }
+
+ if( getHighlight() )
+ {
+ gGL.color4fv( mHighlightBackground.get().mV );
+ gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 );
+ }
+
+ LLColor4 color;
+ if (getHighlight())
+ {
+ color = mHighlightForeground.get();
+ }
+ else if( getEnabled() )
+ {
+ color = mEnabledColor.get();
+ }
+ else
+ {
+ color = mDisabledColor.get();
+ }
+ getFont()->render( mLabel.getWString(), 0, (F32)getRect().getWidth() / 2.f, (F32)LABEL_BOTTOM_PAD_PIXELS, color,
+ LLFontGL::HCENTER, LLFontGL::BOTTOM, LLFontGL::NORMAL);
+
+
+ // underline navigation key only when keyboard navigation has been initiated
+ if (getMenu()->jumpKeysActive() && LLMenuGL::getKeyboardMode())
+ {
+ std::string upper_case_label = mLabel.getString();
+ LLStringUtil::toUpper(upper_case_label);
+ std::string::size_type offset = upper_case_label.find(getJumpKey());
+ if (offset != std::string::npos)
+ {
+ S32 x_offset = ll_round((F32)getRect().getWidth() / 2.f - getFont()->getWidthF32(mLabel.getString(), 0, S32_MAX) / 2.f);
+ S32 x_begin = x_offset + getFont()->getWidth(mLabel, 0, offset);
+ S32 x_end = x_offset + getFont()->getWidth(mLabel, 0, offset + 1);
+ gl_line_2d(x_begin, LABEL_BOTTOM_PAD_PIXELS, x_end, LABEL_BOTTOM_PAD_PIXELS);
+ }
+ }
+}
+
+
+class LLMenuScrollItem : public LLMenuItemCallGL
+{
+public:
+ enum EArrowType
+ {
+ ARROW_DOWN,
+ ARROW_UP
+ };
+ struct ArrowTypes : public LLInitParam::TypeValuesHelper<EArrowType, ArrowTypes>
+ {
+ static void declareValues()
+ {
+ declare("up", ARROW_UP);
+ declare("down", ARROW_DOWN);
+ }
+ };
+
+ struct Params : public LLInitParam::Block<Params, LLMenuItemCallGL::Params>
+ {
+ Optional<EArrowType, ArrowTypes> arrow_type;
+ Optional<CommitCallbackParam> scroll_callback;
+ };
+
+protected:
+ LLMenuScrollItem(const Params&);
+ friend class LLUICtrlFactory;
+
+public:
+ /*virtual*/ void draw();
+ /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent);
+ /*virtual*/ void setEnabled(bool enabled);
+ virtual void onCommit( void );
+
+private:
+ LLButton* mArrowBtn;
+};
+
+LLMenuScrollItem::LLMenuScrollItem(const Params& p)
+: LLMenuItemCallGL(p)
+{
+ std::string icon;
+ if (p.arrow_type.isProvided() && p.arrow_type == ARROW_UP)
+ {
+ icon = "arrow_up.tga";
+ }
+ else
+ {
+ icon = "arrow_down.tga";
+ }
+
+ LLButton::Params bparams;
+
+ // Disabled the Return key handling by LLMenuScrollItem instead of
+ // passing the key press to the currently selected menu item. See STORM-385.
+ bparams.commit_on_return(false);
+ bparams.mouse_opaque(true);
+ bparams.scale_image(false);
+ bparams.click_callback(p.scroll_callback);
+ bparams.mouse_held_callback(p.scroll_callback);
+ bparams.follows.flags(FOLLOWS_ALL);
+ std::string background = "transparent.j2c";
+ bparams.image_unselected.name(background);
+ bparams.image_disabled.name(background);
+ bparams.image_selected.name(background);
+ bparams.image_hover_selected.name(background);
+ bparams.image_disabled_selected.name(background);
+ bparams.image_hover_unselected.name(background);
+ bparams.image_overlay.name(icon);
+
+ mArrowBtn = LLUICtrlFactory::create<LLButton>(bparams);
+ addChild(mArrowBtn);
+}
+
+/*virtual*/
+void LLMenuScrollItem::draw()
+{
+ LLUICtrl::draw();
+}
+
+/*virtual*/
+void LLMenuScrollItem::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ mArrowBtn->reshape(width, height, called_from_parent);
+ LLView::reshape(width, height, called_from_parent);
+}
+
+/*virtual*/
+void LLMenuScrollItem::setEnabled(bool enabled)
+{
+ mArrowBtn->setEnabled(enabled);
+ LLView::setEnabled(enabled);
+}
+
+void LLMenuScrollItem::onCommit( void )
+{
+ LLUICtrl::onCommit();
+}
+
+///============================================================================
+/// Class LLMenuGL
+///============================================================================
+
+LLMenuGL::LLMenuGL(const LLMenuGL::Params& p)
+: LLUICtrl(p),
+ mBackgroundColor( p.bg_color() ),
+ mBgVisible( p.bg_visible ),
+ mDropShadowed( p.drop_shadow ),
+ mHasSelection(false),
+ mHorizontalLayout( p.horizontal_layout ),
+ mScrollable(mHorizontalLayout ? false : p.scrollable), // Scrolling is supported only for vertical layout
+ mMaxScrollableItems(p.max_scrollable_items),
+ mPreferredWidth(p.preferred_width),
+ mKeepFixedSize( p.keep_fixed_size ),
+ mLabel (p.label),
+ mLastMouseX(0),
+ mLastMouseY(0),
+ mMouseVelX(0),
+ mMouseVelY(0),
+ mTornOff(false),
+ mTearOffItem(NULL),
+ mSpilloverBranch(NULL),
+ mFirstVisibleItem(NULL),
+ mArrowUpItem(NULL),
+ mArrowDownItem(NULL),
+ mSpilloverMenu(NULL),
+ mJumpKey(p.jump_key),
+ mCreateJumpKeys(p.create_jump_keys),
+ mNeedsArrange(false),
+ mAlwaysShowMenu(false),
+ mResetScrollPositionOnShow(true),
+ mShortcutPad(p.shortcut_pad),
+ mFont(p.font)
+{
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("_");
+ tokenizer tokens(p.label(), sep);
+ tokenizer::iterator token_iter;
+
+ S32 token_count = 0;
+ std::string new_menu_label;
+ for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
+ {
+ new_menu_label += (*token_iter);
+ if (token_count > 0)
+ {
+ setJumpKey((*token_iter).c_str()[0]);
+ }
+ ++token_count;
+ }
+ setLabel(new_menu_label);
+
+ mFadeTimer.stop();
+}
+
+void LLMenuGL::initFromParams(const LLMenuGL::Params& p)
+{
+ LLUICtrl::initFromParams(p);
+ setCanTearOff(p.can_tear_off);
+}
+
+// Destroys the object
+LLMenuGL::~LLMenuGL( void )
+{
+ // delete the branch, as it might not be in view hierarchy
+ // leave the menu, because it is always in view hierarchy
+ delete mSpilloverBranch;
+ mJumpKeys.clear();
+}
+
+void LLMenuGL::setCanTearOff(bool tear_off)
+{
+ if (tear_off && mTearOffItem == NULL)
+ {
+ LLMenuItemTearOffGL::Params p;
+ mTearOffItem = LLUICtrlFactory::create<LLMenuItemTearOffGL>(p);
+ addChild(mTearOffItem);
+ }
+ else if (!tear_off && mTearOffItem != NULL)
+ {
+ mItems.remove(mTearOffItem);
+ removeChild(mTearOffItem);
+ delete mTearOffItem;
+ mTearOffItem = NULL;
+ needsArrange();
+ }
+}
+
+bool LLMenuGL::addChild(LLView* view, S32 tab_group)
+{
+ LLMenuGL* menup = dynamic_cast<LLMenuGL*>(view);
+ if (menup)
+ {
+ return appendMenu(menup);
+ }
+
+ LLMenuItemGL* itemp = dynamic_cast<LLMenuItemGL*>(view);
+ if (itemp)
+ {
+ return append(itemp);
+ }
+
+ return false;
+}
+
+// Used in LLContextMenu and in LLTogleableMenu
+
+// Add an item to the context menu branch
+bool LLMenuGL::addContextChild(LLView* view, S32 tab_group)
+{
+ LLContextMenu* context = dynamic_cast<LLContextMenu*>(view);
+ if (context)
+ {
+ return appendContextSubMenu(context);
+ }
+
+ LLMenuItemSeparatorGL* separator = dynamic_cast<LLMenuItemSeparatorGL*>(view);
+ if (separator)
+ {
+ return append(separator);
+ }
+
+ LLMenuItemGL* item = dynamic_cast<LLMenuItemGL*>(view);
+ if (item)
+ {
+ return append(item);
+ }
+
+ LLMenuGL* menup = dynamic_cast<LLMenuGL*>(view);
+ if (menup)
+ {
+ return appendMenu(menup);
+ }
+
+ return false;
+}
+
+
+void LLMenuGL::deleteAllChildren()
+{
+ mItems.clear();
+ LLUICtrl::deleteAllChildren();
+}
+
+void LLMenuGL::removeChild( LLView* ctrl)
+{
+ // previously a dynamic_cast with if statement to check validity
+ // unfortunately removeChild is called by ~LLView, and at that point the
+ // object being deleted is no longer a LLMenuItemGL so a dynamic_cast will fail
+ LLMenuItemGL* itemp = static_cast<LLMenuItemGL*>(ctrl);
+
+ item_list_t::iterator found_it = std::find(mItems.begin(), mItems.end(), (itemp));
+ if (found_it != mItems.end())
+ {
+ mItems.erase(found_it);
+ }
+
+ return LLUICtrl::removeChild(ctrl);
+}
+
+bool LLMenuGL::postBuild()
+{
+ createJumpKeys();
+ return LLUICtrl::postBuild();
+}
+
+// are we the childmost active menu and hence our jump keys should be enabled?
+// or are we a free-standing torn-off menu (which uses jump keys too)
+bool LLMenuGL::jumpKeysActive()
+{
+ LLMenuItemGL* highlighted_item = getHighlightedItem();
+ bool active = getVisible() && getEnabled();
+
+ if (active)
+ {
+ if (getTornOff())
+ {
+ // activation of jump keys on torn off menus controlled by keyboard focus
+ LLFloater * parent = dynamic_cast<LLFloater *>(getParent());
+ if (parent)
+ {
+ active = parent->hasFocus();
+ }
+ }
+ else
+ {
+ // Are we the terminal active menu?
+ // Yes, if parent menu item deems us to be active (just being visible is sufficient for top-level menus)
+ // and we don't have a highlighted menu item pointing to an active sub-menu
+ active = (!getParentMenuItem() || getParentMenuItem()->isActive()) // I have a parent that is active...
+ && (!highlighted_item || !highlighted_item->isActive()); //... but no child that is active
+ }
+ }
+
+ return active;
+}
+
+bool LLMenuGL::isOpen()
+{
+ if (getTornOff())
+ {
+ LLMenuItemGL* itemp = getHighlightedItem();
+ // if we have an open sub-menu, then we are considered part of
+ // the open menu chain even if we don't have focus
+ if (itemp && itemp->isOpen())
+ {
+ return true;
+ }
+ // otherwise we are only active if we have keyboard focus
+ LLFloater * parent = dynamic_cast<LLFloater *>(getParent());
+ if (parent)
+ {
+ return parent->hasFocus();
+ }
+ return false;
+ }
+ else
+ {
+ // normally, menus are hidden as soon as the user focuses
+ // on another menu, so just use the visibility criterion
+ return getVisible();
+ }
+}
+
+
+
+bool LLMenuGL::scrollItems(EScrollingDirection direction)
+{
+ // Slowing down items scrolling when arrow button is held
+ if (mScrollItemsTimer.hasExpired() && NULL != mFirstVisibleItem)
+ {
+ mScrollItemsTimer.setTimerExpirySec(.033f);
+ }
+ else
+ {
+ return false;
+ }
+
+ switch (direction)
+ {
+ case SD_UP:
+ {
+ item_list_t::iterator cur_item_iter;
+ item_list_t::iterator prev_item_iter;
+ for (cur_item_iter = mItems.begin(), prev_item_iter = mItems.begin(); cur_item_iter != mItems.end(); cur_item_iter++)
+ {
+ if( (*cur_item_iter) == mFirstVisibleItem)
+ {
+ break;
+ }
+ if ((*cur_item_iter)->getVisible())
+ {
+ prev_item_iter = cur_item_iter;
+ }
+ }
+
+ if ((*prev_item_iter)->getVisible())
+ {
+ mFirstVisibleItem = *prev_item_iter;
+ }
+ break;
+ }
+ case SD_DOWN:
+ {
+ if (NULL == mFirstVisibleItem)
+ {
+ mFirstVisibleItem = *mItems.begin();
+ }
+
+ item_list_t::iterator cur_item_iter;
+
+ for (cur_item_iter = mItems.begin(); cur_item_iter != mItems.end(); cur_item_iter++)
+ {
+ if( (*cur_item_iter) == mFirstVisibleItem)
+ {
+ break;
+ }
+ }
+
+ item_list_t::iterator next_item_iter;
+
+ if (cur_item_iter != mItems.end())
+ {
+ for (next_item_iter = ++cur_item_iter; next_item_iter != mItems.end(); next_item_iter++)
+ {
+ if( (*next_item_iter)->getVisible())
+ {
+ break;
+ }
+ }
+
+ if (next_item_iter != mItems.end() &&
+ (*next_item_iter)->getVisible())
+ {
+ mFirstVisibleItem = *next_item_iter;
+ }
+ }
+ break;
+ }
+ case SD_BEGIN:
+ {
+ mFirstVisibleItem = *mItems.begin();
+ break;
+ }
+ case SD_END:
+ {
+ item_list_t::reverse_iterator first_visible_item_iter = mItems.rend();
+
+ // Need to scroll through number of actual existing items in menu.
+ // Otherwise viewer will hang for a time needed to scroll U32_MAX
+ // times in std::advance(). STORM-659.
+ size_t nitems = mItems.size();
+ U32 scrollable_items = nitems < mMaxScrollableItems ? nitems : mMaxScrollableItems;
+
+ // Advance by mMaxScrollableItems back from the end of the list
+ // to make the last item visible.
+ std::advance(first_visible_item_iter, scrollable_items);
+ mFirstVisibleItem = *first_visible_item_iter;
+ break;
+ }
+ default:
+ LL_WARNS() << "Unknown scrolling direction: " << direction << LL_ENDL;
+ }
+
+ mNeedsArrange = true;
+ arrangeAndClear();
+
+ return true;
+}
+
+// rearrange the child rects so they fit the shape of the menu.
+void LLMenuGL::arrange( void )
+{
+ // calculate the height & width, and set our rect based on that
+ // information.
+ const LLRect& initial_rect = getRect();
+
+ U32 width = 0, height = MENU_ITEM_PADDING;
+
+ cleanupSpilloverBranch();
+
+ if( mItems.size() )
+ {
+ const LLRect menu_region_rect = LLMenuGL::sMenuContainer ? LLMenuGL::sMenuContainer->getMenuRect() : LLRect(0, S32_MAX, S32_MAX, 0);
+
+ // torn off menus are not constrained to the size of the screen
+ U32 max_width = getTornOff() ? U32_MAX : menu_region_rect.getWidth();
+ U32 max_height = getTornOff() ? U32_MAX: menu_region_rect.getHeight();
+
+ // *FIX: create the item first and then ask for its dimensions?
+ S32 spillover_item_width = PLAIN_PAD_PIXELS + LLFontGL::getFontSansSerif()->getWidth( std::string("More") ); // *TODO: Translate
+ S32 spillover_item_height = LLFontGL::getFontSansSerif()->getLineHeight() + MENU_ITEM_PADDING;
+
+ // Scrolling support
+ item_list_t::iterator first_visible_item_iter;
+ item_list_t::iterator first_hidden_item_iter = mItems.end();
+ S32 height_before_first_visible_item = -1;
+ S32 visible_items_height = 0;
+ U32 scrollable_items_cnt = 0;
+
+ if (mHorizontalLayout)
+ {
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ // do first so LLMenuGLItemCall can call on_visible to determine if visible
+ (*item_iter)->buildDrawLabel();
+
+ if ((*item_iter)->getVisible())
+ {
+ if (!getTornOff()
+ && *item_iter != mSpilloverBranch
+ && width + (*item_iter)->getNominalWidth() > max_width - spillover_item_width)
+ {
+ // no room for any more items
+ createSpilloverBranch();
+
+ std::vector<LLMenuItemGL*> items_to_remove;
+ std::copy(item_iter, mItems.end(), std::back_inserter(items_to_remove));
+ std::vector<LLMenuItemGL*>::iterator spillover_iter;
+ for (spillover_iter= items_to_remove.begin(); spillover_iter != items_to_remove.end(); ++spillover_iter)
+ {
+ LLMenuItemGL* itemp = (*spillover_iter);
+ removeChild(itemp);
+ mSpilloverMenu->addChild(itemp);
+ }
+
+ addChild(mSpilloverBranch);
+
+ height = llmax(height, mSpilloverBranch->getNominalHeight());
+ width += mSpilloverBranch->getNominalWidth();
+
+ break;
+ }
+ else
+ {
+ // track our rect
+ height = llmax(height, (*item_iter)->getNominalHeight());
+ width += (*item_iter)->getNominalWidth();
+ }
+ }
+ }
+ }
+ else
+ {
+ for (LLMenuItemGL* itemp : mItems)
+ {
+ // do first so LLMenuGLItemCall can call on_visible to determine if visible
+ itemp->buildDrawLabel();
+ }
+ item_list_t::iterator item_iter;
+
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if ((*item_iter)->getVisible())
+ {
+ if (!getTornOff()
+ && !mScrollable
+ && *item_iter != mSpilloverBranch
+ && height + (*item_iter)->getNominalHeight() > max_height - spillover_item_height)
+ {
+ // don't show only one item
+ int visible_items = 0;
+ item_list_t::iterator count_iter;
+ for (count_iter = item_iter; count_iter != mItems.end(); ++count_iter)
+ {
+ if((*count_iter)->getVisible())
+ visible_items++;
+ }
+ if (visible_items>1)
+ {
+ // no room for any more items
+ createSpilloverBranch();
+
+ std::vector<LLMenuItemGL*> items_to_remove;
+ std::copy(item_iter, mItems.end(), std::back_inserter(items_to_remove));
+ std::vector<LLMenuItemGL*>::iterator spillover_iter;
+ for (spillover_iter= items_to_remove.begin(); spillover_iter != items_to_remove.end(); ++spillover_iter)
+ {
+ LLMenuItemGL* itemp = (*spillover_iter);
+ removeChild(itemp);
+ mSpilloverMenu->addChild(itemp);
+ }
+
+
+ addChild(mSpilloverBranch);
+
+ height += mSpilloverBranch->getNominalHeight();
+ width = llmax( width, mSpilloverBranch->getNominalWidth() );
+
+ break;
+ }
+ }
+
+ // track our rect
+ height += (*item_iter)->getNominalHeight();
+ width = llmax( width, (*item_iter)->getNominalWidth() );
+
+ if (mScrollable)
+ {
+ // Determining visible items boundaries
+ if (NULL == mFirstVisibleItem)
+ {
+ mFirstVisibleItem = *item_iter;
+ }
+
+ if (*item_iter == mFirstVisibleItem)
+ {
+ height_before_first_visible_item = height - (*item_iter)->getNominalHeight();
+ first_visible_item_iter = item_iter;
+ scrollable_items_cnt = 0;
+ }
+
+ if (-1 != height_before_first_visible_item && 0 == visible_items_height &&
+ (++scrollable_items_cnt > mMaxScrollableItems ||
+ height - height_before_first_visible_item > max_height - spillover_item_height * 2 ))
+ {
+ first_hidden_item_iter = item_iter;
+ visible_items_height = height - height_before_first_visible_item - (*item_iter)->getNominalHeight();
+ scrollable_items_cnt--;
+ }
+ }
+ }
+ }
+
+ if (mPreferredWidth < U32_MAX)
+ width = llmin(mPreferredWidth, max_width);
+
+ if (mScrollable)
+ {
+ S32 max_items_height = max_height - spillover_item_height * 2;
+
+ if (visible_items_height == 0)
+ visible_items_height = height - height_before_first_visible_item;
+
+ // Fix mFirstVisibleItem value, if it doesn't allow to display all items, that can fit
+ if (visible_items_height < max_items_height && scrollable_items_cnt < mMaxScrollableItems)
+ {
+ item_list_t::iterator tmp_iter(first_visible_item_iter);
+ while (visible_items_height < max_items_height &&
+ scrollable_items_cnt < mMaxScrollableItems &&
+ first_visible_item_iter != mItems.begin())
+ {
+ if ((*first_visible_item_iter)->getVisible())
+ {
+ // It keeps visible item, after first_visible_item_iter
+ tmp_iter = first_visible_item_iter;
+ }
+
+ first_visible_item_iter--;
+
+ if ((*first_visible_item_iter)->getVisible())
+ {
+ visible_items_height += (*first_visible_item_iter)->getNominalHeight();
+ height_before_first_visible_item -= (*first_visible_item_iter)->getNominalHeight();
+ scrollable_items_cnt++;
+ }
+ }
+
+ // Roll back one item, that doesn't fit
+ if (visible_items_height > max_items_height)
+ {
+ visible_items_height -= (*first_visible_item_iter)->getNominalHeight();
+ height_before_first_visible_item += (*first_visible_item_iter)->getNominalHeight();
+ scrollable_items_cnt--;
+ first_visible_item_iter = tmp_iter;
+ }
+ if (!(*first_visible_item_iter)->getVisible())
+ {
+ first_visible_item_iter = tmp_iter;
+ }
+
+ mFirstVisibleItem = *first_visible_item_iter;
+ }
+ }
+ }
+
+ S32 cur_height = (S32)llmin(max_height, height);
+
+ if (mScrollable &&
+ (height_before_first_visible_item > MENU_ITEM_PADDING ||
+ height_before_first_visible_item + visible_items_height < (S32)height))
+ {
+ // Reserving 2 extra slots for arrow items
+ cur_height = visible_items_height + spillover_item_height * 2;
+ }
+
+ setRect(LLRect(getRect().mLeft, getRect().mTop, getRect().mLeft + width, getRect().mTop - cur_height));
+
+ S32 cur_width = 0;
+ S32 offset = 0;
+ if (mScrollable)
+ {
+ // No space for all items, creating arrow items
+ if (height_before_first_visible_item > MENU_ITEM_PADDING ||
+ height_before_first_visible_item + visible_items_height < (S32)height)
+ {
+ if (NULL == mArrowUpItem)
+ {
+ LLMenuScrollItem::Params item_params;
+ item_params.name(ARROW_UP);
+ item_params.arrow_type(LLMenuScrollItem::ARROW_UP);
+ item_params.scroll_callback.function(boost::bind(&LLMenuGL::scrollItems, this, SD_UP));
+
+ mArrowUpItem = LLUICtrlFactory::create<LLMenuScrollItem>(item_params);
+ LLUICtrl::addChild(mArrowUpItem);
+
+ }
+ if (NULL == mArrowDownItem)
+ {
+ LLMenuScrollItem::Params item_params;
+ item_params.name(ARROW_DOWN);
+ item_params.arrow_type(LLMenuScrollItem::ARROW_DOWN);
+ item_params.scroll_callback.function(boost::bind(&LLMenuGL::scrollItems, this, SD_DOWN));
+
+ mArrowDownItem = LLUICtrlFactory::create<LLMenuScrollItem>(item_params);
+ LLUICtrl::addChild(mArrowDownItem);
+ }
+
+ LLRect rect;
+ mArrowUpItem->setRect(rect.setLeftTopAndSize( 0, cur_height, width, mArrowUpItem->getNominalHeight()));
+ mArrowUpItem->setVisible(true);
+ mArrowUpItem->setEnabled(height_before_first_visible_item > MENU_ITEM_PADDING);
+ mArrowUpItem->reshape(width, mArrowUpItem->getNominalHeight());
+ mArrowDownItem->setRect(rect.setLeftTopAndSize( 0, mArrowDownItem->getNominalHeight(), width, mArrowDownItem->getNominalHeight()));
+ mArrowDownItem->setVisible(true);
+ mArrowDownItem->setEnabled(height_before_first_visible_item + visible_items_height < (S32)height);
+ mArrowDownItem->reshape(width, mArrowDownItem->getNominalHeight());
+
+ cur_height -= mArrowUpItem->getNominalHeight();
+
+ offset = menu_region_rect.mRight; // This moves items behind visible area
+ }
+ else
+ {
+ if (NULL != mArrowUpItem)
+ {
+ mArrowUpItem->setVisible(false);
+ }
+ if (NULL != mArrowDownItem)
+ {
+ mArrowDownItem->setVisible(false);
+ }
+ }
+
+ }
+
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if ((*item_iter)->getVisible())
+ {
+ if (mScrollable)
+ {
+ if (item_iter == first_visible_item_iter)
+ {
+ offset = 0;
+ }
+ else if (item_iter == first_hidden_item_iter)
+ {
+ offset = menu_region_rect.mRight; // This moves items behind visible area
+ }
+ }
+
+ // setup item rect to hold label
+ LLRect rect;
+ if (mHorizontalLayout)
+ {
+ rect.setLeftTopAndSize( cur_width, height, (*item_iter)->getNominalWidth(), height);
+ cur_width += (*item_iter)->getNominalWidth();
+ }
+ else
+ {
+ rect.setLeftTopAndSize( 0 + offset, cur_height, width, (*item_iter)->getNominalHeight());
+ if (offset == 0)
+ {
+ cur_height -= (*item_iter)->getNominalHeight();
+ }
+ }
+ (*item_iter)->setRect( rect );
+ }
+ }
+
+
+ if (getTornOff())
+ {
+ LLTearOffMenu * torn_off_menu = dynamic_cast<LLTearOffMenu*>(getParent());
+ if (torn_off_menu)
+ {
+ torn_off_menu->updateSize();
+ }
+ }
+ }
+ if (mKeepFixedSize)
+ {
+ reshape(initial_rect.getWidth(), initial_rect.getHeight());
+ }
+}
+
+void LLMenuGL::arrangeAndClear( void )
+{
+ if (mNeedsArrange)
+ {
+ arrange();
+ mNeedsArrange = false;
+ }
+}
+
+void LLMenuGL::createSpilloverBranch()
+{
+ if (!mSpilloverBranch)
+ {
+ // should be NULL but delete anyway
+ delete mSpilloverMenu;
+ // technically, you can't tear off spillover menus, but we're passing the handle
+ // along just to be safe
+ LLMenuGL::Params p;
+ std::string label = LLTrans::getString("More");
+ p.name("More");
+ p.label(label);
+ p.bg_color(mBackgroundColor);
+ p.bg_visible(true);
+ p.can_tear_off(false);
+ mSpilloverMenu = new LLMenuGL(p);
+ mSpilloverMenu->updateParent(LLMenuGL::sMenuContainer);
+
+ LLMenuItemBranchGL::Params branch_params;
+ branch_params.name = "More";
+ branch_params.label = label;
+ branch_params.branch = mSpilloverMenu;
+ branch_params.font.style = "italic";
+ branch_params.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor");
+ branch_params.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor");
+ branch_params.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor");
+
+ mSpilloverBranch = LLUICtrlFactory::create<LLMenuItemBranchGL>(branch_params);
+ }
+}
+
+void LLMenuGL::cleanupSpilloverBranch()
+{
+ if (mSpilloverBranch && mSpilloverBranch->getParent() == this)
+ {
+ // head-recursion to propagate items back up to root menu
+ mSpilloverMenu->cleanupSpilloverBranch();
+
+ // pop off spillover items
+ while (mSpilloverMenu->getItemCount())
+ {
+ LLMenuItemGL* itemp = mSpilloverMenu->getItem(0);
+ mSpilloverMenu->removeChild(itemp);
+ // put them at the end of our own list
+ addChild(itemp);
+ }
+
+ // Delete the branch, and since the branch will delete the menu,
+ // set the menu* to null.
+ delete mSpilloverBranch;
+ mSpilloverBranch = NULL;
+ mSpilloverMenu = NULL;
+ }
+}
+
+void LLMenuGL::createJumpKeys()
+{
+ if (!mCreateJumpKeys) return;
+ mCreateJumpKeys = false;
+
+ mJumpKeys.clear();
+
+ std::set<std::string> unique_words;
+ std::set<std::string> shared_words;
+
+ item_list_t::iterator item_it;
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(" ");
+
+ for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it)
+ {
+ std::string uppercase_label = (*item_it)->getLabel();
+ LLStringUtil::toUpper(uppercase_label);
+
+ tokenizer tokens(uppercase_label, sep);
+ tokenizer::iterator token_iter;
+ for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
+ {
+ if (unique_words.find(*token_iter) != unique_words.end())
+ {
+ // this word exists in more than one menu instance
+ shared_words.insert(*token_iter);
+ }
+ else
+ {
+ // we have a new word, keep track of it
+ unique_words.insert(*token_iter);
+ }
+ }
+ }
+
+ // pre-assign specified jump keys
+ for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it)
+ {
+ KEY jump_key = (*item_it)->getJumpKey();
+ if(jump_key != KEY_NONE)
+ {
+ if (mJumpKeys.find(jump_key) == mJumpKeys.end())
+ {
+ mJumpKeys.insert(std::pair<KEY, LLMenuItemGL*>(jump_key, (*item_it)));
+ }
+ else
+ {
+ // this key is already spoken for,
+ // so we need to reassign it below
+ (*item_it)->setJumpKey(KEY_NONE);
+ }
+ }
+ }
+
+ for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it)
+ {
+ // skip over items that already have assigned jump keys
+ if ((*item_it)->getJumpKey() != KEY_NONE)
+ {
+ continue;
+ }
+ std::string uppercase_label = (*item_it)->getLabel();
+ LLStringUtil::toUpper(uppercase_label);
+
+ tokenizer tokens(uppercase_label, sep);
+ tokenizer::iterator token_iter;
+
+ bool found_key = false;
+ for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
+ {
+ std::string uppercase_word = *token_iter;
+
+ // this word is not shared with other menu entries...
+ if (shared_words.find(*token_iter) == shared_words.end())
+ {
+ S32 i;
+ for(i = 0; i < (S32)uppercase_word.size(); i++)
+ {
+ char jump_key = uppercase_word[i];
+
+ if (LLStringOps::isDigit(jump_key) || (LLStringOps::isUpper(jump_key) &&
+ mJumpKeys.find(jump_key) == mJumpKeys.end()))
+ {
+ mJumpKeys.insert(std::pair<KEY, LLMenuItemGL*>(jump_key, (*item_it)));
+ (*item_it)->setJumpKey(jump_key);
+ found_key = true;
+ break;
+ }
+ }
+ }
+ if (found_key)
+ {
+ break;
+ }
+ }
+ }
+}
+
+// remove all items on the menu
+void LLMenuGL::empty( void )
+{
+ cleanupSpilloverBranch();
+
+ mItems.clear();
+ mFirstVisibleItem = NULL;
+ mArrowUpItem = NULL;
+ mArrowDownItem = NULL;
+
+ deleteAllChildren();
+}
+
+// erase group of items from menu
+void LLMenuGL::erase( S32 begin, S32 end, bool arrange/* = true*/)
+{
+ S32 items = mItems.size();
+
+ if ( items == 0 || begin >= end || begin < 0 || end > items )
+ {
+ return;
+ }
+
+ item_list_t::iterator start_position = mItems.begin();
+ std::advance(start_position, begin);
+
+ item_list_t::iterator end_position = mItems.begin();
+ std::advance(end_position, end);
+
+ for (item_list_t::iterator position_iter = start_position; position_iter != end_position; position_iter++)
+ {
+ LLUICtrl::removeChild(*position_iter);
+ }
+
+ mItems.erase(start_position, end_position);
+
+ if (arrange)
+ {
+ needsArrange();
+ }
+}
+
+// add new item at position
+void LLMenuGL::insert( S32 position, LLView * ctrl, bool arrange /*= true*/ )
+{
+ LLMenuItemGL * item = dynamic_cast<LLMenuItemGL *>(ctrl);
+
+ if (NULL == item || position < 0 || position >= mItems.size())
+ {
+ return;
+ }
+
+ item_list_t::iterator position_iter = mItems.begin();
+ std::advance(position_iter, position);
+ mItems.insert(position_iter, item);
+ LLUICtrl::addChild(item);
+
+ if (arrange)
+ {
+ needsArrange();
+ }
+}
+
+// Adjust rectangle of the menu
+void LLMenuGL::setLeftAndBottom(S32 left, S32 bottom)
+{
+ setRect(LLRect(left, getRect().mTop, getRect().mRight, bottom));
+ needsArrange();
+}
+
+bool LLMenuGL::handleJumpKey(KEY key)
+{
+ // must perform case-insensitive comparison, so just switch to uppercase input key
+ key = toupper(key);
+ navigation_key_map_t::iterator found_it = mJumpKeys.find(key);
+ if(found_it != mJumpKeys.end() && found_it->second->getEnabled())
+ {
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(true);
+
+ // force highlight to close old menus and open and sub-menus
+ found_it->second->setHighlight(true);
+ found_it->second->onCommit();
+
+ }
+ // if we are navigating the menus, we need to eat the keystroke
+ // so rest of UI doesn't handle it
+ return true;
+}
+
+
+// Add the menu item to this menu.
+bool LLMenuGL::append( LLMenuItemGL* item )
+{
+ if (!item) return false;
+ mItems.push_back( item );
+ LLUICtrl::addChild(item);
+ needsArrange();
+ return true;
+}
+
+// add a separator to this menu
+bool LLMenuGL::addSeparator()
+{
+ LLMenuItemSeparatorGL::Params p;
+ LLMenuItemGL* separator = LLUICtrlFactory::create<LLMenuItemSeparatorGL>(p);
+ return addChild(separator);
+}
+
+// add a menu - this will create a cascading menu
+bool LLMenuGL::appendMenu( LLMenuGL* menu )
+{
+ if( menu == this )
+ {
+ LL_ERRS() << "** Attempt to attach menu to itself. This is certainly "
+ << "a logic error." << LL_ENDL;
+ }
+ bool success = true;
+
+ LLMenuItemBranchGL::Params p;
+ p.name = menu->getName();
+ p.label = menu->getLabel();
+ p.branch = menu;
+ p.jump_key = menu->getJumpKey();
+ p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor");
+ p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor");
+ p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor");
+ p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor");
+
+ LLMenuItemBranchGL* branch = LLUICtrlFactory::create<LLMenuItemBranchGL>(p);
+ success &= append( branch );
+
+ // Inherit colors
+ menu->setBackgroundColor( mBackgroundColor );
+ menu->updateParent(LLMenuGL::sMenuContainer);
+ return success;
+}
+
+// add a context menu branch
+bool LLMenuGL::appendContextSubMenu(LLMenuGL *menu)
+{
+ if (menu == this)
+ {
+ LL_ERRS() << "Can't attach a context menu to itself" << LL_ENDL;
+ }
+
+ LLContextMenuBranch *item;
+ LLContextMenuBranch::Params p;
+ p.name = menu->getName();
+ p.label = menu->getLabel();
+ p.branch = (LLContextMenu *)menu;
+ p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor");
+ p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor");
+ p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor");
+ p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor");
+
+ item = LLUICtrlFactory::create<LLContextMenuBranch>(p);
+ LLMenuGL::sMenuContainer->addChild(item->getBranch());
+
+ return append( item );
+}
+
+void LLMenuGL::setEnabledSubMenus(bool enable)
+{
+ setEnabled(enable);
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ (*item_iter)->setEnabledSubMenus( enable );
+ }
+}
+
+// setItemEnabled() - pass the label and the enable flag for a menu
+// item. true will make sure it's enabled, false will disable it.
+void LLMenuGL::setItemEnabled( const std::string& name, bool enable )
+{
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if( (*item_iter)->getName() == name )
+ {
+ (*item_iter)->setEnabled( enable );
+ (*item_iter)->setEnabledSubMenus( enable );
+ break;
+ }
+ }
+}
+
+void LLMenuGL::setItemVisible( const std::string& name, bool visible )
+{
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if( (*item_iter)->getName() == name )
+ {
+ (*item_iter)->setVisible( visible );
+ needsArrange();
+ break;
+ }
+ }
+}
+
+
+void LLMenuGL::setItemLabel(const std::string &name, const std::string &label)
+{
+ LLMenuItemGL *item = getItem(name);
+
+ if (item)
+ item->setLabel(label);
+}
+
+void LLMenuGL::setItemLastSelected(LLMenuItemGL* item)
+{
+ if (getVisible())
+ {
+ LLMenuHolderGL::setActivatedItem(item);
+ }
+
+ // update enabled and checkmark status
+ item->buildDrawLabel();
+}
+
+// Set whether drop shadowed
+void LLMenuGL::setDropShadowed( const bool shadowed )
+{
+ mDropShadowed = shadowed;
+}
+
+void LLMenuGL::setTornOff(bool torn_off)
+{
+ mTornOff = torn_off;
+}
+
+U32 LLMenuGL::getItemCount()
+{
+ return mItems.size();
+}
+
+LLMenuItemGL* LLMenuGL::getItem(S32 number)
+{
+ if (number >= 0 && number < (S32)mItems.size())
+ {
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if (number == 0)
+ {
+ return (*item_iter);
+ }
+ number--;
+ }
+ }
+ return NULL;
+}
+
+LLMenuItemGL* LLMenuGL::getItem(std::string name)
+{
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if ((*item_iter)->getName() == name)
+ {
+ return (*item_iter);
+ }
+ }
+ return NULL;
+}
+
+LLMenuItemGL* LLMenuGL::getHighlightedItem()
+{
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if ((*item_iter)->getHighlight())
+ {
+ return (*item_iter);
+ }
+ }
+ return NULL;
+}
+
+LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, bool skip_disabled)
+{
+ if (mItems.empty()) return NULL;
+ // highlighting first item on a torn off menu is the
+ // same as giving focus to it
+ if (!cur_item && getTornOff())
+ {
+ LLFloater * parent = dynamic_cast<LLFloater *>(getParent());
+ if (parent)
+ {
+ parent->setFocus(true);
+ }
+ }
+
+ // Current item position in the items list
+ item_list_t::iterator cur_item_iter = std::find(mItems.begin(), mItems.end(), cur_item);
+
+ item_list_t::iterator next_item_iter;
+ if (cur_item_iter == mItems.end())
+ {
+ next_item_iter = mItems.begin();
+ }
+ else
+ {
+ next_item_iter = cur_item_iter;
+ next_item_iter++;
+
+ // First visible item position in the items list
+ item_list_t::iterator first_visible_item_iter = std::find(mItems.begin(), mItems.end(), mFirstVisibleItem);
+
+ if (next_item_iter == mItems.end())
+ {
+ next_item_iter = mItems.begin();
+
+ // If current item is the last in the list, the menu is scrolled to the beginning
+ // and the first item is highlighted.
+ if (mScrollable && !scrollItems(SD_BEGIN))
+ {
+ return NULL;
+ }
+ }
+ // If current item is the last visible, the menu is scrolled one item down
+ // and the next item is highlighted.
+ else if (mScrollable &&
+ (U32)std::abs(std::distance(first_visible_item_iter, next_item_iter)) >= mMaxScrollableItems)
+ {
+ // Call highlightNextItem() recursively only if the menu was successfully scrolled down.
+ // If scroll timer hasn't expired yet the menu won't be scrolled and calling
+ // highlightNextItem() will result in an endless recursion.
+ if (scrollItems(SD_DOWN))
+ {
+ return highlightNextItem(cur_item, skip_disabled);
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+ }
+
+ // when first highlighting a menu, skip over tear off menu item
+ if (mTearOffItem && !cur_item)
+ {
+ // we know the first item is the tear off menu item
+ cur_item_iter = mItems.begin();
+ next_item_iter++;
+ if (next_item_iter == mItems.end())
+ {
+ next_item_iter = mItems.begin();
+ }
+ }
+
+ while(1)
+ {
+ // skip separators and disabled/invisible items
+ if ((*next_item_iter)->getEnabled() && (*next_item_iter)->getVisible() && !dynamic_cast<LLMenuItemSeparatorGL*>(*next_item_iter))
+ {
+ if (cur_item)
+ {
+ cur_item->setHighlight(false);
+ }
+ (*next_item_iter)->setHighlight(true);
+ return (*next_item_iter);
+ }
+
+
+ if (!skip_disabled || next_item_iter == cur_item_iter)
+ {
+ break;
+ }
+
+ next_item_iter++;
+ if (next_item_iter == mItems.end())
+ {
+ if (cur_item_iter == mItems.end())
+ {
+ break;
+ }
+ next_item_iter = mItems.begin();
+ }
+ }
+
+ return NULL;
+}
+
+LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, bool skip_disabled)
+{
+ if (mItems.empty()) return NULL;
+
+ // highlighting first item on a torn off menu is the
+ // same as giving focus to it
+ if (!cur_item && getTornOff())
+ {
+ LLFloater * parent = dynamic_cast<LLFloater *>(getParent());
+ if (parent)
+ {
+ parent->setFocus(true);
+ }
+ }
+
+ // Current item reverse position from the end of the list
+ item_list_t::reverse_iterator cur_item_iter = std::find(mItems.rbegin(), mItems.rend(), cur_item);
+
+ item_list_t::reverse_iterator prev_item_iter;
+ if (cur_item_iter == mItems.rend())
+ {
+ prev_item_iter = mItems.rbegin();
+ }
+ else
+ {
+ prev_item_iter = cur_item_iter;
+ prev_item_iter++;
+
+ // First visible item reverse position in the items list
+ item_list_t::reverse_iterator first_visible_item_iter = std::find(mItems.rbegin(), mItems.rend(), mFirstVisibleItem);
+
+ if (prev_item_iter == mItems.rend())
+ {
+ prev_item_iter = mItems.rbegin();
+
+ // If current item is the first in the list, the menu is scrolled to the end
+ // and the last item is highlighted.
+ if (mScrollable && !scrollItems(SD_END))
+ {
+ return NULL;
+ }
+ }
+ // If current item is the first visible, the menu is scrolled one item up
+ // and the previous item is highlighted.
+ else if (mScrollable &&
+ std::distance(first_visible_item_iter, cur_item_iter) <= 0)
+ {
+ // Call highlightNextItem() only if the menu was successfully scrolled up.
+ // If scroll timer hasn't expired yet the menu won't be scrolled and calling
+ // highlightNextItem() will result in an endless recursion.
+ if (scrollItems(SD_UP))
+ {
+ return highlightPrevItem(cur_item, skip_disabled);
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+ }
+
+ while(1)
+ {
+ // skip separators and disabled/invisible items
+ if ((*prev_item_iter)->getEnabled() && (*prev_item_iter)->getVisible() && (*prev_item_iter)->getName() != SEPARATOR_NAME)
+ {
+ (*prev_item_iter)->setHighlight(true);
+ return (*prev_item_iter);
+ }
+
+ if (!skip_disabled || prev_item_iter == cur_item_iter)
+ {
+ break;
+ }
+
+ prev_item_iter++;
+ if (prev_item_iter == mItems.rend())
+ {
+ if (cur_item_iter == mItems.rend())
+ {
+ break;
+ }
+
+ prev_item_iter = mItems.rbegin();
+ }
+ }
+
+ return NULL;
+}
+
+void LLMenuGL::buildDrawLabels()
+{
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ (*item_iter)->buildDrawLabel();
+ }
+}
+
+void LLMenuGL::updateParent(LLView* parentp)
+{
+ if (getParent())
+ {
+ getParent()->removeChild(this);
+ }
+ if (parentp)
+ {
+ parentp->addChild(this);
+ }
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ (*item_iter)->updateBranchParent(parentp);
+ }
+}
+
+bool LLMenuGL::hasAccelerator(const KEY &key, const MASK &mask) const
+{
+ if (key == KEY_NONE)
+ {
+ return false;
+ }
+ // Note: checking this way because mAccelerators seems to be broken
+ // mAccelerators probably needs to be cleaned up or fixed
+ // It was used for dupplicate accelerator avoidance.
+ item_list_t::const_iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ LLMenuItemGL* itemp = *item_iter;
+ if (itemp->hasAccelerator(key, mask))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool LLMenuGL::handleAcceleratorKey(KEY key, MASK mask)
+{
+ // don't handle if not enabled
+ if(!getEnabled())
+ {
+ return false;
+ }
+
+ // Pass down even if not visible
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ LLMenuItemGL* itemp = *item_iter;
+ if (itemp->handleAcceleratorKey(key, mask))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool LLMenuGL::handleUnicodeCharHere( llwchar uni_char )
+{
+ if (jumpKeysActive())
+ {
+ return handleJumpKey((KEY)uni_char);
+ }
+ return false;
+}
+
+bool LLMenuGL::handleHover( S32 x, S32 y, MASK mask )
+{
+ // leave submenu in place if slope of mouse < MAX_MOUSE_SLOPE_SUB_MENU
+ bool no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0;
+ S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX;
+ S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY;
+ LLVector2 mouse_dir((F32)mouse_delta_x, (F32)mouse_delta_y);
+ mouse_dir.normVec();
+ LLVector2 mouse_avg_dir((F32)mMouseVelX, (F32)mMouseVelY);
+ mouse_avg_dir.normVec();
+ F32 interp = 0.5f * (llclamp(mouse_dir * mouse_avg_dir, 0.f, 1.f));
+ mMouseVelX = ll_round(lerp((F32)mouse_delta_x, (F32)mMouseVelX, interp));
+ mMouseVelY = ll_round(lerp((F32)mouse_delta_y, (F32)mMouseVelY, interp));
+ mLastMouseX = x;
+ mLastMouseY = y;
+
+ // don't change menu focus unless mouse is moving or alt key is not held down
+ if ((llabs(mMouseVelX) > 0 ||
+ llabs(mMouseVelY) > 0) &&
+ (!mHasSelection ||
+ //(mouse_delta_x == 0 && mouse_delta_y == 0) ||
+ (mMouseVelX < 0) ||
+ llabs((F32)mMouseVelY) / llabs((F32)mMouseVelX) > MAX_MOUSE_SLOPE_SUB_MENU))
+ {
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ if (!viewp->pointInView(local_x, local_y) && ((LLMenuItemGL*)viewp)->getHighlight())
+ {
+ // moving mouse always highlights new item
+ if (mouse_delta_x != 0 || mouse_delta_y != 0)
+ {
+ ((LLMenuItemGL*)viewp)->setHighlight(false);
+ }
+ }
+ }
+
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ //RN: always call handleHover to track mGotHover status
+ // but only set highlight when mouse is moving
+ if( viewp->getVisible() &&
+ //RN: allow disabled items to be highlighted to preserve "active" menus when
+ // moving mouse through them
+ //viewp->getEnabled() &&
+ viewp->pointInView(local_x, local_y) &&
+ viewp->handleHover(local_x, local_y, mask))
+ {
+ // moving mouse always highlights new item
+ if (mouse_delta_x != 0 || mouse_delta_y != 0)
+ {
+ ((LLMenuItemGL*)viewp)->setHighlight(true);
+ LLMenuGL::setKeyboardMode(false);
+ }
+ mHasSelection = true;
+ }
+ }
+ }
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+
+ // *HACK Release the mouse capture
+ // This is done to release the mouse after the Navigation Bar "Back" or "Forward" button
+ // drop-down menu is shown. Otherwise any other view won't be able to handle mouse events
+ // until the user chooses one of the drop-down menu items.
+
+ return true;
+}
+
+bool LLMenuGL::handleScrollWheel( S32 x, S32 y, S32 clicks )
+{
+ if (!mScrollable)
+ return blockMouseEvent(x, y);
+
+ if( clicks > 0 )
+ {
+ while( clicks-- )
+ scrollItems(SD_DOWN);
+ }
+ else
+ {
+ while( clicks++ )
+ scrollItems(SD_UP);
+ }
+
+ return true;
+}
+
+
+void LLMenuGL::draw( void )
+{
+ if (mNeedsArrange)
+ {
+ arrange();
+ mNeedsArrange = false;
+ }
+ if (mDropShadowed && !mTornOff)
+ {
+ static LLUIColor color_drop_shadow = LLUIColorTable::instance().getColor("ColorDropShadow");
+ gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0,
+ color_drop_shadow, DROP_SHADOW_FLOATER);
+ }
+
+ if( mBgVisible )
+ {
+ gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0, mBackgroundColor.get() );
+ }
+ LLView::draw();
+}
+
+void LLMenuGL::drawBackground(LLMenuItemGL* itemp, F32 alpha)
+{
+ LLColor4 color = itemp->getHighlightBgColor() % alpha;
+ gGL.color4fv( color.mV );
+ LLRect item_rect = itemp->getRect();
+ gl_rect_2d( 0, item_rect.getHeight(), item_rect.getWidth(), 0);
+}
+
+void LLMenuGL::setVisible(bool visible)
+{
+ if (visible != getVisible())
+ {
+ if (!visible)
+ {
+ mFadeTimer.start();
+ clearHoverItem();
+ // reset last known mouse coordinates so
+ // we don't spoof a mouse move next time we're opened
+ mLastMouseX = 0;
+ mLastMouseY = 0;
+ }
+ else
+ {
+ mHasSelection = true;
+ mFadeTimer.stop();
+ }
+
+ LLView::setVisible(visible);
+ }
+}
+
+LLMenuGL* LLMenuGL::findChildMenuByName(const std::string& name, bool recurse) const
+{
+ LLView* view = findChildView(name, recurse);
+ if (view)
+ {
+ LLMenuItemBranchGL* branch = dynamic_cast<LLMenuItemBranchGL*>(view);
+ if (branch)
+ {
+ return branch->getBranch();
+ }
+
+ LLMenuGL* menup = dynamic_cast<LLMenuGL*>(view);
+ if (menup)
+ {
+ return menup;
+ }
+ }
+ LL_WARNS() << "Child Menu " << name << " not found in menu " << getName() << LL_ENDL;
+ return NULL;
+}
+
+bool LLMenuGL::clearHoverItem()
+{
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLMenuItemGL* itemp = (LLMenuItemGL*)*child_it;
+ if (itemp->getHighlight())
+ {
+ itemp->setHighlight(false);
+ return true;
+ }
+ }
+ return false;
+}
+
+void hide_top_view( LLView* view )
+{
+ if( view ) view->setVisible( false );
+}
+
+
+// x and y are the desired location for the popup, in the spawning_view's
+// coordinate frame, NOT necessarily the mouse location
+// static
+void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y, S32 mouse_x, S32 mouse_y)
+{
+ const S32 CURSOR_HEIGHT = 22; // Approximate "normal" cursor size
+ const S32 CURSOR_WIDTH = 12;
+
+ if (menu->getChildList()->empty())
+ {
+ return;
+ }
+
+ menu->setVisible( true );
+
+ if(!menu->getAlwaysShowMenu())
+ {
+ //Do not show menu if all menu items are disabled
+ bool item_enabled = false;
+ for (LLView::child_list_t::const_iterator itor = menu->getChildList()->begin();
+ itor != menu->getChildList()->end();
+ ++itor)
+ {
+ LLView *menu_item = (*itor);
+ item_enabled = item_enabled || menu_item->getEnabled();
+ }
+
+ if(!item_enabled)
+ {
+ menu->setVisible( false );
+ return;
+ }
+ }
+
+ // Resetting scrolling position
+ if (menu->isScrollable() && menu->isScrollPositionOnShowReset())
+ {
+ menu->mFirstVisibleItem = NULL;
+ }
+
+ // Fix menu rect if needed.
+ menu->needsArrange();
+ menu->arrangeAndClear();
+
+ if ((mouse_x == 0) || (mouse_y == 0))
+
+ {
+ // Save click point for detecting cursor moves before mouse-up.
+ // Must be in local coords to compare with mouseUp events.
+ // If the mouse doesn't move, the menu will stay open ala the Mac.
+ // See also LLContextMenu::show()
+
+ LLUI::getInstance()->getMousePositionLocal(menu->getParent(), &mouse_x, &mouse_y);
+ }
+
+
+ LLMenuHolderGL::sContextMenuSpawnPos.set(mouse_x,mouse_y);
+
+ const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getRect();
+
+ const S32 HPAD = 2;
+ LLRect rect = menu->getRect();
+ S32 left = x + HPAD;
+ S32 top = y;
+ spawning_view->localPointToOtherView(left, top, &left, &top, menu->getParent());
+ rect.setLeftTopAndSize( left, top,
+ rect.getWidth(), rect.getHeight() );
+ menu->setRect( rect );
+
+
+ // Adjust context menu to fit onscreen
+ LLRect mouse_rect;
+ const S32 MOUSE_CURSOR_PADDING = 5;
+ mouse_rect.setLeftTopAndSize(mouse_x - MOUSE_CURSOR_PADDING,
+ mouse_y + MOUSE_CURSOR_PADDING,
+ CURSOR_WIDTH + MOUSE_CURSOR_PADDING * 2,
+ CURSOR_HEIGHT + MOUSE_CURSOR_PADDING * 2);
+ menu->translateIntoRectWithExclusion( menu_region_rect, mouse_rect );
+ if (menu->getRect().mTop > menu_region_rect.mTop)
+ {
+ // not enough space: align with top, ignore exclusion
+ menu->translateIntoRect( menu_region_rect );
+ }
+ menu->getParent()->sendChildToFront(menu);
+}
+
+///============================================================================
+/// Class LLMenuBarGL
+///============================================================================
+
+static LLDefaultChildRegistry::Register<LLMenuBarGL> r2("menu_bar");
+
+LLMenuBarGL::LLMenuBarGL( const Params& p )
+: LLMenuGL(p),
+ mAltKeyTrigger(false)
+{}
+
+// Default destructor
+LLMenuBarGL::~LLMenuBarGL()
+{
+ std::for_each(mAccelerators.begin(), mAccelerators.end(), DeletePointer());
+ mAccelerators.clear();
+}
+
+bool LLMenuBarGL::handleAcceleratorKey(KEY key, MASK mask)
+{
+ if (getHighlightedItem() && mask == MASK_NONE)
+ {
+ // unmodified key accelerators are ignored when navigating menu
+ // (but are used as jump keys so will still work when appropriate menu is up)
+ return false;
+ }
+ bool result = LLMenuGL::handleAcceleratorKey(key, mask);
+ if (result && mask & MASK_ALT)
+ {
+ // ALT key used to trigger hotkey, don't use as shortcut to open menu
+ mAltKeyTrigger = false;
+ }
+
+ if(!result
+ && (key == KEY_F10 && mask == MASK_CONTROL)
+ && !gKeyboard->getKeyRepeated(key)
+ && isInVisibleChain())
+ {
+ if (getHighlightedItem())
+ {
+ clearHoverItem();
+ LLMenuGL::setKeyboardMode(false);
+ }
+ else
+ {
+ // close menus originating from other menu bars when first opening menu via keyboard
+ LLMenuGL::sMenuContainer->hideMenus();
+ highlightNextItem(NULL);
+ LLMenuGL::setKeyboardMode(true);
+ }
+ return true;
+ }
+
+ if (result && !getHighlightedItem() && LLMenuGL::sMenuContainer->hasVisibleMenu())
+ {
+ // close menus originating from other menu bars
+ LLMenuGL::sMenuContainer->hideMenus();
+ }
+
+ return result;
+}
+
+bool LLMenuBarGL::handleKeyHere(KEY key, MASK mask)
+{
+ static LLUICachedControl<bool> use_altkey_for_menus ("UseAltKeyForMenus", 0);
+ if(key == KEY_ALT && !gKeyboard->getKeyRepeated(key) && use_altkey_for_menus)
+ {
+ mAltKeyTrigger = true;
+ }
+ else // if any key other than ALT hit, clear out waiting for Alt key mode
+ {
+ mAltKeyTrigger = false;
+ }
+
+ if (key == KEY_ESCAPE && mask == MASK_NONE)
+ {
+ LLMenuGL::setKeyboardMode(false);
+ // if any menus are visible, this will return true, stopping further processing of ESCAPE key
+ return LLMenuGL::sMenuContainer->hideMenus();
+ }
+
+ // before processing any other key, check to see if ALT key has triggered menu access
+ checkMenuTrigger();
+
+ return LLMenuGL::handleKeyHere(key, mask);
+}
+
+bool LLMenuBarGL::handleJumpKey(KEY key)
+{
+ // perform case-insensitive comparison
+ key = toupper(key);
+ navigation_key_map_t::iterator found_it = mJumpKeys.find(key);
+ if(found_it != mJumpKeys.end() && found_it->second->getEnabled())
+ {
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(true);
+
+ found_it->second->setHighlight(true);
+ found_it->second->onCommit();
+ }
+ return true;
+}
+
+bool LLMenuBarGL::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // clicks on menu bar closes existing menus from other contexts but leave
+ // own menu open so that we get toggle behavior
+ if (!getHighlightedItem() || !getHighlightedItem()->isActive())
+ {
+ LLMenuGL::sMenuContainer->hideMenus();
+ }
+
+ return LLMenuGL::handleMouseDown(x, y, mask);
+}
+
+bool LLMenuBarGL::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ return LLMenuGL::handleMouseDown(x, y, mask);
+}
+
+void LLMenuBarGL::draw()
+{
+ LLMenuItemGL* itemp = getHighlightedItem();
+ // If we are in mouse-control mode and the mouse cursor is not hovering over
+ // the current highlighted menu item and it isn't open, then remove the
+ // highlight. This is done via a polling mechanism here, as we don't receive
+ // notifications when the mouse cursor moves off of us
+ if (itemp && !itemp->isOpen() && !itemp->getHover() && !LLMenuGL::getKeyboardMode())
+ {
+ clearHoverItem();
+ }
+
+ checkMenuTrigger();
+
+ LLMenuGL::draw();
+}
+
+
+void LLMenuBarGL::checkMenuTrigger()
+{
+ // has the ALT key been pressed and subsequently released?
+ if (mAltKeyTrigger && !gKeyboard->getKeyDown(KEY_ALT))
+ {
+ // if alt key was released quickly, treat it as a menu access key
+ // otherwise it was probably an Alt-zoom or similar action
+ static LLUICachedControl<F32> menu_access_key_time ("MenuAccessKeyTime", 0);
+ if (gKeyboard->getKeyElapsedTime(KEY_ALT) <= menu_access_key_time ||
+ gKeyboard->getKeyElapsedFrameCount(KEY_ALT) < 2)
+ {
+ if (getHighlightedItem())
+ {
+ clearHoverItem();
+ }
+ else
+ {
+ // close menus originating from other menu bars
+ LLMenuGL::sMenuContainer->hideMenus();
+
+ highlightNextItem(NULL);
+ LLMenuGL::setKeyboardMode(true);
+ }
+ }
+ mAltKeyTrigger = false;
+ }
+}
+
+bool LLMenuBarGL::jumpKeysActive()
+{
+ // require user to be in keyboard navigation mode to activate key triggers
+ // as menu bars are always visible and it is easy to leave the mouse cursor over them
+ return LLMenuGL::getKeyboardMode() && getHighlightedItem() && LLMenuGL::jumpKeysActive();
+}
+
+// rearrange the child rects so they fit the shape of the menu bar.
+void LLMenuBarGL::arrange( void )
+{
+ U32 pos = 0;
+ LLRect rect( 0, getRect().getHeight(), 0, 0 );
+ item_list_t::const_iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ LLMenuItemGL* item = *item_iter;
+ if (item->getVisible())
+ {
+ rect.mLeft = pos;
+ pos += item->getNominalWidth();
+ rect.mRight = pos;
+ item->setRect( rect );
+ item->buildDrawLabel();
+ }
+ }
+ reshape(rect.mRight, rect.getHeight());
+}
+
+
+S32 LLMenuBarGL::getRightmostMenuEdge()
+{
+ // Find the last visible menu
+ item_list_t::reverse_iterator item_iter;
+ for (item_iter = mItems.rbegin(); item_iter != mItems.rend(); ++item_iter)
+ {
+ if ((*item_iter)->getVisible())
+ {
+ break;
+ }
+ }
+
+ if (item_iter == mItems.rend())
+ {
+ return 0;
+ }
+ return (*item_iter)->getRect().mRight;
+}
+
+// add a vertical separator to this menu
+bool LLMenuBarGL::addSeparator()
+{
+ LLMenuItemGL* separator = new LLMenuItemVerticalSeparatorGL();
+ return append( separator );
+}
+
+// add a menu - this will create a drop down menu.
+bool LLMenuBarGL::appendMenu( LLMenuGL* menu )
+{
+ if( menu == this )
+ {
+ LL_ERRS() << "** Attempt to attach menu to itself. This is certainly "
+ << "a logic error." << LL_ENDL;
+ }
+
+ bool success = true;
+
+ // *TODO: Hack! Fix this
+ LLMenuItemBranchDownGL::Params p;
+ p.name = menu->getName();
+ p.label = menu->getLabel();
+ p.visible = menu->getVisible();
+ p.branch = menu;
+ p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor");
+ p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor");
+ p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor");
+ p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor");
+ p.font = menu->getFont();
+
+ LLMenuItemBranchDownGL* branch = LLUICtrlFactory::create<LLMenuItemBranchDownGL>(p);
+ success &= branch->addToAcceleratorList(&mAccelerators);
+ success &= append( branch );
+ branch->setJumpKey(branch->getJumpKey());
+ menu->updateParent(LLMenuGL::sMenuContainer);
+
+ return success;
+}
+
+bool LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask )
+{
+ bool handled = false;
+ LLView* active_menu = NULL;
+
+ bool no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0;
+ S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX;
+ S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY;
+ mMouseVelX = (mMouseVelX / 2) + (mouse_delta_x / 2);
+ mMouseVelY = (mMouseVelY / 2) + (mouse_delta_y / 2);
+ mLastMouseX = x;
+ mLastMouseY = y;
+
+ // if nothing currently selected or mouse has moved since last call, pick menu item via mouse
+ // otherwise let keyboard control it
+ if (!getHighlightedItem() || !LLMenuGL::getKeyboardMode() || llabs(mMouseVelX) > 0 || llabs(mMouseVelY) > 0)
+ {
+ // find current active menu
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if (((LLMenuItemGL*)viewp)->isOpen())
+ {
+ active_menu = viewp;
+ }
+ }
+
+ // check for new active menu
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ if( viewp->getVisible() &&
+ viewp->getEnabled() &&
+ viewp->pointInView(local_x, local_y) &&
+ viewp->handleHover(local_x, local_y, mask))
+ {
+ ((LLMenuItemGL*)viewp)->setHighlight(true);
+ handled = true;
+ if (active_menu && active_menu != viewp)
+ {
+ ((LLMenuItemGL*)viewp)->onCommit();
+ LLMenuGL::setKeyboardMode(false);
+ }
+ LLMenuGL::setKeyboardMode(false);
+ }
+ }
+
+ if (handled)
+ {
+ // set hover false on inactive menus
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ if (!viewp->pointInView(local_x, local_y) && ((LLMenuItemGL*)viewp)->getHighlight())
+ {
+ ((LLMenuItemGL*)viewp)->setHighlight(false);
+ }
+ }
+ }
+ }
+
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+
+ return true;
+}
+
+///============================================================================
+/// Class LLMenuHolderGL
+///============================================================================
+LLCoordGL LLMenuHolderGL::sContextMenuSpawnPos(S32_MAX, S32_MAX);
+
+LLMenuHolderGL::LLMenuHolderGL(const LLMenuHolderGL::Params& p)
+ : LLPanel(p)
+{
+ sItemActivationTimer.stop();
+ mCanHide = true;
+}
+
+void LLMenuHolderGL::draw()
+{
+ LLView::draw();
+ // now draw last selected item as overlay
+ LLMenuItemGL* selecteditem = (LLMenuItemGL*)sItemLastSelectedHandle.get();
+ if (selecteditem && selecteditem->getVisible() && sItemActivationTimer.getStarted() && sItemActivationTimer.getElapsedTimeF32() < ACTIVATE_HIGHLIGHT_TIME)
+ {
+ // make sure toggle items, for example, show the proper state when fading out
+ selecteditem->buildDrawLabel();
+
+ LLRect item_rect;
+ selecteditem->localRectToOtherView(selecteditem->getLocalRect(), &item_rect, this);
+
+ F32 interpolant = sItemActivationTimer.getElapsedTimeF32() / ACTIVATE_HIGHLIGHT_TIME;
+
+ LLUI::pushMatrix();
+ {
+ LLUI::translate((F32)item_rect.mLeft, (F32)item_rect.mBottom);
+ selecteditem->getMenu()->drawBackground(selecteditem, interpolant);
+ selecteditem->draw();
+ }
+ LLUI::popMatrix();
+ }
+}
+
+bool LLMenuHolderGL::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ bool handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
+ if (!handled)
+ {
+ LLMenuGL* visible_menu = (LLMenuGL*)getVisibleMenu();
+ LLMenuItemGL* parent_menu = visible_menu ? visible_menu->getParentMenuItem() : NULL;
+ if (parent_menu && parent_menu->getVisible())
+ {
+ // don't hide menu if parent was hit
+ LLRect parent_rect;
+ parent_menu->localRectToOtherView(parent_menu->getLocalRect(), &parent_rect, this);
+ if (!parent_rect.pointInRect(x, y))
+ {
+ // clicked off of menu and parent, hide them all
+ hideMenus();
+ }
+ }
+ else
+ {
+ // no visible parent, clicked off of menu, hide them all
+ hideMenus();
+ }
+ }
+ return handled;
+}
+
+bool LLMenuHolderGL::handleRightMouseDown( S32 x, S32 y, MASK mask )
+{
+ bool handled = LLView::childrenHandleRightMouseDown(x, y, mask) != NULL;
+ if (!handled)
+ {
+ // clicked off of menu, hide them all
+ hideMenus();
+ }
+ return handled;
+}
+
+// This occurs when you mouse-down to spawn a context menu, hold the button
+// down, move off the menu, then mouse-up. We want this to close the menu.
+bool LLMenuHolderGL::handleRightMouseUp( S32 x, S32 y, MASK mask )
+{
+ const S32 SLOP = 2;
+ S32 spawn_dx = (x - sContextMenuSpawnPos.mX);
+ S32 spawn_dy = (y - sContextMenuSpawnPos.mY);
+ if (-SLOP <= spawn_dx && spawn_dx <= SLOP
+ && -SLOP <= spawn_dy && spawn_dy <= SLOP)
+ {
+ // we're still inside the slop region from spawning this menu
+ // so interpret the mouse-up as a single-click to show and leave on
+ // screen
+ sContextMenuSpawnPos.set(S32_MAX, S32_MAX);
+ return true;
+ }
+
+ bool handled = LLView::childrenHandleRightMouseUp(x, y, mask) != NULL;
+ if (!handled)
+ {
+ // clicked off of menu, hide them all
+ hideMenus();
+ }
+ return handled;
+}
+
+bool LLMenuHolderGL::handleKey(KEY key, MASK mask, bool called_from_parent)
+{
+ bool handled = false;
+ LLMenuGL* const pMenu = dynamic_cast<LLMenuGL*>(getVisibleMenu());
+
+ if (pMenu)
+ {
+ //eat TAB key - EXT-7000
+ if (key == KEY_TAB && mask == MASK_NONE)
+ {
+ return true;
+ }
+
+ //handle ESCAPE and RETURN key
+ handled = LLPanel::handleKey(key, mask, called_from_parent);
+ if (!handled)
+ {
+ if (pMenu->getHighlightedItem())
+ {
+ handled = pMenu->handleKey(key, mask, true);
+ }
+ else if (mask == MASK_NONE || (key >= KEY_LEFT && key <= KEY_DOWN))
+ {
+ //highlight first enabled one
+ if(pMenu->highlightNextItem(NULL))
+ {
+ handled = true;
+ }
+ }
+ }
+ }
+
+ return handled;
+
+}
+
+void LLMenuHolderGL::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ if (width != getRect().getWidth() || height != getRect().getHeight())
+ {
+ hideMenus();
+ }
+ LLView::reshape(width, height, called_from_parent);
+}
+
+LLView* const LLMenuHolderGL::getVisibleMenu() const
+{
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if (viewp->getVisible() && dynamic_cast<LLMenuGL*>(viewp) != NULL)
+ {
+ return viewp;
+ }
+ }
+ return NULL;
+}
+
+
+bool LLMenuHolderGL::hideMenus()
+{
+ if (!mCanHide)
+ {
+ return false;
+ }
+ LLMenuGL::setKeyboardMode(false);
+ bool menu_visible = hasVisibleMenu();
+ if (menu_visible)
+ {
+ // clicked off of menu, hide them all
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if (dynamic_cast<LLMenuGL*>(viewp) != NULL && viewp->getVisible())
+ {
+ viewp->setVisible(false);
+ }
+ }
+ }
+ //if (gFocusMgr.childHasKeyboardFocus(this))
+ //{
+ // gFocusMgr.setKeyboardFocus(NULL);
+ //}
+
+ return menu_visible;
+}
+
+void LLMenuHolderGL::setActivatedItem(LLMenuItemGL* item)
+{
+ sItemLastSelectedHandle = item->getHandle();
+ sItemActivationTimer.start();
+}
+
+///============================================================================
+/// Class LLTearOffMenu
+///============================================================================
+LLTearOffMenu::LLTearOffMenu(LLMenuGL* menup) :
+ LLFloater(LLSD()),
+ mQuitRequested(false)
+{
+ S32 floater_header_size = getHeaderHeight();
+
+ setName(menup->getName());
+ setTitle(menup->getLabel());
+ setCanMinimize(false);
+ // flag menu as being torn off
+ menup->setTornOff(true);
+ // update menu layout as torn off menu (no spillover menus)
+ menup->needsArrange();
+
+ LLRect rect;
+ menup->localRectToOtherView(LLRect(-1, menup->getRect().getHeight(), menup->getRect().getWidth() + 3, 0), &rect, gFloaterView);
+ // make sure this floater is big enough for menu
+ mTargetHeight = rect.getHeight() + floater_header_size;
+ reshape(rect.getWidth(), rect.getHeight());
+ setRect(rect);
+
+ // attach menu to floater
+ menup->setFollows( FOLLOWS_LEFT | FOLLOWS_BOTTOM );
+ mOldParent = menup->getParent();
+ addChild(menup);
+ menup->setVisible(true);
+ LLRect menu_rect = menup->getRect();
+ menu_rect.setOriginAndSize( 1, 1,
+ menu_rect.getWidth(), menu_rect.getHeight());
+ menup->setRect(menu_rect);
+ menup->setDropShadowed(false);
+
+ mMenu = menup;
+
+ // highlight first item (tear off item will be disabled)
+ mMenu->highlightNextItem(NULL);
+
+ // Can't do this in postBuild() because that is only called for floaters
+ // constructed from XML.
+ mCloseSignal.connect(boost::bind(&LLTearOffMenu::closeTearOff, this));
+}
+
+LLTearOffMenu::~LLTearOffMenu()
+{
+}
+
+void LLTearOffMenu::draw()
+{
+ mMenu->setBackgroundVisible(isBackgroundOpaque());
+
+ if (getRect().getHeight() != mTargetHeight)
+ {
+ // animate towards target height
+ reshape(getRect().getWidth(), llceil(lerp((F32)getRect().getHeight(), (F32)mTargetHeight, LLSmoothInterpolation::getInterpolant(0.05f))));
+ }
+ mMenu->needsArrange();
+ LLFloater::draw();
+}
+
+void LLTearOffMenu::onFocusReceived()
+{
+ if (mQuitRequested)
+ {
+ return;
+ }
+
+ // if nothing is highlighted, just highlight first item
+ if (!mMenu->getHighlightedItem())
+ {
+ mMenu->highlightNextItem(NULL);
+ }
+
+ // parent menu items get highlights so navigation logic keeps working
+ LLMenuItemGL* parent_menu_item = mMenu->getParentMenuItem();
+ while(parent_menu_item)
+ {
+ if (parent_menu_item->getMenu()->getVisible())
+ {
+ parent_menu_item->setHighlight(true);
+ parent_menu_item = parent_menu_item->getMenu()->getParentMenuItem();
+ }
+ else
+ {
+ break;
+ }
+ }
+ LLFloater::onFocusReceived();
+}
+
+void LLTearOffMenu::onFocusLost()
+{
+ // remove highlight from parent item and our own menu
+ mMenu->clearHoverItem();
+ LLFloater::onFocusLost();
+}
+
+bool LLTearOffMenu::handleUnicodeChar(llwchar uni_char, bool called_from_parent)
+{
+ // pass keystrokes down to menu
+ return mMenu->handleUnicodeChar(uni_char, true);
+}
+
+bool LLTearOffMenu::handleKeyHere(KEY key, MASK mask)
+{
+ if (!mMenu->getHighlightedItem())
+ {
+ if (key == KEY_UP)
+ {
+ mMenu->highlightPrevItem(NULL);
+ return true;
+ }
+ else if (key == KEY_DOWN)
+ {
+ mMenu->highlightNextItem(NULL);
+ return true;
+ }
+ }
+ // pass keystrokes down to menu
+ return mMenu->handleKey(key, mask, true);
+}
+
+void LLTearOffMenu::translate(S32 x, S32 y)
+{
+ if (x != 0 && y != 0)
+ {
+ // hide open sub-menus by clearing current hover item
+ mMenu->clearHoverItem();
+ }
+ LLFloater::translate(x, y);
+}
+
+//static
+LLTearOffMenu* LLTearOffMenu::create(LLMenuGL* menup)
+{
+ LLTearOffMenu* tearoffp = new LLTearOffMenu(menup);
+ // keep onscreen
+ gFloaterView->adjustToFitScreen(tearoffp, false);
+ tearoffp->openFloater(LLSD());
+
+ return tearoffp;
+}
+
+void LLTearOffMenu::updateSize()
+{
+ if (mMenu)
+ {
+ S32 floater_header_size = getHeaderHeight();
+ const LLRect &floater_rect = getRect();
+ LLRect new_rect;
+ mMenu->localRectToOtherView(LLRect(-1, mMenu->getRect().getHeight() + floater_header_size, mMenu->getRect().getWidth() + 3, 0), &new_rect, gFloaterView);
+
+ if (floater_rect.getWidth() != new_rect.getWidth()
+ || mTargetHeight != new_rect.getHeight())
+ {
+ // make sure this floater is big enough for menu
+ mTargetHeight = new_rect.getHeight();
+ reshape(new_rect.getWidth(), mTargetHeight);
+
+ // Restore menu position
+ LLRect menu_rect = mMenu->getRect();
+ menu_rect.setOriginAndSize(1, 1,
+ menu_rect.getWidth(), menu_rect.getHeight());
+ mMenu->setRect(menu_rect);
+ }
+ }
+}
+
+void LLTearOffMenu::closeTearOff()
+{
+ removeChild(mMenu);
+ mOldParent->addChild(mMenu);
+ mMenu->clearHoverItem();
+ mMenu->setFollowsNone();
+ mMenu->setBackgroundVisible(true);
+ mMenu->setVisible(false);
+ mMenu->setTornOff(false);
+ mMenu->setDropShadowed(true);
+ mQuitRequested = true;
+}
+
+LLContextMenuBranch::LLContextMenuBranch(const LLContextMenuBranch::Params& p)
+: LLMenuItemGL(p)
+{
+ LLContextMenu* branch = static_cast<LLContextMenu*>(p.branch);
+ if (branch)
+ {
+ mBranch = branch->getHandle();
+ branch->hide();
+ branch->setParentMenuItem(this);
+ }
+}
+
+LLContextMenuBranch::~LLContextMenuBranch()
+{
+ if (mBranch.get())
+ {
+ mBranch.get()->die();
+ }
+}
+
+// called to rebuild the draw label
+void LLContextMenuBranch::buildDrawLabel( void )
+{
+ auto menu = getBranch();
+ if (menu)
+ {
+ // default enablement is this -- if any of the subitems are
+ // enabled, this item is enabled. JC
+ U32 sub_count = menu->getItemCount();
+ U32 i;
+ bool any_enabled = false;
+ for (i = 0; i < sub_count; i++)
+ {
+ LLMenuItemGL* item = menu->getItem(i);
+ item->buildDrawLabel();
+ if (item->getEnabled() && !item->getDrawTextDisabled() )
+ {
+ any_enabled = true;
+ break;
+ }
+ }
+ setDrawTextDisabled(!any_enabled);
+ setEnabled(true);
+ }
+
+ mDrawAccelLabel.clear();
+ std::string st = mDrawAccelLabel;
+ appendAcceleratorString( st );
+ mDrawAccelLabel = st;
+
+ mDrawBranchLabel = LLMenuGL::BRANCH_SUFFIX;
+}
+
+void LLContextMenuBranch::showSubMenu()
+{
+ auto menu = getBranch();
+ if(menu)
+ {
+ LLMenuItemGL* menu_item = menu->getParentMenuItem();
+ if (menu_item != NULL && menu_item->getVisible())
+ {
+ S32 center_x;
+ S32 center_y;
+ localPointToScreen(getRect().getWidth(), getRect().getHeight(), ¢er_x, ¢er_y);
+ menu->show(center_x, center_y);
+ }
+ }
+}
+
+// onCommit() - do the primary funcationality of the menu item.
+void LLContextMenuBranch::onCommit( void )
+{
+ showSubMenu();
+
+}
+void LLContextMenuBranch::setHighlight( bool highlight )
+{
+ if (highlight == getHighlight()) return;
+ LLMenuItemGL::setHighlight(highlight);
+ auto menu = getBranch();
+ if (menu)
+ {
+ if (highlight)
+ {
+ showSubMenu();
+ }
+ else
+ {
+ menu->hide();
+ }
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//-----------------------------------------------------------------------------
+// class LLContextMenu
+// A context menu
+//-----------------------------------------------------------------------------
+static LLDefaultChildRegistry::Register<LLContextMenu> context_menu_register("context_menu");
+static MenuRegistry::Register<LLContextMenu> context_menu_register2("context_menu");
+
+
+LLContextMenu::LLContextMenu(const Params& p)
+: LLMenuGL(p),
+ mHoveredAnyItem(false),
+ mHoverItem(NULL)
+{
+ //setBackgroundVisible(true);
+}
+
+void LLContextMenu::setVisible(bool visible)
+{
+ if (!visible)
+ hide();
+}
+
+// Takes cursor position in screen space?
+void LLContextMenu::show(S32 x, S32 y, LLView* spawning_view)
+{
+ if (getChildList()->empty())
+ {
+ // nothing to show, so abort
+ return;
+ }
+ // Save click point for detecting cursor moves before mouse-up.
+ // Must be in local coords to compare with mouseUp events.
+ // If the mouse doesn't move, the menu will stay open ala the Mac.
+ // See also LLMenuGL::showPopup()
+ LLMenuHolderGL::sContextMenuSpawnPos.set(x,y);
+
+ arrangeAndClear();
+
+ S32 width = getRect().getWidth();
+ S32 height = getRect().getHeight();
+ const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect();
+ LLView* parent_view = getParent();
+
+ // Open upwards if menu extends past bottom
+ if (y - height < menu_region_rect.mBottom)
+ {
+ if (getParentMenuItem()) // Adjust if this is a submenu
+ {
+ y += height - getParentMenuItem()->getNominalHeight();
+ }
+ else
+ {
+ y += height;
+ }
+ }
+
+ // Open out to the left if menu extends past right edge
+ if (x + width > menu_region_rect.mRight)
+ {
+ if (getParentMenuItem())
+ {
+ x -= getParentMenuItem()->getRect().getWidth() + width;
+ }
+ else
+ {
+ x -= width;
+ }
+ }
+
+ S32 local_x, local_y;
+ parent_view->screenPointToLocal(x, y, &local_x, &local_y);
+
+ LLRect rect;
+ rect.setLeftTopAndSize(local_x, local_y, width, height);
+ setRect(rect);
+ arrange();
+
+ if (spawning_view)
+ {
+ mSpawningViewHandle = spawning_view->getHandle();
+ }
+ else
+ {
+ mSpawningViewHandle.markDead();
+ }
+ LLView::setVisible(true);
+}
+
+void LLContextMenu::hide()
+{
+ if (!getVisible()) return;
+
+ LLView::setVisible(false);
+
+ if (mHoverItem)
+ {
+ mHoverItem->setHighlight( false );
+ }
+ mHoverItem = NULL;
+}
+
+
+bool LLContextMenu::handleHover( S32 x, S32 y, MASK mask )
+{
+ LLMenuGL::handleHover(x,y,mask);
+
+ bool handled = false;
+
+ LLMenuItemGL *item = getHighlightedItem();
+
+ if (item && item->getEnabled())
+ {
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ handled = true;
+
+ if (item != mHoverItem)
+ {
+ if (mHoverItem)
+ {
+ mHoverItem->setHighlight( false );
+ }
+ mHoverItem = item;
+ mHoverItem->setHighlight( true );
+ }
+ mHoveredAnyItem = true;
+ }
+ else
+ {
+ // clear out our selection
+ if (mHoverItem)
+ {
+ mHoverItem->setHighlight(false);
+ mHoverItem = NULL;
+ }
+ }
+
+ if( !handled && pointInView( x, y ) )
+ {
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ handled = true;
+ }
+
+ return handled;
+}
+
+// handleMouseDown and handleMouseUp are handled by LLMenuGL
+
+
+bool LLContextMenu::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ bool handled = false;
+
+ // The click was somewhere within our rectangle
+ LLMenuItemGL *item = getHighlightedItem();
+
+ S32 local_x = x - getRect().mLeft;
+ S32 local_y = y - getRect().mBottom;
+
+ bool clicked_in_menu = pointInView(local_x, local_y) ;
+
+ // grab mouse if right clicking anywhere within pie (even deadzone in middle), to detect drag outside of pie
+ if (clicked_in_menu)
+ {
+ // capture mouse cursor as if on initial menu show
+ handled = true;
+ }
+
+ if (item)
+ {
+ // lie to the item about where the click happened
+ // to make sure it's within the item's rectangle
+ if (item->handleMouseDown( 0, 0, mask ))
+ {
+ handled = true;
+ }
+ }
+
+ return handled;
+}
+
+bool LLContextMenu::handleRightMouseUp( S32 x, S32 y, MASK mask )
+{
+ S32 local_x = x - getRect().mLeft;
+ S32 local_y = y - getRect().mBottom;
+
+ if (!mHoveredAnyItem && !pointInView(local_x, local_y))
+ {
+ sMenuContainer->hideMenus();
+ return true;
+ }
+
+
+ bool result = handleMouseUp( x, y, mask );
+ mHoveredAnyItem = false;
+
+ return result;
+}
+
+bool LLContextMenu::addChild(LLView* view, S32 tab_group)
+{
+ return addContextChild(view, tab_group);
+}
+
|