PageRenderTime 162ms CodeModel.GetById 42ms app.highlight 106ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/llui/llmenugl.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2505 lines | 1881 code | 345 blank | 279 comment | 373 complexity | d76650c162abcb8c82071b5f2aa201c4 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/** 
   2 * @file llmenugl.cpp
   3 * @brief LLMenuItemGL base class
   4 *
   5 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
   6 * Second Life Viewer Source Code
   7 * Copyright (C) 2010, Linden Research, Inc.
   8 * 
   9 * This library is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public
  11 * License as published by the Free Software Foundation;
  12 * version 2.1 of the License only.
  13 * 
  14 * This library is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * Lesser General Public License for more details.
  18 * 
  19 * You should have received a copy of the GNU Lesser General Public
  20 * License along with this library; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  22 * 
  23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  24 * $/LicenseInfo$
  25 */
  26
  27//*****************************************************************************
  28//
  29// This file contains the opengl based menu implementation.
  30//
  31// NOTES: A menu label is split into 4 columns. The left column, the
  32// label colum, the accelerator column, and the right column. The left
  33// column is used for displaying boolean values for toggle and check
  34// controls. The right column is used for submenus.
  35//
  36//*****************************************************************************
  37
  38//#include "llviewerprecompiledheaders.h"
  39#include "linden_common.h"
  40
  41#include "llmenugl.h"
  42
  43#include "llgl.h"
  44#include "llmath.h"
  45#include "llrender.h"
  46#include "llfocusmgr.h"
  47#include "llcoord.h"
  48#include "llwindow.h"
  49#include "llcriticaldamp.h"
  50#include "lluictrlfactory.h"
  51
  52#include "llbutton.h"
  53#include "llfontgl.h"
  54#include "llresmgr.h"
  55#include "lltrans.h"
  56#include "llui.h"
  57
  58#include "llstl.h"
  59
  60#include "v2math.h"
  61#include <set>
  62#include <boost/tokenizer.hpp>
  63
  64// static
  65LLMenuHolderGL *LLMenuGL::sMenuContainer = NULL;
  66
  67S32 MENU_BAR_HEIGHT = 0;
  68S32 MENU_BAR_WIDTH = 0;
  69
  70///============================================================================
  71/// Local function declarations, constants, enums, and typedefs
  72///============================================================================
  73
  74const S32 LABEL_BOTTOM_PAD_PIXELS = 2;
  75
  76const U32 LEFT_PAD_PIXELS = 3;
  77const U32 LEFT_WIDTH_PIXELS = 15;
  78const U32 LEFT_PLAIN_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS;
  79
  80const U32 RIGHT_PAD_PIXELS = 2;
  81const U32 RIGHT_WIDTH_PIXELS = 15;
  82const U32 RIGHT_PLAIN_PIXELS = RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS;
  83
  84const U32 PLAIN_PAD_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS;
  85
  86const U32 BRIEF_PAD_PIXELS = 2;
  87
  88const U32 SEPARATOR_HEIGHT_PIXELS = 8;
  89const S32 TEAROFF_SEPARATOR_HEIGHT_PIXELS = 10;
  90const S32 MENU_ITEM_PADDING = 4;
  91
  92const std::string SEPARATOR_NAME("separator");
  93const std::string VERTICAL_SEPARATOR_LABEL( "|" );
  94
  95const std::string LLMenuGL::BOOLEAN_TRUE_PREFIX( "\xE2\x9C\x94" ); // U+2714 HEAVY CHECK MARK
  96const std::string LLMenuGL::BRANCH_SUFFIX( "\xE2\x96\xB6" ); // U+25B6 BLACK RIGHT-POINTING TRIANGLE
  97const std::string LLMenuGL::ARROW_UP  ("^^^^^^^");
  98const std::string LLMenuGL::ARROW_DOWN("vvvvvvv");
  99
 100const F32 MAX_MOUSE_SLOPE_SUB_MENU = 0.9f;
 101
 102const S32 PIE_GESTURE_ACTIVATE_DISTANCE = 10;
 103
 104BOOL LLMenuGL::sKeyboardMode = FALSE;
 105
 106LLHandle<LLView> LLMenuHolderGL::sItemLastSelectedHandle;
 107LLFrameTimer LLMenuHolderGL::sItemActivationTimer;
 108//LLColor4 LLMenuGL::sBackgroundColor( 0.8f, 0.8f, 0.0f, 1.0f );
 109
 110const S32 PIE_CENTER_SIZE = 20;		// pixels, radius of center hole
 111const F32 PIE_SCALE_FACTOR = 1.7f; // scale factor for pie menu when mouse is initially down
 112const F32 PIE_SHRINK_TIME = 0.2f; // time of transition between unbounded and bounded display of pie menu
 113
 114const F32 ACTIVATE_HIGHLIGHT_TIME = 0.3f;
 115
 116static MenuRegistry::Register<LLMenuItemGL> register_menu_item("menu_item");
 117static MenuRegistry::Register<LLMenuItemSeparatorGL> register_separator("menu_item_separator");
 118static MenuRegistry::Register<LLMenuItemCallGL> register_menu_item_call("menu_item_call");
 119static MenuRegistry::Register<LLMenuItemCheckGL> register_menu_item_check("menu_item_check");
 120// Created programmatically but we need to specify custom colors in xml
 121static MenuRegistry::Register<LLMenuItemTearOffGL> register_menu_item_tear_off("menu_item_tear_off");
 122static MenuRegistry::Register<LLMenuGL> register_menu("menu");
 123
 124static LLDefaultChildRegistry::Register<LLMenuGL> register_menu_default("menu");
 125
 126
 127
 128///============================================================================
 129/// Class LLMenuItemGL
 130///============================================================================
 131
 132LLMenuItemGL::Params::Params()
 133:	shortcut("shortcut"),
 134	jump_key("jump_key", KEY_NONE),
 135	use_mac_ctrl("use_mac_ctrl", false),
 136	allow_key_repeat("allow_key_repeat", false),
 137	rect("rect"),
 138	left("left"),
 139	top("top"),
 140	right("right"),
 141	bottom("bottom"),
 142	width("width"),
 143	height("height"),
 144	bottom_delta("bottom_delta"),
 145	left_delta("left_delta"),
 146	enabled_color("enabled_color"),
 147	disabled_color("disabled_color"),
 148	highlight_bg_color("highlight_bg_color"),
 149	highlight_fg_color("highlight_fg_color")
 150{	
 151	changeDefault(mouse_opaque, true);
 152}
 153
 154// Default constructor
 155LLMenuItemGL::LLMenuItemGL(const LLMenuItemGL::Params& p)
 156:	LLUICtrl(p),
 157	mJumpKey(p.jump_key),
 158	mAllowKeyRepeat(p.allow_key_repeat),
 159	mHighlight( FALSE ),
 160	mGotHover( FALSE ),
 161	mBriefItem( FALSE ),
 162	mDrawTextDisabled( FALSE ),
 163	mFont(p.font),
 164	mAcceleratorKey(KEY_NONE),
 165	mAcceleratorMask(MASK_NONE),
 166	mLabel(p.label.isProvided() ? p.label() : p.name()),
 167	mEnabledColor(p.enabled_color()),
 168	mDisabledColor(p.disabled_color()),
 169	mHighlightBackground(p.highlight_bg_color()),
 170	mHighlightForeground(p.highlight_fg_color())
 171{
 172#ifdef LL_DARWIN
 173	// See if this Mac accelerator should really use the ctrl key and not get mapped to cmd
 174	BOOL useMacCtrl = p.use_mac_ctrl;
 175#endif // LL_DARWIN
 176	
 177	std::string shortcut = p.shortcut;
 178	if (shortcut.find("control") != shortcut.npos)
 179	{
 180#ifdef LL_DARWIN
 181		if ( useMacCtrl )
 182		{
 183			mAcceleratorMask |= MASK_MAC_CONTROL;
 184		}
 185#endif // LL_DARWIN
 186		mAcceleratorMask |= MASK_CONTROL;
 187	}
 188	if (shortcut.find("alt") != shortcut.npos)
 189	{
 190		mAcceleratorMask |= MASK_ALT;
 191	}
 192	if (shortcut.find("shift") != shortcut.npos)
 193	{
 194		mAcceleratorMask |= MASK_SHIFT;
 195	}
 196	S32 pipe_pos = shortcut.rfind("|");
 197	std::string key_str = shortcut.substr(pipe_pos+1);
 198
 199	LLKeyboard::keyFromString(key_str, &mAcceleratorKey);
 200
 201	LL_DEBUGS("HotKeys") << "Process short cut key: shortcut: " << shortcut
 202		<< ", key str: " << key_str
 203		<< ", accelerator mask: " << mAcceleratorMask
 204		<< ", accelerator key: " << mAcceleratorKey
 205		<< LL_ENDL;
 206}
 207
 208//virtual
 209void LLMenuItemGL::setValue(const LLSD& value)
 210{
 211	setLabel(value.asString());
 212}
 213
 214//virtual
 215LLSD LLMenuItemGL::getValue() const
 216{
 217	return getLabel();
 218}
 219
 220//virtual
 221BOOL LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask)
 222{
 223	if( getEnabled() && (!gKeyboard->getKeyRepeated(key) || mAllowKeyRepeat) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) )
 224	{
 225		onCommit();
 226		return TRUE;
 227	}
 228	return FALSE;
 229}
 230
 231BOOL LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask)
 232{
 233	setHover(TRUE);
 234	getWindow()->setCursor(UI_CURSOR_ARROW);
 235	return TRUE;
 236}
 237
 238//virtual
 239BOOL LLMenuItemGL::handleRightMouseDown(S32 x, S32 y, MASK mask)
 240{
 241	return LLUICtrl::handleRightMouseDown(x,y,mask);
 242}
 243
 244//virtual
 245BOOL LLMenuItemGL::handleRightMouseUp(S32 x, S32 y, MASK mask)
 246{
 247	// If this event came from a right-click context menu spawn,
 248	// process as a left-click to allow menu items to be hit
 249	if (LLMenuHolderGL::sContextMenuSpawnPos.mX != S32_MAX
 250		|| LLMenuHolderGL::sContextMenuSpawnPos.mY != S32_MAX)
 251	{
 252		BOOL handled = handleMouseUp(x, y, mask);
 253		return handled;
 254	}
 255	return LLUICtrl::handleRightMouseUp(x,y,mask);
 256}
 257
 258// This function checks to see if the accelerator key is already in use;
 259// if not, it will be added to the list
 260BOOL LLMenuItemGL::addToAcceleratorList(std::list <LLKeyBinding*> *listp)
 261{
 262	LLKeyBinding *accelerator = NULL;
 263
 264	if (mAcceleratorKey != KEY_NONE)
 265	{
 266		std::list<LLKeyBinding*>::iterator list_it;
 267		for (list_it = listp->begin(); list_it != listp->end(); ++list_it)
 268		{
 269			accelerator = *list_it;
 270			if ((accelerator->mKey == mAcceleratorKey) && (accelerator->mMask == (mAcceleratorMask & MASK_NORMALKEYS)))
 271			{
 272
 273			// *NOTE: get calling code to throw up warning or route
 274			// warning messages back to app-provided output
 275			//	std::string warning;
 276			//	warning.append("Duplicate key binding <");
 277			//	appendAcceleratorString( warning );
 278			//	warning.append("> for menu items:\n    ");
 279			//	warning.append(accelerator->mName);
 280			//	warning.append("\n    ");
 281			//	warning.append(mLabel);
 282
 283			//	llwarns << warning << llendl;
 284			//	LLAlertDialog::modalAlert(warning);
 285				return FALSE;
 286			}
 287		}
 288		if (!accelerator)
 289		{				
 290			accelerator = new LLKeyBinding;
 291			if (accelerator)
 292			{
 293				accelerator->mKey = mAcceleratorKey;
 294				accelerator->mMask = (mAcceleratorMask & MASK_NORMALKEYS);
 295// 				accelerator->mName = mLabel;
 296			}
 297			listp->push_back(accelerator);//addData(accelerator);
 298		}
 299	}
 300	return TRUE;
 301}
 302
 303// This function appends the character string representation of
 304// the current accelerator key and mask to the provided string.
 305void LLMenuItemGL::appendAcceleratorString( std::string& st ) const
 306{
 307	st = LLKeyboard::stringFromAccelerator( mAcceleratorMask, mAcceleratorKey );
 308	LL_DEBUGS("HotKeys") << "appendAcceleratorString: " << st << LL_ENDL;
 309}
 310
 311void LLMenuItemGL::setJumpKey(KEY key)
 312{
 313	mJumpKey = LLStringOps::toUpper((char)key);
 314}
 315
 316
 317// virtual 
 318U32 LLMenuItemGL::getNominalHeight( void ) const 
 319{ 
 320	return llround(mFont->getLineHeight()) + MENU_ITEM_PADDING; 
 321}
 322
 323//virtual
 324void LLMenuItemGL::setBriefItem(BOOL brief)
 325{
 326	mBriefItem = brief;
 327}
 328
 329//virtual
 330BOOL LLMenuItemGL::isBriefItem() const
 331{
 332	return mBriefItem;
 333}
 334
 335// Get the parent menu for this item
 336LLMenuGL* LLMenuItemGL::getMenu() const
 337{
 338	return (LLMenuGL*) getParent();
 339}
 340
 341
 342// getNominalWidth() - returns the normal width of this control in
 343// pixels - this is used for calculating the widest item, as well as
 344// for horizontal arrangement.
 345U32 LLMenuItemGL::getNominalWidth( void ) const
 346{
 347	U32 width;
 348	
 349	if (mBriefItem)
 350	{
 351		width = BRIEF_PAD_PIXELS;
 352	}
 353	else
 354	{
 355		width = PLAIN_PAD_PIXELS;
 356	}
 357
 358	if( KEY_NONE != mAcceleratorKey )
 359	{
 360		width += getMenu()->getShortcutPad();
 361		std::string temp;
 362		appendAcceleratorString( temp );
 363		width += mFont->getWidth( temp );
 364	}
 365	width += mFont->getWidth( mLabel.getWString().c_str() );
 366	return width;
 367}
 368
 369// called to rebuild the draw label
 370void LLMenuItemGL::buildDrawLabel( void )
 371{
 372	mDrawAccelLabel.clear();
 373	std::string st = mDrawAccelLabel.getString();
 374	appendAcceleratorString( st );
 375	mDrawAccelLabel = st;
 376}
 377
 378void LLMenuItemGL::onCommit( void )
 379{
 380	// Check torn-off status to allow left-arrow keyboard navigation back
 381	// to parent menu.
 382	// Also, don't hide if item triggered by keyboard shortcut (and hence
 383	// parent not visible).
 384	if (!getMenu()->getTornOff() 
 385		&& getMenu()->getVisible())
 386	{
 387		LLMenuGL::sMenuContainer->hideMenus();
 388	}
 389	
 390	LLUICtrl::onCommit();
 391}
 392
 393// set the hover status (called by it's menu)
 394 void LLMenuItemGL::setHighlight( BOOL highlight )
 395{
 396	if (highlight)
 397	{
 398		getMenu()->clearHoverItem();
 399	}
 400
 401	if (mHighlight != highlight)
 402	{
 403		dirtyRect();
 404	}
 405
 406	mHighlight = highlight;
 407}
 408
 409
 410BOOL LLMenuItemGL::handleKeyHere( KEY key, MASK mask )
 411{
 412	if (getHighlight() && 
 413		getMenu()->isOpen())
 414	{
 415		if (key == KEY_UP)
 416		{
 417			// switch to keyboard navigation mode
 418			LLMenuGL::setKeyboardMode(TRUE);
 419
 420			getMenu()->highlightPrevItem(this);
 421			return TRUE;
 422		}
 423		else if (key == KEY_DOWN)
 424		{
 425			// switch to keyboard navigation mode
 426			LLMenuGL::setKeyboardMode(TRUE);
 427
 428			getMenu()->highlightNextItem(this);
 429			return TRUE;
 430		}
 431		else if (key == KEY_RETURN && mask == MASK_NONE)
 432		{
 433			// switch to keyboard navigation mode
 434			LLMenuGL::setKeyboardMode(TRUE);
 435
 436			onCommit();
 437			return TRUE;
 438		}
 439	}
 440
 441	return FALSE;
 442}
 443
 444BOOL LLMenuItemGL::handleMouseUp( S32 x, S32 y, MASK mask)
 445{
 446	// switch to mouse navigation mode
 447	LLMenuGL::setKeyboardMode(FALSE);
 448
 449	onCommit();
 450	make_ui_sound("UISndClickRelease");
 451	return LLView::handleMouseUp(x, y, mask);
 452}
 453
 454BOOL LLMenuItemGL::handleMouseDown( S32 x, S32 y, MASK mask)
 455{
 456	// switch to mouse navigation mode
 457	LLMenuGL::setKeyboardMode(FALSE);
 458
 459	setHighlight(TRUE);
 460	return LLView::handleMouseDown(x, y, mask);
 461}
 462
 463BOOL LLMenuItemGL::handleScrollWheel( S32 x, S32 y, S32 clicks )
 464{
 465	// If the menu is scrollable let it handle the wheel event.
 466	return !getMenu()->isScrollable();
 467}
 468
 469void LLMenuItemGL::draw( void )
 470{
 471	// *FIX: This can be optimized by using switches. Want to avoid
 472	// that until the functionality is finalized.
 473
 474	// HACK: Brief items don't highlight.  Pie menu takes care of it.  JC
 475	// let disabled items be highlighted, just don't draw them as such
 476	if( getEnabled() && getHighlight() && !mBriefItem)
 477	{
 478		int debug_count = 0;
 479		if (dynamic_cast<LLMenuItemCallGL*>(this))
 480			debug_count++;
 481		gGL.color4fv( mHighlightBackground.get().mV );
 482
 483		gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 );
 484	}
 485
 486	LLColor4 color;
 487
 488	if ( getEnabled() && getHighlight() )
 489	{
 490		color = mHighlightForeground.get();
 491	}
 492	else if( getEnabled() && !mDrawTextDisabled )
 493	{
 494		color = mEnabledColor.get();
 495	}
 496	else
 497	{
 498		color = mDisabledColor.get();
 499	}
 500
 501	// Draw the text on top.
 502	if (mBriefItem)
 503	{
 504		mFont->render( mLabel, 0, BRIEF_PAD_PIXELS / 2, 0, color,
 505					   LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL);
 506	}
 507	else
 508	{
 509		if( !mDrawBoolLabel.empty() )
 510		{
 511			mFont->render( mDrawBoolLabel.getWString(), 0, (F32)LEFT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
 512						   LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE );
 513		}
 514		mFont->render( mLabel.getWString(), 0, (F32)LEFT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
 515					   LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE );
 516		if( !mDrawAccelLabel.empty() )
 517		{
 518			mFont->render( mDrawAccelLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
 519						   LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE );
 520		}
 521		if( !mDrawBranchLabel.empty() )
 522		{
 523			mFont->render( mDrawBranchLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
 524						   LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE );
 525		}
 526	}
 527
 528	// underline "jump" key only when keyboard navigation has been initiated
 529	if (getMenu()->jumpKeysActive() && LLMenuGL::getKeyboardMode())
 530	{
 531		std::string upper_case_label = mLabel.getString();
 532		LLStringUtil::toUpper(upper_case_label);
 533		std::string::size_type offset = upper_case_label.find(mJumpKey);
 534		if (offset != std::string::npos)
 535		{
 536			S32 x_begin = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset);
 537			S32 x_end = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset + 1);
 538			gl_line_2d(x_begin, (MENU_ITEM_PADDING / 2) + 1, x_end, (MENU_ITEM_PADDING / 2) + 1);
 539		}
 540	}
 541
 542	// clear got hover every frame
 543	setHover(FALSE);
 544}
 545
 546BOOL LLMenuItemGL::setLabelArg( const std::string& key, const LLStringExplicit& text )
 547{
 548	mLabel.setArg(key, text);
 549	return TRUE;
 550}
 551
 552void LLMenuItemGL::handleVisibilityChange(BOOL new_visibility)
 553{
 554	if (getMenu())
 555	{
 556		getMenu()->needsArrange();
 557	}
 558	LLView::handleVisibilityChange(new_visibility);
 559}
 560
 561//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 562// Class LLMenuItemSeparatorGL
 563//
 564// This class represents a separator.
 565//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 566LLMenuItemSeparatorGL::Params::Params()
 567{
 568}
 569
 570LLMenuItemSeparatorGL::LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p) :
 571	LLMenuItemGL( p )
 572{
 573}
 574
 575//virtual
 576U32 LLMenuItemSeparatorGL::getNominalHeight( void ) const
 577{
 578	return SEPARATOR_HEIGHT_PIXELS;
 579}
 580
 581void LLMenuItemSeparatorGL::draw( void )
 582{
 583	gGL.color4fv( mDisabledColor.get().mV );
 584	const S32 y = getRect().getHeight() / 2;
 585	const S32 PAD = 6;
 586	gl_line_2d( PAD, y, getRect().getWidth() - PAD, y );
 587}
 588
 589BOOL LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask)
 590{
 591	LLMenuGL* parent_menu = getMenu();
 592	if (y > getRect().getHeight() / 2)
 593	{
 594		// the menu items are in the child list in bottom up order
 595		LLView* prev_menu_item = parent_menu->findNextSibling(this);
 596		return prev_menu_item ? prev_menu_item->handleMouseDown(x, prev_menu_item->getRect().getHeight(), mask) : FALSE;
 597	}
 598	else
 599	{
 600		LLView* next_menu_item = parent_menu->findPrevSibling(this);
 601		return next_menu_item ? next_menu_item->handleMouseDown(x, 0, mask) : FALSE;
 602	}
 603}
 604
 605BOOL LLMenuItemSeparatorGL::handleMouseUp(S32 x, S32 y, MASK mask) 
 606{
 607	LLMenuGL* parent_menu = getMenu();
 608	if (y > getRect().getHeight() / 2)
 609	{
 610		LLView* prev_menu_item = parent_menu->findNextSibling(this);
 611		return prev_menu_item ? prev_menu_item->handleMouseUp(x, prev_menu_item->getRect().getHeight(), mask) : FALSE;
 612	}
 613	else
 614	{
 615		LLView* next_menu_item = parent_menu->findPrevSibling(this);
 616		return next_menu_item ? next_menu_item->handleMouseUp(x, 0, mask) : FALSE;
 617	}
 618}
 619
 620BOOL LLMenuItemSeparatorGL::handleHover(S32 x, S32 y, MASK mask) 
 621{
 622	LLMenuGL* parent_menu = getMenu();
 623	if (y > getRect().getHeight() / 2)
 624	{
 625		parent_menu->highlightPrevItem(this, FALSE);
 626		return FALSE;
 627	}
 628	else
 629	{
 630		parent_menu->highlightNextItem(this, FALSE);
 631		return FALSE;
 632	}
 633}
 634
 635//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 636// Class LLMenuItemVerticalSeparatorGL
 637//
 638// This class represents a vertical separator.
 639//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 640
 641class LLMenuItemVerticalSeparatorGL
 642:	public LLMenuItemSeparatorGL
 643{
 644public:
 645	LLMenuItemVerticalSeparatorGL( void );
 646
 647	virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask) { return FALSE; }
 648};
 649
 650LLMenuItemVerticalSeparatorGL::LLMenuItemVerticalSeparatorGL( void )
 651{
 652	setLabel( VERTICAL_SEPARATOR_LABEL );
 653}
 654
 655//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 656// Class LLMenuItemTearOffGL
 657//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 658LLMenuItemTearOffGL::LLMenuItemTearOffGL(const LLMenuItemTearOffGL::Params& p) 
 659:	LLMenuItemGL(p)
 660{
 661}
 662
 663// Returns the first floater ancestor if there is one
 664LLFloater* LLMenuItemTearOffGL::getParentFloater()
 665{
 666	LLView* parent_view = getMenu();
 667
 668	while (parent_view)
 669	{
 670		if (dynamic_cast<LLFloater*>(parent_view))
 671		{
 672			return dynamic_cast<LLFloater*>(parent_view);
 673		}
 674
 675		bool parent_is_menu = dynamic_cast<LLMenuGL*>(parent_view) && !dynamic_cast<LLMenuBarGL*>(parent_view);
 676
 677		if (parent_is_menu)
 678		{
 679			// use menu parent
 680			parent_view =  dynamic_cast<LLMenuGL*>(parent_view)->getParentMenuItem();
 681		}
 682		else
 683		{
 684			// just use regular view parent
 685			parent_view = parent_view->getParent();
 686		}
 687	}
 688
 689	return NULL;
 690}
 691
 692void LLMenuItemTearOffGL::onCommit()
 693{
 694	if (getMenu()->getTornOff())
 695	{
 696		LLTearOffMenu* torn_off_menu = (LLTearOffMenu*)(getMenu()->getParent());
 697		torn_off_menu->closeFloater();
 698	}
 699	else
 700	{
 701		// transfer keyboard focus and highlight to first real item in list
 702		if (getHighlight())
 703		{
 704			getMenu()->highlightNextItem(this);
 705		}
 706
 707		getMenu()->needsArrange();
 708
 709		LLFloater* parent_floater = getParentFloater();
 710		LLFloater* tear_off_menu = LLTearOffMenu::create(getMenu());
 711
 712		if (tear_off_menu)
 713		{
 714			if (parent_floater)
 715			{
 716				parent_floater->addDependentFloater(tear_off_menu, FALSE);
 717			}
 718
 719			// give focus to torn off menu because it will have
 720			// been taken away when parent menu closes
 721			tear_off_menu->setFocus(TRUE);
 722		}
 723	}
 724	LLMenuItemGL::onCommit();
 725}
 726
 727void LLMenuItemTearOffGL::draw()
 728{
 729	// disabled items can be highlighted, but shouldn't render as such
 730	if( getEnabled() && getHighlight() && !isBriefItem())
 731	{
 732		gGL.color4fv( mHighlightBackground.get().mV );
 733		gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 );
 734	}
 735
 736	if (getEnabled())
 737	{
 738		gGL.color4fv( mEnabledColor.get().mV );
 739	}
 740	else
 741	{
 742		gGL.color4fv( mDisabledColor.get().mV );
 743	}
 744	const S32 y = getRect().getHeight() / 3;
 745	const S32 PAD = 6;
 746	gl_line_2d( PAD, y, getRect().getWidth() - PAD, y );
 747	gl_line_2d( PAD, y * 2, getRect().getWidth() - PAD, y * 2 );
 748}
 749
 750U32 LLMenuItemTearOffGL::getNominalHeight( void ) const 
 751{ 
 752	return TEAROFF_SEPARATOR_HEIGHT_PIXELS; 
 753}
 754
 755///============================================================================
 756/// Class LLMenuItemCallGL
 757///============================================================================
 758
 759LLMenuItemCallGL::LLMenuItemCallGL(const LLMenuItemCallGL::Params& p)
 760:	LLMenuItemGL(p)
 761{
 762}
 763
 764void LLMenuItemCallGL::initFromParams(const Params& p)
 765{
 766	if (p.on_visible.isProvided())
 767	{
 768		mVisibleSignal.connect(initEnableCallback(p.on_visible));
 769	}
 770	if (p.on_enable.isProvided())
 771	{
 772		setEnableCallback(initEnableCallback(p.on_enable));
 773		// Set the enabled control variable (for backwards compatability)
 774		if (p.on_enable.control_name.isProvided() && !p.on_enable.control_name().empty())
 775		{
 776			LLControlVariable* control = findControl(p.on_enable.control_name());
 777			if (control)
 778			{
 779				setEnabledControlVariable(control);
 780			}
 781		}
 782	}
 783	if (p.on_click.isProvided())
 784	{
 785		setCommitCallback(initCommitCallback(p.on_click));
 786	}
 787		
 788	LLUICtrl::initFromParams(p);
 789}
 790
 791void LLMenuItemCallGL::onCommit( void )
 792{
 793	// RN: menu item can be deleted in callback, so beware
 794	getMenu()->setItemLastSelected( this );
 795	
 796	LLMenuItemGL::onCommit();
 797}
 798
 799void LLMenuItemCallGL::updateEnabled( void )
 800{
 801	if (mEnableSignal.num_slots() > 0)
 802	{
 803		bool enabled = mEnableSignal(this, LLSD());
 804		if (mEnabledControlVariable)
 805		{
 806			if (!enabled)
 807			{
 808				// callback overrides control variable; this will call setEnabled()
 809				mEnabledControlVariable->set(false); 
 810			}
 811		}
 812		else
 813		{
 814			setEnabled(enabled);
 815		}
 816	}
 817}
 818
 819void LLMenuItemCallGL::updateVisible( void )
 820{
 821	if (mVisibleSignal.num_slots() > 0)
 822	{
 823		bool visible = mVisibleSignal(this, LLSD());
 824		setVisible(visible);
 825	}
 826}
 827
 828void LLMenuItemCallGL::buildDrawLabel( void )
 829{
 830	updateEnabled();
 831	updateVisible();
 832	LLMenuItemGL::buildDrawLabel();
 833}
 834
 835BOOL LLMenuItemCallGL::handleKeyHere( KEY key, MASK mask )
 836{
 837	return LLMenuItemGL::handleKeyHere(key, mask);
 838}
 839
 840BOOL LLMenuItemCallGL::handleAcceleratorKey( KEY key, MASK mask )
 841{
 842	if( (!gKeyboard->getKeyRepeated(key) || getAllowKeyRepeat()) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) )
 843	{
 844		updateEnabled();
 845		if (getEnabled())
 846		{
 847			onCommit();
 848			return TRUE;
 849		}
 850	}
 851	return FALSE;
 852}
 853
 854// handleRightMouseUp moved into base class LLMenuItemGL so clicks are
 855// handled for all menu item types
 856
 857///============================================================================
 858/// Class LLMenuItemCheckGL
 859///============================================================================
 860LLMenuItemCheckGL::LLMenuItemCheckGL (const LLMenuItemCheckGL::Params& p)
 861:	LLMenuItemCallGL(p)
 862{
 863}
 864
 865void LLMenuItemCheckGL::initFromParams(const Params& p)
 866{
 867	if (p.on_check.isProvided())
 868	{
 869		setCheckCallback(initEnableCallback(p.on_check));
 870		// Set the control name (for backwards compatability)
 871		if (p.on_check.control_name.isProvided() && !p.on_check.control_name().empty())
 872		{
 873			setControlName(p.on_check.control_name());
 874		}
 875	}
 876		
 877	LLMenuItemCallGL::initFromParams(p);
 878}
 879
 880void LLMenuItemCheckGL::onCommit( void )
 881{
 882	LLMenuItemCallGL::onCommit();
 883}
 884
 885//virtual
 886void LLMenuItemCheckGL::setValue(const LLSD& value)
 887{
 888	LLUICtrl::setValue(value);
 889	if(value.asBoolean())
 890	{
 891		mDrawBoolLabel = LLMenuGL::BOOLEAN_TRUE_PREFIX;
 892	}
 893	else
 894	{
 895		mDrawBoolLabel.clear();
 896	}
 897}
 898
 899//virtual
 900LLSD LLMenuItemCheckGL::getValue() const
 901{
 902	// Get our boolean value from the view model.
 903	// If we don't override this method then the implementation from
 904	// LLMenuItemGL will return a string. (EXT-8501)
 905	return LLUICtrl::getValue();
 906}
 907
 908// called to rebuild the draw label
 909void LLMenuItemCheckGL::buildDrawLabel( void )
 910{
 911	// Note: mCheckSignal() returns true if no callbacks are set
 912	bool checked = mCheckSignal(this, LLSD());
 913	if (mControlVariable)
 914	{
 915		if (!checked) 
 916			setControlValue(false); // callback overrides control variable; this will call setValue()
 917	}
 918	else
 919	{
 920		setValue(checked);
 921	}
 922	if(getValue().asBoolean())
 923	{
 924		mDrawBoolLabel = LLMenuGL::BOOLEAN_TRUE_PREFIX;
 925	}
 926	else
 927	{
 928		mDrawBoolLabel.clear();
 929	}
 930	LLMenuItemCallGL::buildDrawLabel();
 931}
 932
 933///============================================================================
 934/// Class LLMenuItemBranchGL
 935///============================================================================
 936LLMenuItemBranchGL::LLMenuItemBranchGL(const LLMenuItemBranchGL::Params& p)
 937  : LLMenuItemGL(p)
 938{
 939	LLMenuGL* branch = p.branch;
 940	if (branch)
 941	{
 942		mBranchHandle = branch->getHandle();
 943		branch->setVisible(FALSE);
 944		branch->setParentMenuItem(this);
 945	}
 946}
 947
 948LLMenuItemBranchGL::~LLMenuItemBranchGL()
 949{
 950	if (mBranchHandle.get())
 951	{
 952		mBranchHandle.get()->die();
 953	}
 954}
 955
 956
 957
 958// virtual
 959LLView* LLMenuItemBranchGL::getChildView(const std::string& name, BOOL recurse) const
 960{
 961	LLMenuGL* branch = getBranch();
 962	if (branch)
 963	{
 964		if (branch->getName() == name)
 965		{
 966			return branch;
 967		}
 968
 969		// Always recurse on branches
 970		return branch->getChildView(name, recurse);
 971	}
 972
 973	return LLView::getChildView(name, recurse);
 974}
 975
 976LLView* LLMenuItemBranchGL::findChildView(const std::string& name, BOOL recurse) const
 977{
 978	LLMenuGL* branch = getBranch();
 979	if (branch)
 980	{
 981		if (branch->getName() == name)
 982		{
 983			return branch;
 984		}
 985
 986		// Always recurse on branches
 987		return branch->findChildView(name, recurse);
 988	}
 989
 990	return LLView::findChildView(name, recurse);
 991}
 992
 993// virtual
 994BOOL LLMenuItemBranchGL::handleMouseUp(S32 x, S32 y, MASK mask)
 995{
 996	// switch to mouse navigation mode
 997	LLMenuGL::setKeyboardMode(FALSE);
 998
 999	onCommit();
1000	make_ui_sound("UISndClickRelease");
1001	return TRUE;
1002}
1003
1004BOOL LLMenuItemBranchGL::handleAcceleratorKey(KEY key, MASK mask)
1005{
1006	return getBranch() && getBranch()->handleAcceleratorKey(key, mask);
1007}
1008
1009// This function checks to see if the accelerator key is already in use;
1010// if not, it will be added to the list
1011BOOL LLMenuItemBranchGL::addToAcceleratorList(std::list<LLKeyBinding*> *listp)
1012{
1013	LLMenuGL* branch = getBranch();
1014	if (!branch)
1015		return FALSE;
1016
1017	U32 item_count = branch->getItemCount();
1018	LLMenuItemGL *item;
1019	
1020	while (item_count--)
1021	{
1022		if ((item = branch->getItem(item_count)))
1023		{
1024			return item->addToAcceleratorList(listp);
1025		}
1026	}
1027
1028	return FALSE;
1029}
1030
1031
1032// called to rebuild the draw label
1033void LLMenuItemBranchGL::buildDrawLabel( void )
1034{
1035	mDrawAccelLabel.clear();
1036	std::string st = mDrawAccelLabel;
1037	appendAcceleratorString( st );
1038	mDrawAccelLabel = st;
1039	mDrawBranchLabel = LLMenuGL::BRANCH_SUFFIX;
1040}
1041
1042void LLMenuItemBranchGL::onCommit( void )
1043{
1044	openMenu();
1045
1046	// keyboard navigation automatically propagates highlight to sub-menu
1047	// to facilitate fast menu control via jump keys
1048	if (LLMenuGL::getKeyboardMode() && getBranch()&& !getBranch()->getHighlightedItem())
1049	{
1050		getBranch()->highlightNextItem(NULL);
1051	}
1052	
1053	LLUICtrl::onCommit();
1054}
1055
1056BOOL LLMenuItemBranchGL::handleKey(KEY key, MASK mask, BOOL called_from_parent)
1057{
1058	BOOL handled = FALSE;
1059	if (getBranch() && called_from_parent)
1060	{
1061		handled = getBranch()->handleKey(key, mask, called_from_parent);
1062	}
1063
1064	if (!handled)
1065	{
1066		handled = LLMenuItemGL::handleKey(key, mask, called_from_parent);
1067	}
1068
1069	return handled;
1070}
1071
1072BOOL LLMenuItemBranchGL::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent)
1073{
1074	BOOL handled = FALSE;
1075	if (getBranch() && called_from_parent)
1076	{
1077		handled = getBranch()->handleUnicodeChar(uni_char, TRUE);
1078	}
1079
1080	if (!handled)
1081	{
1082		handled = LLMenuItemGL::handleUnicodeChar(uni_char, called_from_parent);
1083	}
1084
1085	return handled;
1086}
1087
1088
1089void LLMenuItemBranchGL::setHighlight( BOOL highlight )
1090{
1091	if (highlight == getHighlight())
1092		return;
1093
1094	LLMenuGL* branch = getBranch();
1095	if (!branch)
1096		return;
1097
1098	BOOL auto_open = getEnabled() && (!branch->getVisible() || branch->getTornOff());
1099	// torn off menus don't open sub menus on hover unless they have focus
1100	if (getMenu()->getTornOff() && !((LLFloater*)getMenu()->getParent())->hasFocus())
1101	{
1102		auto_open = FALSE;
1103	}
1104	// don't auto open torn off sub-menus (need to explicitly active menu item to give them focus)
1105	if (branch->getTornOff())
1106	{
1107		auto_open = FALSE;
1108	}
1109	LLMenuItemGL::setHighlight(highlight);
1110	if( highlight )
1111	{
1112		if(auto_open)
1113		{
1114			openMenu();
1115		}
1116	}
1117	else
1118	{
1119		if (branch->getTornOff())
1120		{
1121			((LLFloater*)branch->getParent())->setFocus(FALSE);
1122			branch->clearHoverItem();
1123		}
1124		else
1125		{
1126			branch->setVisible( FALSE );
1127		}
1128	}
1129}
1130
1131void LLMenuItemBranchGL::draw()
1132{
1133	LLMenuItemGL::draw();
1134	if (getBranch() && getBranch()->getVisible() && !getBranch()->getTornOff())
1135	{
1136		setHighlight(TRUE);
1137	}
1138}
1139
1140void LLMenuItemBranchGL::updateBranchParent(LLView* parentp)
1141{
1142	if (getBranch() && getBranch()->getParent() == NULL)
1143	{
1144		// make the branch menu a sibling of my parent menu
1145		getBranch()->updateParent(parentp);
1146	}
1147}
1148
1149void LLMenuItemBranchGL::handleVisibilityChange( BOOL new_visibility )
1150{
1151	if (new_visibility == FALSE && getBranch() && !getBranch()->getTornOff())
1152	{
1153		getBranch()->setVisible(FALSE);
1154	}
1155	LLMenuItemGL::handleVisibilityChange(new_visibility);
1156}
1157
1158BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask )
1159{
1160	LLMenuGL* branch = getBranch();
1161	if (!branch)
1162		return LLMenuItemGL::handleKeyHere(key, mask);
1163
1164	// an item is highlighted, my menu is open, and I have an active sub menu or we are in
1165	// keyboard navigation mode
1166	if (getHighlight() 
1167		&& getMenu()->isOpen() 
1168		&& (isActive() || LLMenuGL::getKeyboardMode()))
1169	{
1170		if (branch->getVisible() && key == KEY_LEFT)
1171		{
1172			// switch to keyboard navigation mode
1173			LLMenuGL::setKeyboardMode(TRUE);
1174
1175			BOOL handled = branch->clearHoverItem();
1176			if (branch->getTornOff())
1177			{
1178				((LLFloater*)branch->getParent())->setFocus(FALSE);
1179			}
1180			if (handled && getMenu()->getTornOff())
1181			{
1182				((LLFloater*)getMenu()->getParent())->setFocus(TRUE);
1183			}
1184			return handled;
1185		}
1186
1187		if (key == KEY_RIGHT && !branch->getHighlightedItem())
1188		{
1189			// switch to keyboard navigation mode
1190			LLMenuGL::setKeyboardMode(TRUE);
1191
1192			LLMenuItemGL* itemp = branch->highlightNextItem(NULL);
1193			if (itemp)
1194			{
1195				return TRUE;
1196			}
1197		}
1198	}
1199	return LLMenuItemGL::handleKeyHere(key, mask);
1200}
1201
1202//virtual
1203BOOL LLMenuItemBranchGL::isActive() const
1204{
1205	return isOpen() && getBranch() && getBranch()->getHighlightedItem();
1206}
1207
1208//virtual
1209BOOL LLMenuItemBranchGL::isOpen() const
1210{
1211	return getBranch() && getBranch()->isOpen();
1212}
1213
1214void LLMenuItemBranchGL::openMenu()
1215{
1216	LLMenuGL* branch = getBranch();
1217	if (!branch)
1218		return;
1219
1220	if (branch->getTornOff())
1221	{
1222		gFloaterView->bringToFront((LLFloater*)branch->getParent());
1223		// this might not be necessary, as torn off branches don't get focus and hence no highligth
1224		branch->highlightNextItem(NULL);
1225	}
1226	else if( !branch->getVisible() )
1227	{
1228		// get valid rectangle for menus
1229		const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect();
1230
1231		branch->arrange();
1232
1233		LLRect branch_rect = branch->getRect();
1234		// calculate root-view relative position for branch menu
1235		S32 left = getRect().mRight;
1236		S32 top = getRect().mTop - getRect().mBottom;
1237
1238		localPointToOtherView(left, top, &left, &top, branch->getParent());
1239
1240		branch_rect.setLeftTopAndSize( left, top,
1241								branch_rect.getWidth(), branch_rect.getHeight() );
1242
1243		if (branch->getCanTearOff())
1244		{
1245			branch_rect.translate(0, TEAROFF_SEPARATOR_HEIGHT_PIXELS);
1246		}
1247		branch->setRect( branch_rect );
1248		
1249		// if branch extends outside of menu region change the direction it opens in
1250		S32 x, y;
1251		S32 delta_x = 0;
1252		S32 delta_y = 0;
1253		branch->localPointToOtherView( 0, 0, &x, &y, branch->getParent() ); 
1254		if( y < menu_region_rect.mBottom )
1255		{
1256			// open upwards if menu extends past bottom
1257			// adjust by the height of the menu item branch since it is a submenu
1258			delta_y = branch_rect.getHeight() - getRect().getHeight();		
1259		}
1260
1261		if( x + branch_rect.getWidth() > menu_region_rect.mRight )
1262		{
1263			// move sub-menu over to left side
1264			delta_x = llmax(-x, ( -(branch_rect.getWidth() + getRect().getWidth())));
1265		}
1266		branch->translate( delta_x, delta_y );
1267
1268		branch->setVisible( TRUE );
1269		branch->getParent()->sendChildToFront(branch);
1270
1271		dirtyRect();
1272	}
1273}
1274
1275
1276//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1277// Class LLMenuItemBranchDownGL
1278//
1279// The LLMenuItemBranchDownGL represents a menu item that has a
1280// sub-menu. This is used to make menu bar menus.
1281//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1282
1283class LLMenuItemBranchDownGL : public LLMenuItemBranchGL
1284{
1285protected:
1286
1287public:
1288	LLMenuItemBranchDownGL( const Params& );
1289
1290	// returns the normal width of this control in pixels - this is
1291	// used for calculating the widest item, as well as for horizontal
1292	// arrangement.
1293	virtual U32 getNominalWidth( void ) const;
1294
1295	// called to rebuild the draw label
1296	virtual void buildDrawLabel( void );
1297
1298	// handles opening, positioning, and arranging the menu branch associated with this item
1299	virtual void openMenu( void );
1300
1301	// set the hover status (called by it's menu) and if the object is
1302	// active. This is used for behavior transfer.
1303	virtual void setHighlight( BOOL highlight );
1304
1305	virtual BOOL isActive( void ) const;
1306
1307	// LLView functionality
1308	virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask );
1309	virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask ); 
1310	virtual void draw( void );
1311	virtual BOOL handleKeyHere(KEY key, MASK mask);
1312	
1313	virtual BOOL handleAcceleratorKey(KEY key, MASK mask);
1314};
1315
1316LLMenuItemBranchDownGL::LLMenuItemBranchDownGL( const Params& p) :
1317	LLMenuItemBranchGL(p)
1318{
1319}
1320
1321// returns the normal width of this control in pixels - this is used
1322// for calculating the widest item, as well as for horizontal
1323// arrangement.
1324U32 LLMenuItemBranchDownGL::getNominalWidth( void ) const
1325{
1326	U32 width = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS;
1327	width += getFont()->getWidth( mLabel.getWString().c_str() ); 
1328	return width;
1329}
1330
1331// called to rebuild the draw label
1332void LLMenuItemBranchDownGL::buildDrawLabel( void )
1333{
1334	mDrawAccelLabel.clear();
1335	std::string st = mDrawAccelLabel;
1336	appendAcceleratorString( st );
1337	mDrawAccelLabel = st;
1338}
1339
1340void LLMenuItemBranchDownGL::openMenu( void )
1341{
1342	LLMenuGL* branch = getBranch();
1343	if( branch->getVisible() && !branch->getTornOff() )
1344	{
1345		branch->setVisible( FALSE );
1346	}
1347	else
1348	{
1349		if (branch->getTornOff())
1350		{
1351			gFloaterView->bringToFront((LLFloater*)branch->getParent());
1352		}
1353		else
1354		{
1355			// We're showing the drop-down menu, so patch up its labels/rects
1356			branch->arrange();
1357
1358			LLRect rect = branch->getRect();
1359			S32 left = 0;
1360			S32 top = getRect().mBottom;
1361			localPointToOtherView(left, top, &left, &top, branch->getParent());
1362
1363			rect.setLeftTopAndSize( left, top,
1364									rect.getWidth(), rect.getHeight() );
1365			branch->setRect( rect );
1366			S32 x = 0;
1367			S32 y = 0;
1368			branch->localPointToScreen( 0, 0, &x, &y ); 
1369			S32 delta_x = 0;
1370
1371			LLCoordScreen window_size;
1372			LLWindow* windowp = getWindow();
1373			windowp->getSize(&window_size);
1374
1375			S32 window_width = window_size.mX;
1376			if( x > window_width - rect.getWidth() )
1377			{
1378				delta_x = (window_width - rect.getWidth()) - x;
1379			}
1380			branch->translate( delta_x, 0 );
1381
1382			setHighlight(TRUE);
1383			branch->setVisible( TRUE );
1384			branch->getParent()->sendChildToFront(branch);
1385		}
1386	}
1387}
1388
1389// set the hover status (called by it's menu)
1390void LLMenuItemBranchDownGL::setHighlight( BOOL highlight )
1391{
1392 	if (highlight == getHighlight())
1393		return;
1394
1395	//NOTE: Purposely calling all the way to the base to bypass auto-open.
1396	LLMenuItemGL::setHighlight(highlight);
1397
1398	LLMenuGL* branch = getBranch();
1399	if (!branch)
1400		return;
1401	
1402	if( !highlight)
1403	{
1404		if (branch->getTornOff())
1405		{
1406			((LLFloater*)branch->getParent())->setFocus(FALSE);
1407			branch->clearHoverItem();
1408		}
1409		else
1410		{
1411			branch->setVisible( FALSE );
1412		}
1413	}
1414}
1415
1416BOOL LLMenuItemBranchDownGL::isActive() const
1417{
1418	// for top level menus, being open is sufficient to be considered 
1419	// active, because clicking on them with the mouse will open
1420	// them, without moving keyboard focus to them
1421	return isOpen();
1422}
1423
1424BOOL LLMenuItemBranchDownGL::handleMouseDown( S32 x, S32 y, MASK mask )
1425{
1426	// switch to mouse control mode
1427	LLMenuGL::setKeyboardMode(FALSE);
1428	onCommit();
1429	make_ui_sound("UISndClick");
1430	return TRUE;
1431}
1432
1433BOOL LLMenuItemBranchDownGL::handleMouseUp( S32 x, S32 y, MASK mask )
1434{
1435	return TRUE;
1436}
1437
1438
1439BOOL LLMenuItemBranchDownGL::handleAcceleratorKey(KEY key, MASK mask)
1440{
1441	BOOL branch_visible = getBranch()->getVisible();
1442	BOOL handled = getBranch()->handleAcceleratorKey(key, mask);
1443	if (handled && !branch_visible && isInVisibleChain())
1444	{
1445		// flash this menu entry because we triggered an invisible menu item
1446		LLMenuHolderGL::setActivatedItem(this);
1447	}
1448
1449	return handled;
1450}
1451
1452BOOL LLMenuItemBranchDownGL::handleKeyHere(KEY key, MASK mask)
1453{
1454	BOOL menu_open = getBranch()->getVisible();
1455	// don't do keyboard navigation of top-level menus unless in keyboard mode, or menu expanded
1456	if (getHighlight() && getMenu()->isOpen() && (isActive() || LLMenuGL::getKeyboardMode()))
1457	{
1458		if (key == KEY_LEFT)
1459		{
1460			// switch to keyboard navigation mode
1461			LLMenuGL::setKeyboardMode(TRUE);
1462
1463			LLMenuItemGL* itemp = getMenu()->highlightPrevItem(this);
1464			// open new menu only if previous menu was open
1465			if (itemp && itemp->getEnabled() && menu_open)
1466			{
1467				itemp->onCommit();
1468			}
1469
1470			return TRUE;
1471		}
1472		else if (key == KEY_RIGHT)
1473		{
1474			// switch to keyboard navigation mode
1475			LLMenuGL::setKeyboardMode(TRUE);
1476
1477			LLMenuItemGL* itemp = getMenu()->highlightNextItem(this);
1478			// open new menu only if previous menu was open
1479			if (itemp && itemp->getEnabled() && menu_open)
1480			{
1481				itemp->onCommit();
1482			}
1483
1484			return TRUE;
1485		}
1486		else if (key == KEY_DOWN)
1487		{
1488			// switch to keyboard navigation mode
1489			LLMenuGL::setKeyboardMode(TRUE);
1490
1491			if (!isActive())
1492			{
1493				onCommit();
1494			}
1495			getBranch()->highlightNextItem(NULL);
1496			return TRUE;
1497		}
1498		else if (key == KEY_UP)
1499		{
1500			// switch to keyboard navigation mode
1501			LLMenuGL::setKeyboardMode(TRUE);
1502
1503			if (!isActive())
1504			{
1505				onCommit();
1506			}
1507			getBranch()->highlightPrevItem(NULL);
1508			return TRUE;
1509		}
1510	}
1511
1512	return FALSE;
1513}
1514
1515void LLMenuItemBranchDownGL::draw( void )
1516{
1517	//FIXME: try removing this
1518	if (getBranch()->getVisible() && !getBranch()->getTornOff())
1519	{
1520		setHighlight(TRUE);
1521	}
1522
1523	if( getHighlight() )
1524	{
1525		gGL.color4fv( mHighlightBackground.get().mV );
1526		gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 );
1527	}
1528
1529	LLColor4 color;
1530	if (getHighlight())
1531	{
1532		color = mHighlightForeground.get();
1533	}
1534	else if( getEnabled() )
1535	{
1536		color = mEnabledColor.get();
1537	}
1538	else
1539	{
1540		color = mDisabledColor.get();
1541	}
1542	getFont()->render( mLabel.getWString(), 0, (F32)getRect().getWidth() / 2.f, (F32)LABEL_BOTTOM_PAD_PIXELS, color,
1543				   LLFontGL::HCENTER, LLFontGL::BOTTOM, LLFontGL::NORMAL);
1544
1545
1546	// underline navigation key only when keyboard navigation has been initiated
1547	if (getMenu()->jumpKeysActive() && LLMenuGL::getKeyboardMode())
1548	{
1549		std::string upper_case_label = mLabel.getString();
1550		LLStringUtil::toUpper(upper_case_label);
1551		std::string::size_type offset = upper_case_label.find(getJumpKey());
1552		if (offset != std::string::npos)
1553		{
1554			S32 x_offset = llround((F32)getRect().getWidth() / 2.f - getFont()->getWidthF32(mLabel.getString(), 0, S32_MAX) / 2.f);
1555			S32 x_begin = x_offset + getFont()->getWidth(mLabel, 0, offset);
1556			S32 x_end = x_offset + getFont()->getWidth(mLabel, 0, offset + 1);
1557			gl_line_2d(x_begin, LABEL_BOTTOM_PAD_PIXELS, x_end, LABEL_BOTTOM_PAD_PIXELS);
1558		}
1559	}
1560
1561	// reset every frame so that we only show highlight 
1562	// when we get hover events on that frame
1563	setHover(FALSE);
1564}
1565
1566
1567class LLMenuScrollItem : public LLMenuItemCallGL
1568{
1569public:
1570	enum EArrowType
1571	{
1572		ARROW_DOWN,
1573		ARROW_UP
1574	};
1575	struct ArrowTypes : public LLInitParam::TypeValuesHelper<EArrowType, ArrowTypes>
1576	{
1577		static void declareValues()
1578		{
1579			declare("up", ARROW_UP);
1580			declare("down", ARROW_DOWN);
1581		}
1582	};
1583
1584	struct Params : public LLInitParam::Block<Params, LLMenuItemCallGL::Params>
1585	{
1586		Optional<EArrowType, ArrowTypes> arrow_type;
1587		Optional<CommitCallbackParam> scroll_callback;
1588	};
1589
1590protected:
1591	LLMenuScrollItem(const Params&);
1592	friend class LLUICtrlFactory;
1593
1594public:
1595	/*virtual*/ void draw();
1596	/*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent);
1597	/*virtual*/ void setEnabled(BOOL enabled);
1598	virtual void onCommit( void );
1599
1600private:
1601	LLButton*				mArrowBtn;
1602};
1603
1604LLMenuScrollItem::LLMenuScrollItem(const Params& p)
1605:	LLMenuItemCallGL(p)
1606{
1607	std::string icon;
1608	if (p.arrow_type.isProvided() && p.arrow_type == ARROW_UP)
1609	{
1610		icon = "arrow_up.tga";
1611	}
1612	else
1613	{
1614		icon = "arrow_down.tga";
1615	}
1616
1617	LLButton::Params bparams;
1618
1619	// Disabled the Return key handling by LLMenuScrollItem instead of
1620	// passing the key press to the currently selected menu item. See STORM-385.
1621	bparams.commit_on_return(false);
1622	bparams.mouse_opaque(true);
1623	bparams.scale_image(false);
1624	bparams.click_callback(p.scroll_callback);
1625	bparams.mouse_held_callback(p.scroll_callback);
1626	bparams.follows.flags(FOLLOWS_ALL);
1627	std::string background = "transparent.j2c";
1628	bparams.image_unselected.name(background);
1629	bparams.image_disabled.name(background);
1630	bparams.image_selected.name(background);
1631	bparams.image_hover_selected.name(background);
1632	bparams.image_disabled_selected.name(background);
1633	bparams.image_hover_unselected.name(background);
1634	bparams.image_overlay.name(icon);
1635
1636	mArrowBtn = LLUICtrlFactory::create<LLButton>(bparams);
1637	addChild(mArrowBtn);
1638}
1639
1640/*virtual*/
1641void LLMenuScrollItem::draw()
1642{
1643	LLUICtrl::draw();
1644}
1645
1646/*virtual*/
1647void LLMenuScrollItem::reshape(S32 width, S32 height, BOOL called_from_parent)
1648{
1649	mArrowBtn->reshape(width, height, called_from_parent);
1650	LLView::reshape(width, height, called_from_parent);
1651}
1652
1653/*virtual*/
1654void LLMenuScrollItem::setEnabled(BOOL enabled)
1655{
1656	mArrowBtn->setEnabled(enabled);
1657	LLView::setEnabled(enabled);
1658}
1659
1660void LLMenuScrollItem::onCommit( void )
1661{
1662	LLUICtrl::onCommit();
1663}
1664
1665///============================================================================
1666/// Class LLMenuGL
1667///============================================================================
1668
1669LLMenuGL::LLMenuGL(const LLMenuGL::Params& p)
1670:	LLUICtrl(p),
1671	mBackgroundColor( p.bg_color() ),
1672	mBgVisible( p.bg_visible ),
1673	mDropShadowed( p.drop_shadow ),
1674	mHasSelection(false),
1675	mHorizontalLayout( p.horizontal_layout ),
1676	mScrollable(mHorizontalLayout ? FALSE : p.scrollable), // Scrolling is supported only for vertical layout
1677	mMaxScrollableItems(p.max_scrollable_items),
1678	mPreferredWidth(p.preferred_width),
1679	mKeepFixedSize( p.keep_fixed_size ),
1680	mLabel (p.label),
1681	mLastMouseX(0),
1682	mLastMouseY(0),
1683	mMouseVelX(0),
1684	mMouseVelY(0),
1685	mTornOff(FALSE),
1686	mTearOffItem(NULL),
1687	mSpilloverBranch(NULL),
1688	mFirstVisibleItem(NULL),
1689	mArrowUpItem(NULL),
1690	mArrowDownItem(NULL),
1691	mSpilloverMenu(NULL),
1692	mJumpKey(p.jump_key),
1693	mCreateJumpKeys(p.create_jump_keys),
1694	mNeedsArrange(FALSE),
1695	mResetScrollPositionOnShow(true),
1696	mShortcutPad(p.shortcut_pad)
1697{
1698	typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
1699	boost::char_separator<char> sep("_");
1700	tokenizer tokens(p.label(), sep);
1701	tokenizer::iterator token_iter;
1702
1703	S32 token_count = 0;
1704	std::string new_menu_label;
1705	for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
1706	{
1707		new_menu_label += (*token_iter);
1708		if (token_count > 0)
1709		{
1710			setJumpKey((*token_iter).c_str()[0]);
1711		}
1712		++token_count;
1713	}
1714	setLabel(new_menu_label);
1715
1716	mFadeTimer.stop();
1717}
1718
1719void LLMenuGL::initFromParams(const LLMenuGL::Params& p)
1720{
1721	LLUICtrl::initFromParams(p);
1722	setCanTearOff(p.can_tear_off);
1723}
1724
1725// Destroys the object
1726LLMenuGL::~LLMenuGL( void )
1727{
1728	// delete the branch, as it might not be in view hierarchy
1729	// leave the menu, because it is always in view hierarchy
1730	delete mSpilloverBranch;
1731	mJumpKeys.clear();
1732}
1733
1734void LLMenuGL::setCanTearOff(BOOL tear_off)
1735{
1736	if (tear_off && mTearOffItem == NULL)
1737	{
1738		LLMenuItemTearOffGL::Params p;
1739		mTearOffItem = LLUICtrlFactory::create<LLMenuItemTearOffGL>(p);
1740		addChild(mTearOffItem);
1741	}
1742	else if (!tear_off && mTearOffItem != NULL)
1743	{
1744		mItems.remove(mTearOffItem);
1745		removeChild(mTearOffItem);
1746		delete mTearOffItem;
1747		mTearOffItem = NULL;
1748		needsArrange();
1749	}
1750}
1751
1752bool LLMenuGL::addChild(LLView* view, S32 tab_group)
1753{
1754	if (LLMenuGL* menup = dynamic_cast<LLMenuGL*>(view))
1755	{
1756		appendMenu(menup);
1757		return true;
1758	}
1759	else if (LLMenuItemGL* itemp = dynamic_cast<LLMenuItemGL*>(view))
1760	{
1761		append(itemp);
1762		return true;
1763	}
1764	return false;
1765}
1766
1767void LLMenuGL::removeChild( LLView* ctrl)
1768{
1769	// previously a dynamic_cast with if statement to check validity
1770	// unfortunately removeChild is called by ~LLView, and at that point the
1771	// object being deleted is no longer a LLMenuItemGL so a dynamic_cast will fail
1772	LLMenuItemGL* itemp = static_cast<LLMenuItemGL*>(ctrl);
1773
1774	item_list_t::iterator found_it = std::find(mItems.begin(), mItems.end(), (itemp));
1775	if (found_it != mItems.end())
1776	{
1777		mItems.erase(found_it);
1778	}
1779
1780	return LLUICtrl::removeChild(ctrl);
1781}
1782
1783BOOL LLMenuGL::postBuild()
1784{
1785	createJumpKeys();
1786	return LLUICtrl::postBuild();
1787}
1788
1789// are we the childmost active menu and hence our jump keys should be enabled?
1790// or are we a free-standing torn-off menu (which uses jump keys too)
1791BOOL LLMenuGL::jumpKeysActive()
1792{
1793	LLMenuItemGL* highlighted_item = getHighlightedItem();
1794	BOOL active = getVisible() && getEnabled();
1795	if (getTornOff())
1796	{
1797		// activation of jump keys on torn off menus controlled by keyboard focus
1798		active = active && ((LLFloater*)getParent())->hasFocus();
1799
1800	}
1801	else
1802	{
1803		// Are we the terminal active menu?
1804		// Yes, if parent menu item deems us to be active (just being visible is sufficient for top-level menus)
1805		// and we don't have a highlighted menu item pointing to an active sub-menu
1806		active = active && (!getParentMenuItem() || getParentMenuItem()->isActive()) // I have a parent that is active...
1807		                && (!highlighted_item || !highlighted_item->isActive()); //... but no child that is active
1808	}
1809	return active;
1810}
1811
1812BOOL LLMenuGL::isOpen()
1813{
1814	if (getTornOff())
1815	{
1816		LLMenuItemGL* itemp = getHighlightedItem();
1817		// if we have an open sub-menu, then we are considered part of 
1818		// the open menu chain even if we don't have focus
1819		if (itemp && itemp->isOpen())
1820		{
1821			return TRUE;
1822		}
1823		// otherwise we are only active if we have keyboard focus
1824		return ((LLFloater*)getParent())->hasFocus();
1825	}
1826	else
1827	{
1828		// normally, menus are hidden as soon as the user focuses
1829		// on another menu, so just use the visibility criterion
1830		return getVisible();
1831	}
1832}
1833
1834
1835
1836bool LLMenuGL::scrollItems(EScrollingDirection direction)
1837{
1838	// Slowing down items scrolling when arrow button is held
1839	if (mScrollItemsTimer.hasExpired() && NULL != mFirstVisibleItem)
1840	{
1841		mScrollItemsTimer.setTimerExpirySec(.033f);
1842	}
1843	else
1844	{
1845		return false;
1846	}
1847
1848	switch (direction)
1849	{
1850	case SD_UP:
1851	{
1852		item_list_t::iterator cur_item_iter;
1853		item_list_t::iterator prev_item_iter;
1854		for (cur_item_iter = mItems.begin(), prev_item_iter = mItems.begin(); cur_item_iter != mItems.end(); cur_item_iter++)
1855		{
1856			if( (*cur_item_iter) == mFirstVisibleItem)
1857			{
1858				break;
1859			}
1860			if ((*cur_item_iter)->getVisible())
1861			{
1862				prev_item_iter = cur_item_iter;
1863			}
1864		}
1865
1866		if ((*prev_item_iter)->getVisible())
1867		{
1868			mFirstVisibleItem = *prev_item_iter;
1869		}
1870		break;
1871	}
1872	case SD_DOWN:
1873	{
1874		if (NULL == mFirstVisibleItem)
1875		{
1876			mFirstVisibleItem = *mItems.begin();
1877		}
1878
1879		item_list_t::iterator cur_item_iter;
1880
1881		for (cur_item_iter = mItems.begin(); cur_item_iter != mItems.end(); cur_item_iter++)
1882		{
1883			if( (*cur_item_iter) == mFirstVisibleItem)
1884			{
1885				break;
1886			}
1887		}
1888
1889		item_list_t::iterator next_item_iter;
1890
1891		if (cur_item_iter != mItems.end())
1892		{
1893			for (next_item_iter = ++cur_item_iter; next_item_iter != mItems.end(); next_item_iter++)
1894			{
1895				if( (*next_item_iter)->getVisible())
1896				{
1897					break;
1898				}
1899			}
1900
1901			if (next_item_iter != mItems.end() &&
1902				(*next_item_iter)->getVisible())
1903			{
1904				mFirstVisibleItem = *next_item_iter;
1905			}
1906		}
1907		break;
1908	}
1909	case SD_BEGIN:
1910	{
1911		mFirstVisibleItem = *mItems.begin();
1912		break;
1913	}
1914	case SD_END:
1915	{
1916		item_list_t::reverse_iterator first_visible_item_iter = mItems.rend();
1917
1918		// Need to scroll through number of actual existing items in menu.
1919		// Otherwise viewer will hang for a time needed to scroll U32_MAX
1920		// times in std::advance(). STORM-659.
1921		size_t nitems = mItems.size();
1922		U32 scrollable_items = nitems < mMaxScrollableItems ? nitems : mMaxScrollableItems;
1923
1924		// Advance by mMaxScrollableItems back from the end of the list
1925		// to make the last item visible.
1926		std::advance(first_visible_item_iter, scrollable_items);
1927		mFirstVisibleItem = *first_visible_item_iter;
1928		break;
1929	}
1930	default:
1931		llwarns << "Unknown scrolling direction: " << direction << llendl;
1932	}
1933
1934	mNeedsArrange = TRUE;
1935	arrangeAndClear();
1936
1937	return true;
1938}
1939
1940// rearrange the child rects so they fit the shape of the menu.
1941void LLMenuGL::arrange( void )
1942{
1943	// calculate the height & width, and set our rect based on that
1944	// information.
1945	const LLRect& initial_rect = getRect();
1946
1947	U32 width = 0, height = MENU_ITEM_PADDING;
1948
1949	cleanupSpilloverBranch();
1950
1951	if( mItems.size() ) 
1952	{
1953		const LLRect menu_region_rect = LLMenuGL::sMenuContainer ? LLMenuGL::sMenuContainer->getMenuRect() : LLRect(0, S32_MAX, S32_MAX, 0);
1954
1955		// torn off menus are not constrained to the size of the screen
1956		U32 max_width = getTornOff() ? U32_MAX : menu_region_rect.getWidth();
1957		U32 max_height = U32_MAX;
1958		if (!getTornOff())
1959		{
1960			max_height = getRect().mTop - menu_region_rect.mBottom;
1961			if (menu_region_rect.mTop - getRect().mTop > (S32)max_height)
1962			{
1963				max_height = menu_region_rect.mTop - getRect().mTop;
1964			}
1965		}
1966
1967		// *FIX: create the item first and then ask for its dimensions?
1968		S32 spill

Large files files are truncated, but you can click here to view the full file