PageRenderTime 111ms CodeModel.GetById 10ms app.highlight 92ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/llui/llbutton.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1249 lines | 968 code | 147 blank | 134 comment | 143 complexity | 7b768b78a517fefad3f673a645257548 MD5 | raw file
   1
   2/** 
   3 * @file llbutton.cpp
   4 * @brief LLButton base class
   5 *
   6 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
   7 * Second Life Viewer Source Code
   8 * Copyright (C) 2010, Linden Research, Inc.
   9 * 
  10 * This library is free software; you can redistribute it and/or
  11 * modify it under the terms of the GNU Lesser General Public
  12 * License as published by the Free Software Foundation;
  13 * version 2.1 of the License only.
  14 * 
  15 * This library is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18 * Lesser General Public License for more details.
  19 * 
  20 * You should have received a copy of the GNU Lesser General Public
  21 * License along with this library; if not, write to the Free Software
  22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  23 * 
  24 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  25 * $/LicenseInfo$
  26 */
  27
  28#include "linden_common.h"
  29
  30#define LLBUTTON_CPP
  31#include "llbutton.h"
  32
  33// Linden library includes
  34#include "v4color.h"
  35#include "llstring.h"
  36
  37// Project includes
  38#include "llkeyboard.h"
  39#include "llui.h"
  40#include "lluiconstants.h"
  41#include "llresmgr.h"
  42#include "llcriticaldamp.h"
  43#include "llfloater.h"
  44#include "llfloaterreg.h"
  45#include "llfocusmgr.h"
  46#include "llwindow.h"
  47#include "llnotificationsutil.h"
  48#include "llrender.h"
  49#include "lluictrlfactory.h"
  50#include "llhelp.h"
  51#include "lldockablefloater.h"
  52
  53static LLDefaultChildRegistry::Register<LLButton> r("button");
  54
  55// Compiler optimization, generate extern template
  56template class LLButton* LLView::getChild<class LLButton>(
  57	const std::string& name, BOOL recurse) const;
  58
  59// globals loaded from settings.xml
  60S32	LLBUTTON_H_PAD	= 0;
  61S32 BTN_HEIGHT_SMALL= 0;
  62S32 BTN_HEIGHT		= 0;
  63
  64LLButton::Params::Params()
  65:	label_selected("label_selected"),				// requires is_toggle true
  66	label_shadow("label_shadow", true),
  67	auto_resize("auto_resize", false),
  68	use_ellipses("use_ellipses", false),
  69	image_unselected("image_unselected"),
  70	image_selected("image_selected"),
  71	image_hover_selected("image_hover_selected"),
  72	image_hover_unselected("image_hover_unselected"),
  73	image_disabled_selected("image_disabled_selected"),
  74	image_disabled("image_disabled"),
  75	image_pressed("image_pressed"),
  76	image_pressed_selected("image_pressed_selected"),
  77	image_overlay("image_overlay"),
  78	image_overlay_alignment("image_overlay_alignment", std::string("center")),
  79	image_top_pad("image_top_pad"),
  80	image_bottom_pad("image_bottom_pad"),
  81	imgoverlay_label_space("imgoverlay_label_space", 1),
  82	label_color("label_color"),
  83	label_color_selected("label_color_selected"),	// requires is_toggle true
  84	label_color_disabled("label_color_disabled"),
  85	label_color_disabled_selected("label_color_disabled_selected"),
  86	image_color("image_color"),
  87	image_color_disabled("image_color_disabled"),
  88	image_overlay_color("image_overlay_color", LLColor4::white % 0.75f),
  89	image_overlay_disabled_color("image_overlay_disabled_color", LLColor4::white % 0.3f),
  90	image_overlay_selected_color("image_overlay_selected_color", LLColor4::white),
  91	flash_color("flash_color"),
  92	pad_right("pad_right", LLUI::sSettingGroups["config"]->getS32("ButtonHPad")),
  93	pad_left("pad_left", LLUI::sSettingGroups["config"]->getS32("ButtonHPad")),
  94	pad_bottom("pad_bottom"),
  95	click_callback("click_callback"),
  96	mouse_down_callback("mouse_down_callback"),
  97	mouse_up_callback("mouse_up_callback"),
  98	mouse_held_callback("mouse_held_callback"),
  99	is_toggle("is_toggle", false),
 100	scale_image("scale_image", true),
 101	hover_glow_amount("hover_glow_amount"),
 102	commit_on_return("commit_on_return", true),
 103	display_pressed_state("display_pressed_state", true),
 104	use_draw_context_alpha("use_draw_context_alpha", true),
 105	badge("badge"),
 106	handle_right_mouse("handle_right_mouse"),
 107	held_down_delay("held_down_delay"),
 108	button_flash_count("button_flash_count"),
 109	button_flash_rate("button_flash_rate")
 110{
 111	addSynonym(is_toggle, "toggle");
 112	changeDefault(initial_value, LLSD(false));
 113}
 114
 115
 116LLButton::LLButton(const LLButton::Params& p)
 117:	LLUICtrl(p),
 118	LLBadgeOwner(getHandle()),
 119	mMouseDownFrame(0),
 120	mMouseHeldDownCount(0),
 121	mBorderEnabled( FALSE ),
 122	mFlashing( FALSE ),
 123	mCurGlowStrength(0.f),
 124	mNeedsHighlight(FALSE),
 125	mUnselectedLabel(p.label()),
 126	mSelectedLabel(p.label_selected()),
 127	mGLFont(p.font),
 128	mHeldDownDelay(p.held_down_delay.seconds),			// seconds until held-down callback is called
 129	mHeldDownFrameDelay(p.held_down_delay.frames),
 130	mImageUnselected(p.image_unselected),
 131	mImageSelected(p.image_selected),
 132	mImageDisabled(p.image_disabled),
 133	mImageDisabledSelected(p.image_disabled_selected),
 134	mImageFlash(p.image_flash),
 135	mImagePressed(p.image_pressed),
 136	mImagePressedSelected(p.image_pressed_selected),
 137	mImageHoverSelected(p.image_hover_selected),
 138	mImageHoverUnselected(p.image_hover_unselected),
 139	mUnselectedLabelColor(p.label_color()),
 140	mSelectedLabelColor(p.label_color_selected()),
 141	mDisabledLabelColor(p.label_color_disabled()),
 142	mDisabledSelectedLabelColor(p.label_color_disabled_selected()),
 143	mImageColor(p.image_color()),
 144	mFlashBgColor(p.flash_color()),
 145	mDisabledImageColor(p.image_color_disabled()),
 146	mImageOverlay(p.image_overlay()),
 147	mImageOverlayColor(p.image_overlay_color()),
 148	mImageOverlayDisabledColor(p.image_overlay_disabled_color()),
 149	mImageOverlaySelectedColor(p.image_overlay_selected_color()),
 150	mImageOverlayAlignment(LLFontGL::hAlignFromName(p.image_overlay_alignment)),
 151	mImageOverlayTopPad(p.image_top_pad),
 152	mImageOverlayBottomPad(p.image_bottom_pad),
 153	mImgOverlayLabelSpace(p.imgoverlay_label_space),
 154	mIsToggle(p.is_toggle),
 155	mScaleImage(p.scale_image),
 156	mDropShadowedText(p.label_shadow),
 157	mAutoResize(p.auto_resize),
 158	mUseEllipses( p.use_ellipses ),
 159	mHAlign(p.font_halign),
 160	mLeftHPad(p.pad_left),
 161	mRightHPad(p.pad_right),
 162	mBottomVPad(p.pad_bottom),
 163	mHoverGlowStrength(p.hover_glow_amount),
 164	mCommitOnReturn(p.commit_on_return),
 165	mFadeWhenDisabled(FALSE),
 166	mForcePressedState(false),
 167	mDisplayPressedState(p.display_pressed_state),
 168	mLastDrawCharsCount(0),
 169	mMouseDownSignal(NULL),
 170	mMouseUpSignal(NULL),
 171	mHeldDownSignal(NULL),
 172	mUseDrawContextAlpha(p.use_draw_context_alpha),
 173	mHandleRightMouse(p.handle_right_mouse),
 174	mButtonFlashCount(p.button_flash_count),
 175	mButtonFlashRate(p.button_flash_rate)
 176{
 177	static LLUICachedControl<S32> llbutton_orig_h_pad ("UIButtonOrigHPad", 0);
 178	static Params default_params(LLUICtrlFactory::getDefaultParams<LLButton>());
 179
 180	if (!p.label_selected.isProvided())
 181	{
 182		mSelectedLabel = mUnselectedLabel;
 183	}
 184
 185	// Hack to make sure there is space for at least one character
 186	if (getRect().getWidth() - (mRightHPad + mLeftHPad) < mGLFont->getWidth(std::string(" ")))
 187	{
 188		// Use old defaults
 189		mLeftHPad = llbutton_orig_h_pad;
 190		mRightHPad = llbutton_orig_h_pad;
 191	}
 192	
 193	mMouseDownTimer.stop();
 194
 195	// if custom unselected button image provided...
 196	if (p.image_unselected != default_params.image_unselected)
 197	{
 198		//...fade it out for disabled image by default...
 199		if (p.image_disabled() == default_params.image_disabled() )
 200		{
 201			mImageDisabled = p.image_unselected;
 202			mFadeWhenDisabled = TRUE;
 203		}
 204
 205		if (p.image_pressed_selected == default_params.image_pressed_selected)
 206		{
 207			mImagePressedSelected = mImageUnselected;
 208		}
 209	}
 210
 211	// if custom selected button image provided...
 212	if (p.image_selected != default_params.image_selected)
 213	{
 214		//...fade it out for disabled image by default...
 215		if (p.image_disabled_selected() == default_params.image_disabled_selected())
 216		{
 217			mImageDisabledSelected = p.image_selected;
 218			mFadeWhenDisabled = TRUE;
 219		}
 220
 221		if (p.image_pressed == default_params.image_pressed)
 222		{
 223			mImagePressed = mImageSelected;
 224		}
 225	}
 226
 227	if (!p.image_pressed.isProvided())
 228	{
 229		mImagePressed = mImageSelected;
 230	}
 231
 232	if (!p.image_pressed_selected.isProvided())
 233	{
 234		mImagePressedSelected = mImageUnselected;
 235	}
 236	
 237	if (mImageUnselected.isNull())
 238	{
 239		llwarns << "Button: " << getName() << " with no image!" << llendl;
 240	}
 241	
 242	if (p.click_callback.isProvided())
 243	{
 244		setCommitCallback(initCommitCallback(p.click_callback)); // alias -> commit_callback
 245	}
 246	if (p.mouse_down_callback.isProvided())
 247	{
 248		setMouseDownCallback(initCommitCallback(p.mouse_down_callback));
 249	}
 250	if (p.mouse_up_callback.isProvided())
 251	{
 252		setMouseUpCallback(initCommitCallback(p.mouse_up_callback));
 253	}
 254	if (p.mouse_held_callback.isProvided())
 255	{
 256		setHeldDownCallback(initCommitCallback(p.mouse_held_callback));
 257	}
 258
 259	if (p.badge.isProvided())
 260	{
 261		LLBadgeOwner::initBadgeParams(p.badge());
 262	}
 263}
 264
 265LLButton::~LLButton()
 266{
 267	delete mMouseDownSignal;
 268	delete mMouseUpSignal;
 269	delete mHeldDownSignal;
 270}
 271
 272// HACK: Committing a button is the same as instantly clicking it.
 273// virtual
 274void LLButton::onCommit()
 275{
 276	// WARNING: Sometimes clicking a button destroys the floater or
 277	// panel containing it.  Therefore we need to call 	LLUICtrl::onCommit()
 278	// LAST, otherwise this becomes deleted memory.
 279
 280	if (mMouseDownSignal) (*mMouseDownSignal)(this, LLSD());
 281	
 282	if (mMouseUpSignal) (*mMouseUpSignal)(this, LLSD());
 283
 284	if (getSoundFlags() & MOUSE_DOWN)
 285	{
 286		make_ui_sound("UISndClick");
 287	}
 288
 289	if (getSoundFlags() & MOUSE_UP)
 290	{
 291		make_ui_sound("UISndClickRelease");
 292	}
 293
 294	if (mIsToggle)
 295	{
 296		toggleState();
 297	}
 298
 299	// do this last, as it can result in destroying this button
 300	LLUICtrl::onCommit();
 301}
 302
 303boost::signals2::connection LLButton::setClickedCallback(const CommitCallbackParam& cb)
 304{
 305	return setClickedCallback(initCommitCallback(cb));
 306}
 307boost::signals2::connection LLButton::setMouseDownCallback(const CommitCallbackParam& cb)
 308{
 309	return setMouseDownCallback(initCommitCallback(cb));
 310}
 311boost::signals2::connection LLButton::setMouseUpCallback(const CommitCallbackParam& cb)
 312{
 313	return setMouseUpCallback(initCommitCallback(cb));
 314}
 315boost::signals2::connection LLButton::setHeldDownCallback(const CommitCallbackParam& cb)
 316{
 317	return setHeldDownCallback(initCommitCallback(cb));
 318}
 319
 320
 321boost::signals2::connection LLButton::setClickedCallback( const commit_signal_t::slot_type& cb )
 322{
 323	if (!mCommitSignal) mCommitSignal = new commit_signal_t();
 324	return mCommitSignal->connect(cb);
 325}
 326boost::signals2::connection LLButton::setMouseDownCallback( const commit_signal_t::slot_type& cb )
 327{
 328	if (!mMouseDownSignal) mMouseDownSignal = new commit_signal_t();
 329	return mMouseDownSignal->connect(cb);
 330}
 331boost::signals2::connection LLButton::setMouseUpCallback( const commit_signal_t::slot_type& cb )
 332{
 333	if (!mMouseUpSignal) mMouseUpSignal = new commit_signal_t();
 334	return mMouseUpSignal->connect(cb);
 335}
 336boost::signals2::connection LLButton::setHeldDownCallback( const commit_signal_t::slot_type& cb )
 337{
 338	if (!mHeldDownSignal) mHeldDownSignal = new commit_signal_t();
 339	return mHeldDownSignal->connect(cb);
 340}
 341
 342
 343// *TODO: Deprecate (for backwards compatibility only)
 344boost::signals2::connection LLButton::setClickedCallback( button_callback_t cb, void* data )
 345{
 346	return setClickedCallback(boost::bind(cb, data));
 347}
 348boost::signals2::connection LLButton::setMouseDownCallback( button_callback_t cb, void* data )
 349{
 350	return setMouseDownCallback(boost::bind(cb, data));
 351}
 352boost::signals2::connection LLButton::setMouseUpCallback( button_callback_t cb, void* data )
 353{
 354	return setMouseUpCallback(boost::bind(cb, data));
 355}
 356boost::signals2::connection LLButton::setHeldDownCallback( button_callback_t cb, void* data )
 357{
 358	return setHeldDownCallback(boost::bind(cb, data));
 359}
 360
 361BOOL LLButton::postBuild()
 362{
 363	autoResize();
 364
 365	addBadgeToParentPanel();
 366
 367	return LLUICtrl::postBuild();
 368}
 369
 370BOOL LLButton::handleUnicodeCharHere(llwchar uni_char)
 371{
 372	BOOL handled = FALSE;
 373	if(' ' == uni_char 
 374		&& !gKeyboard->getKeyRepeated(' '))
 375	{
 376		if (mIsToggle)
 377		{
 378			toggleState();
 379		}
 380
 381		LLUICtrl::onCommit();
 382		
 383		handled = TRUE;		
 384	}
 385	return handled;	
 386}
 387
 388BOOL LLButton::handleKeyHere(KEY key, MASK mask )
 389{
 390	BOOL handled = FALSE;
 391	if( mCommitOnReturn && KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key))
 392	{
 393		if (mIsToggle)
 394		{
 395			toggleState();
 396		}
 397
 398		handled = TRUE;
 399
 400		LLUICtrl::onCommit();
 401	}
 402	return handled;
 403}
 404
 405
 406BOOL LLButton::handleMouseDown(S32 x, S32 y, MASK mask)
 407{
 408	if (!childrenHandleMouseDown(x, y, mask))
 409	{
 410		// Route future Mouse messages here preemptively.  (Release on mouse up.)
 411		gFocusMgr.setMouseCapture( this );
 412
 413		if (hasTabStop() && !getIsChrome())
 414		{
 415			setFocus(TRUE);
 416		}
 417
 418		/*
 419		 * ATTENTION! This call fires another mouse down callback.
 420		 * If you wish to remove this call emit that signal directly
 421		 * by calling LLUICtrl::mMouseDownSignal(x, y, mask);
 422		 */
 423		LLUICtrl::handleMouseDown(x, y, mask);
 424
 425		if(mMouseDownSignal) (*mMouseDownSignal)(this, LLSD());
 426
 427		mMouseDownTimer.start();
 428		mMouseDownFrame = (S32) LLFrameTimer::getFrameCount();
 429		mMouseHeldDownCount = 0;
 430
 431		
 432		if (getSoundFlags() & MOUSE_DOWN)
 433		{
 434			make_ui_sound("UISndClick");
 435		}
 436	}
 437	return TRUE;
 438}
 439
 440
 441BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask)
 442{
 443	// We only handle the click if the click both started and ended within us
 444	if( hasMouseCapture() )
 445	{
 446		// Always release the mouse
 447		gFocusMgr.setMouseCapture( NULL );
 448
 449		/*
 450		 * ATTENTION! This call fires another mouse up callback.
 451		 * If you wish to remove this call emit that signal directly
 452		 * by calling LLUICtrl::mMouseUpSignal(x, y, mask);
 453		 */
 454		LLUICtrl::handleMouseUp(x, y, mask);
 455
 456		// Regardless of where mouseup occurs, handle callback
 457		if(mMouseUpSignal) (*mMouseUpSignal)(this, LLSD());
 458
 459		resetMouseDownTimer();
 460
 461		// DO THIS AT THE VERY END to allow the button to be destroyed as a result of being clicked.
 462		// If mouseup in the widget, it's been clicked
 463		if (pointInView(x, y))
 464		{
 465			if (getSoundFlags() & MOUSE_UP)
 466			{
 467				make_ui_sound("UISndClickRelease");
 468			}
 469
 470			if (mIsToggle)
 471			{
 472				toggleState();
 473			}
 474
 475			LLUICtrl::onCommit();
 476		}
 477	}
 478	else
 479	{
 480		childrenHandleMouseUp(x, y, mask);
 481	}
 482
 483	return TRUE;
 484}
 485
 486BOOL	LLButton::handleRightMouseDown(S32 x, S32 y, MASK mask)
 487{
 488	if (mHandleRightMouse && !childrenHandleRightMouseDown(x, y, mask))
 489	{
 490		// Route future Mouse messages here preemptively.  (Release on mouse up.)
 491		gFocusMgr.setMouseCapture( this );
 492
 493		if (hasTabStop() && !getIsChrome())
 494		{
 495			setFocus(TRUE);
 496		}
 497
 498//		if (pointInView(x, y))
 499//		{
 500//		}
 501		// send the mouse down signal
 502		LLUICtrl::handleRightMouseDown(x,y,mask);
 503		// *TODO: Return result of LLUICtrl call above?  Should defer to base class
 504		// but this might change the mouse handling of existing buttons in a bad way
 505		// if they are not mouse opaque.
 506	}
 507
 508	return TRUE;
 509}
 510
 511BOOL	LLButton::handleRightMouseUp(S32 x, S32 y, MASK mask)
 512{
 513	if (mHandleRightMouse)
 514	{
 515		// We only handle the click if the click both started and ended within us
 516		if( hasMouseCapture() )
 517		{
 518			// Always release the mouse
 519			gFocusMgr.setMouseCapture( NULL );
 520
 521	//		if (pointInView(x, y))
 522	//		{
 523	//			mRightMouseUpSignal(this, x,y,mask);
 524	//		}
 525		}
 526		else 
 527		{
 528			childrenHandleRightMouseUp(x, y, mask);
 529		}
 530	
 531		// send the mouse up signal
 532		LLUICtrl::handleRightMouseUp(x,y,mask);
 533		// *TODO: Return result of LLUICtrl call above?  Should defer to base class
 534		// but this might change the mouse handling of existing buttons in a bad way.
 535		// if they are not mouse opaque.
 536	}
 537	return TRUE;
 538}
 539
 540void LLButton::onMouseLeave(S32 x, S32 y, MASK mask)
 541{
 542	LLUICtrl::onMouseLeave(x, y, mask);
 543
 544	mNeedsHighlight = FALSE;
 545}
 546
 547void LLButton::setHighlight(bool b)
 548{
 549	mNeedsHighlight = b;
 550}
 551
 552BOOL LLButton::handleHover(S32 x, S32 y, MASK mask)
 553{
 554	if (isInEnabledChain() 
 555		&& (!gFocusMgr.getMouseCapture() || gFocusMgr.getMouseCapture() == this))
 556		mNeedsHighlight = TRUE;
 557
 558	if (!childrenHandleHover(x, y, mask))
 559	{
 560		if (mMouseDownTimer.getStarted())
 561		{
 562			F32 elapsed = getHeldDownTime();
 563			if( mHeldDownDelay <= elapsed && mHeldDownFrameDelay <= (S32)LLFrameTimer::getFrameCount() - mMouseDownFrame)
 564			{
 565				LLSD param;
 566				param["count"] = mMouseHeldDownCount++;
 567				if (mHeldDownSignal) (*mHeldDownSignal)(this, param);
 568			}
 569		}
 570
 571		// We only handle the click if the click both started and ended within us
 572		getWindow()->setCursor(UI_CURSOR_ARROW);
 573		lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl;
 574	}
 575	return TRUE;
 576}
 577
 578void LLButton::getOverlayImageSize(S32& overlay_width, S32& overlay_height)
 579{
 580	overlay_width = mImageOverlay->getWidth();
 581	overlay_height = mImageOverlay->getHeight();
 582
 583	F32 scale_factor = llmin((F32)getRect().getWidth() / (F32)overlay_width, (F32)getRect().getHeight() / (F32)overlay_height, 1.f);
 584	overlay_width = llround((F32)overlay_width * scale_factor);
 585	overlay_height = llround((F32)overlay_height * scale_factor);
 586}
 587
 588
 589// virtual
 590void LLButton::draw()
 591{
 592	static LLCachedControl<bool> sEnableButtonFlashing(*LLUI::sSettingGroups["config"], "EnableButtonFlashing", true);
 593	F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency();
 594	bool flash = FALSE;
 595
 596	if( mFlashing)
 597	{
 598		if ( sEnableButtonFlashing)
 599		{
 600			F32 elapsed = mFlashingTimer.getElapsedTimeF32();
 601			S32 flash_count = S32(elapsed * mButtonFlashRate * 2.f);
 602			// flash on or off?
 603			flash = (flash_count % 2 == 0) || flash_count > S32((F32)mButtonFlashCount * 2.f);
 604		}
 605		else
 606		{ // otherwise just highlight button in flash color
 607			flash = true;
 608		}
 609	}
 610
 611	bool pressed_by_keyboard = FALSE;
 612	if (hasFocus())
 613	{
 614		pressed_by_keyboard = gKeyboard->getKeyDown(' ') || (mCommitOnReturn && gKeyboard->getKeyDown(KEY_RETURN));
 615	}
 616
 617	bool mouse_pressed_and_over = false;
 618	if (hasMouseCapture())
 619	{
 620		S32 local_mouse_x ;
 621		S32 local_mouse_y;
 622		LLUI::getMousePositionLocal(this, &local_mouse_x, &local_mouse_y);
 623		mouse_pressed_and_over = pointInView(local_mouse_x, local_mouse_y);
 624	}
 625
 626	bool enabled = isInEnabledChain();
 627
 628	bool pressed = pressed_by_keyboard 
 629					|| mouse_pressed_and_over
 630					|| mForcePressedState;
 631	bool selected = getToggleState();
 632	
 633	bool use_glow_effect = FALSE;
 634	LLColor4 glow_color = LLColor4::white;
 635	LLRender::eBlendType glow_type = LLRender::BT_ADD_WITH_ALPHA;
 636	LLUIImage* imagep = NULL;
 637	if (pressed && mDisplayPressedState)
 638	{
 639		imagep = selected ? mImagePressedSelected : mImagePressed;
 640	}
 641	else if ( mNeedsHighlight )
 642	{
 643		if (selected)
 644		{
 645			if (mImageHoverSelected)
 646			{
 647				imagep = mImageHoverSelected;
 648			}
 649			else
 650			{
 651				imagep = mImageSelected;
 652				use_glow_effect = TRUE;
 653			}
 654		}
 655		else
 656		{
 657			if (mImageHoverUnselected)
 658			{
 659				imagep = mImageHoverUnselected;
 660			}
 661			else
 662			{
 663				imagep = mImageUnselected;
 664				use_glow_effect = TRUE;
 665			}
 666		}
 667	}
 668	else 
 669	{
 670		imagep = selected ? mImageSelected : mImageUnselected;
 671	}
 672
 673	// Override if more data is available
 674	// HACK: Use gray checked state to mean either:
 675	//   enabled and tentative
 676	// or
 677	//   disabled but checked
 678	if (!mImageDisabledSelected.isNull() 
 679		&& 
 680			( (enabled && getTentative()) 
 681			|| (!enabled && selected ) ) )
 682	{
 683		imagep = mImageDisabledSelected;
 684	}
 685	else if (!mImageDisabled.isNull() 
 686		&& !enabled 
 687		&& !selected)
 688	{
 689		imagep = mImageDisabled;
 690	}
 691
 692	if (mFlashing)
 693	{
 694		// if button should flash and we have icon for flashing, use it as image for button
 695		if(flash && mImageFlash)
 696		{
 697			// setting flash to false to avoid its further influence on glow
 698			flash = false;
 699			imagep = mImageFlash;
 700		}
 701		// else use usual flashing via flash_color
 702		else
 703		{
 704			LLColor4 flash_color = mFlashBgColor.get();
 705			use_glow_effect = TRUE;
 706			glow_type = LLRender::BT_ALPHA; // blend the glow
 707			if (mNeedsHighlight) // highlighted AND flashing
 708				glow_color = (glow_color*0.5f + flash_color*0.5f) % 2.0f; // average between flash and highlight colour, with sum of the opacity
 709			else
 710				glow_color = flash_color;
 711		}
 712	}
 713
 714	if (mNeedsHighlight && !imagep)
 715	{
 716		use_glow_effect = TRUE;
 717	}
 718
 719	// Figure out appropriate color for the text
 720	LLColor4 label_color;
 721
 722	// label changes when button state changes, not when pressed
 723	if ( enabled )
 724	{
 725		if ( getToggleState() )
 726		{
 727			label_color = mSelectedLabelColor.get();
 728		}
 729		else
 730		{
 731			label_color = mUnselectedLabelColor.get();
 732		}
 733	}
 734	else
 735	{
 736		if ( getToggleState() )
 737		{
 738			label_color = mDisabledSelectedLabelColor.get();
 739		}
 740		else
 741		{
 742			label_color = mDisabledLabelColor.get();
 743		}
 744	}
 745
 746	// Unselected label assignments
 747	LLWString label = getCurrentLabel();
 748
 749	// overlay with keyboard focus border
 750	if (hasFocus())
 751	{
 752		F32 lerp_amt = gFocusMgr.getFocusFlashAmt();
 753		drawBorder(imagep, gFocusMgr.getFocusColor() % alpha, llround(lerp(1.f, 3.f, lerp_amt)));
 754	}
 755	
 756	if (use_glow_effect)
 757	{
 758		mCurGlowStrength = lerp(mCurGlowStrength,
 759					mFlashing ? (flash? 1.0 : 0.0)
 760					: mHoverGlowStrength,
 761					LLCriticalDamp::getInterpolant(0.05f));
 762	}
 763	else
 764	{
 765		mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLCriticalDamp::getInterpolant(0.05f));
 766	}
 767
 768	// Draw button image, if available.
 769	// Otherwise draw basic rectangular button.
 770	if (imagep != NULL)
 771	{
 772		// apply automatic 50% alpha fade to disabled image
 773		LLColor4 disabled_color = mFadeWhenDisabled ? mDisabledImageColor.get() % 0.5f : mDisabledImageColor.get();
 774		if ( mScaleImage)
 775		{
 776			imagep->draw(getLocalRect(), (enabled ? mImageColor.get() : disabled_color) % alpha  );
 777			if (mCurGlowStrength > 0.01f)
 778			{
 779				gGL.setSceneBlendType(glow_type);
 780				imagep->drawSolid(0, 0, getRect().getWidth(), getRect().getHeight(), glow_color % (mCurGlowStrength * alpha));
 781				gGL.setSceneBlendType(LLRender::BT_ALPHA);
 782			}
 783		}
 784		else
 785		{
 786			imagep->draw(0, 0, (enabled ? mImageColor.get() : disabled_color) % alpha );
 787			if (mCurGlowStrength > 0.01f)
 788			{
 789				gGL.setSceneBlendType(glow_type);
 790				imagep->drawSolid(0, 0, glow_color % (mCurGlowStrength * alpha));
 791				gGL.setSceneBlendType(LLRender::BT_ALPHA);
 792			}
 793		}
 794	}
 795	else
 796	{
 797		// no image
 798		lldebugs << "No image for button " << getName() << llendl;
 799		// draw it in pink so we can find it
 800		gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4::pink1 % alpha, FALSE);
 801	}
 802
 803	// let overlay image and text play well together
 804	S32 text_left = mLeftHPad;
 805	S32 text_right = getRect().getWidth() - mRightHPad;
 806	S32 text_width = getRect().getWidth() - mLeftHPad - mRightHPad;
 807
 808	// draw overlay image
 809	if (mImageOverlay.notNull())
 810	{
 811		// get max width and height (discard level 0)
 812		S32 overlay_width;
 813		S32 overlay_height;
 814
 815		getOverlayImageSize(overlay_width, overlay_height);
 816
 817		S32 center_x = getLocalRect().getCenterX();
 818		S32 center_y = getLocalRect().getCenterY();
 819
 820		//FUGLY HACK FOR "DEPRESSED" BUTTONS
 821		if (pressed && mDisplayPressedState)
 822		{
 823			center_y--;
 824			center_x++;
 825		}
 826
 827		center_y += (mImageOverlayBottomPad - mImageOverlayTopPad);
 828		// fade out overlay images on disabled buttons
 829		LLColor4 overlay_color = mImageOverlayColor.get();
 830		if (!enabled)
 831		{
 832			overlay_color = mImageOverlayDisabledColor.get();
 833		}
 834		else if (getToggleState())
 835		{
 836			overlay_color = mImageOverlaySelectedColor.get();
 837		}
 838		overlay_color.mV[VALPHA] *= alpha;
 839
 840		switch(mImageOverlayAlignment)
 841		{
 842		case LLFontGL::LEFT:
 843			text_left += overlay_width + mImgOverlayLabelSpace;
 844			text_width -= overlay_width + mImgOverlayLabelSpace;
 845			mImageOverlay->draw(
 846				mLeftHPad,
 847				center_y - (overlay_height / 2), 
 848				overlay_width, 
 849				overlay_height, 
 850				overlay_color);
 851			break;
 852		case LLFontGL::HCENTER:
 853			mImageOverlay->draw(
 854				center_x - (overlay_width / 2), 
 855				center_y - (overlay_height / 2), 
 856				overlay_width, 
 857				overlay_height, 
 858				overlay_color);
 859			break;
 860		case LLFontGL::RIGHT:
 861			text_right -= overlay_width + mImgOverlayLabelSpace;
 862			text_width -= overlay_width + mImgOverlayLabelSpace;
 863			mImageOverlay->draw(
 864				getRect().getWidth() - mRightHPad - overlay_width,
 865				center_y - (overlay_height / 2), 
 866				overlay_width, 
 867				overlay_height, 
 868				overlay_color);
 869			break;
 870		default:
 871			// draw nothing
 872			break;
 873		}
 874	}
 875
 876	// Draw label
 877	if( !label.empty() )
 878	{
 879		LLWStringUtil::trim(label);
 880
 881		S32 x;
 882		switch( mHAlign )
 883		{
 884		case LLFontGL::RIGHT:
 885			x = text_right;
 886			break;
 887		case LLFontGL::HCENTER:
 888			x = text_left + (text_width / 2);
 889			break;
 890		case LLFontGL::LEFT:
 891		default:
 892			x = text_left;
 893			break;
 894		}
 895
 896		S32 y_offset = 2 + (getRect().getHeight() - 20)/2;
 897	
 898		if (pressed && mDisplayPressedState)
 899		{
 900			y_offset--;
 901			x++;
 902		}
 903
 904		// *NOTE: mantipov: before mUseEllipses is implemented in EXT-279 U32_MAX has been passed as
 905		// max_chars.
 906		// LLFontGL::render expects S32 max_chars variable but process in a separate way -1 value.
 907		// Due to U32_MAX is equal to S32 -1 value I have rest this value for non-ellipses mode.
 908		// Not sure if it is really needed. Probably S32_MAX should be always passed as max_chars.
 909		mLastDrawCharsCount = mGLFont->render(label, 0,
 910			(F32)x,
 911			(F32)(mBottomVPad + y_offset),
 912			label_color % alpha,
 913			mHAlign, LLFontGL::BOTTOM,
 914			LLFontGL::NORMAL,
 915			mDropShadowedText ? LLFontGL::DROP_SHADOW_SOFT : LLFontGL::NO_SHADOW,
 916			S32_MAX, text_width,
 917			NULL, mUseEllipses);
 918	}
 919
 920	LLUICtrl::draw();
 921}
 922
 923void LLButton::drawBorder(LLUIImage* imagep, const LLColor4& color, S32 size)
 924{
 925	if (imagep == NULL) return;
 926	if (mScaleImage)
 927	{
 928		imagep->drawBorder(getLocalRect(), color, size);
 929	}
 930	else
 931	{
 932		imagep->drawBorder(0, 0, color, size);
 933	}
 934}
 935
 936BOOL LLButton::getToggleState() const
 937{
 938    return getValue().asBoolean();
 939}
 940
 941void LLButton::setToggleState(BOOL b)
 942{
 943	if( b != getToggleState() )
 944	{
 945		setControlValue(b); // will fire LLControlVariable callbacks (if any)
 946		setValue(b);        // may or may not be redundant
 947		// Unselected label assignments
 948		autoResize();
 949	}
 950}
 951
 952void LLButton::setFlashing( BOOL b )	
 953{ 
 954	if ((bool)b != mFlashing)
 955	{
 956		mFlashing = b; 
 957		mFlashingTimer.reset();
 958	}
 959}
 960
 961
 962BOOL LLButton::toggleState()			
 963{
 964    bool flipped = ! getToggleState();
 965	setToggleState(flipped); 
 966
 967	return flipped; 
 968}
 969
 970void LLButton::setLabel( const LLStringExplicit& label )
 971{
 972	setLabelUnselected(label);
 973	setLabelSelected(label);
 974}
 975
 976//virtual
 977BOOL LLButton::setLabelArg( const std::string& key, const LLStringExplicit& text )
 978{
 979	mUnselectedLabel.setArg(key, text);
 980	mSelectedLabel.setArg(key, text);
 981	return TRUE;
 982}
 983
 984void LLButton::setLabelUnselected( const LLStringExplicit& label )
 985{
 986	mUnselectedLabel = label;
 987}
 988
 989void LLButton::setLabelSelected( const LLStringExplicit& label )
 990{
 991	mSelectedLabel = label;
 992}
 993
 994bool LLButton::labelIsTruncated() const
 995{
 996	return getCurrentLabel().getString().size() > mLastDrawCharsCount;
 997}
 998
 999const LLUIString& LLButton::getCurrentLabel() const
1000{
1001	if( getToggleState() )
1002	{
1003		return mSelectedLabel;
1004	}
1005	else
1006	{
1007		return mUnselectedLabel;
1008	}
1009}
1010
1011void LLButton::setImageUnselected(LLPointer<LLUIImage> image)
1012{
1013	mImageUnselected = image;
1014	if (mImageUnselected.isNull())
1015	{
1016		llwarns << "Setting default button image for: " << getName() << " to NULL" << llendl;
1017	}
1018}
1019
1020void LLButton::autoResize()
1021{
1022	resize(getCurrentLabel());
1023}
1024
1025void LLButton::resize(LLUIString label)
1026{
1027	// get label length 
1028	S32 label_width = mGLFont->getWidth(label.getString());
1029	// get current btn length 
1030	S32 btn_width =getRect().getWidth();
1031    // check if it need resize 
1032	if (mAutoResize)
1033	{ 
1034		S32 min_width = label_width + mLeftHPad + mRightHPad;
1035		if (mImageOverlay)
1036		{
1037			S32 overlay_width = mImageOverlay->getWidth();
1038			F32 scale_factor = (getRect().getHeight() - (mImageOverlayBottomPad + mImageOverlayTopPad)) / (F32)mImageOverlay->getHeight();
1039			overlay_width = llround((F32)overlay_width * scale_factor);
1040
1041			switch(mImageOverlayAlignment)
1042			{
1043			case LLFontGL::LEFT:
1044			case LLFontGL::RIGHT:
1045				min_width += overlay_width + mImgOverlayLabelSpace;
1046				break;
1047			case LLFontGL::HCENTER:
1048				min_width = llmax(min_width, overlay_width + mLeftHPad + mRightHPad);
1049				break;
1050			default:
1051				// draw nothing
1052				break;
1053			}
1054		}
1055		if (btn_width < min_width)
1056		{
1057			reshape(min_width, getRect().getHeight());
1058		}
1059	} 
1060}
1061void LLButton::setImages( const std::string &image_name, const std::string &selected_name )
1062{
1063	setImageUnselected(LLUI::getUIImage(image_name));
1064	setImageSelected(LLUI::getUIImage(selected_name));
1065}
1066
1067void LLButton::setImageSelected(LLPointer<LLUIImage> image)
1068{
1069	mImageSelected = image;
1070}
1071
1072void LLButton::setImageColor(const LLColor4& c)		
1073{ 
1074	mImageColor = c; 
1075}
1076
1077void LLButton::setColor(const LLColor4& color)
1078{
1079	setImageColor(color);
1080}
1081
1082void LLButton::setImageDisabled(LLPointer<LLUIImage> image)
1083{
1084	mImageDisabled = image;
1085	mDisabledImageColor = mImageColor;
1086	mFadeWhenDisabled = TRUE;
1087}
1088
1089void LLButton::setImageDisabledSelected(LLPointer<LLUIImage> image)
1090{
1091	mImageDisabledSelected = image;
1092	mDisabledImageColor = mImageColor;
1093	mFadeWhenDisabled = TRUE;
1094}
1095
1096void LLButton::setImagePressed(LLPointer<LLUIImage> image)
1097{
1098	mImagePressed = image;
1099}
1100
1101void LLButton::setImageHoverSelected(LLPointer<LLUIImage> image)
1102{
1103	mImageHoverSelected = image;
1104}
1105
1106void LLButton::setImageHoverUnselected(LLPointer<LLUIImage> image)
1107{
1108	mImageHoverUnselected = image;
1109}
1110
1111void LLButton::setImageFlash(LLPointer<LLUIImage> image)
1112{
1113	mImageFlash = image;
1114}
1115
1116void LLButton::setImageOverlay(const std::string& image_name, LLFontGL::HAlign alignment, const LLColor4& color)
1117{
1118	if (image_name.empty())
1119	{
1120		mImageOverlay = NULL;
1121	}
1122	else
1123	{
1124		mImageOverlay = LLUI::getUIImage(image_name);
1125		mImageOverlayAlignment = alignment;
1126		mImageOverlayColor = color;
1127	}
1128}
1129
1130void LLButton::setImageOverlay(const LLUUID& image_id, LLFontGL::HAlign alignment, const LLColor4& color)
1131{
1132	if (image_id.isNull())
1133	{
1134		mImageOverlay = NULL;
1135	}
1136	else
1137	{
1138		mImageOverlay = LLUI::getUIImageByID(image_id);
1139		mImageOverlayAlignment = alignment;
1140		mImageOverlayColor = color;
1141	}
1142}
1143
1144void LLButton::onMouseCaptureLost()
1145{
1146	resetMouseDownTimer();
1147}
1148
1149//-------------------------------------------------------------------------
1150// Utilities
1151//-------------------------------------------------------------------------
1152S32 round_up(S32 grid, S32 value)
1153{
1154	S32 mod = value % grid;
1155
1156	if (mod > 0)
1157	{
1158		// not even multiple
1159		return value + (grid - mod);
1160	}
1161	else
1162	{
1163		return value;
1164	}
1165}
1166
1167void LLButton::addImageAttributeToXML(LLXMLNodePtr node, 
1168									  const std::string& image_name,
1169									  const LLUUID&	image_id,
1170									  const std::string& xml_tag_name) const
1171{
1172	if( !image_name.empty() )
1173	{
1174		node->createChild(xml_tag_name.c_str(), TRUE)->setStringValue(image_name);
1175	}
1176	else if( image_id != LLUUID::null )
1177	{
1178		node->createChild((xml_tag_name + "_id").c_str(), TRUE)->setUUIDValue(image_id);
1179	}
1180}
1181
1182
1183// static
1184void LLButton::toggleFloaterAndSetToggleState(LLUICtrl* ctrl, const LLSD& sdname)
1185{
1186	bool floater_vis = LLFloaterReg::toggleInstance(sdname.asString());
1187	LLButton* button = dynamic_cast<LLButton*>(ctrl);
1188	if (button)
1189		button->setToggleState(floater_vis);
1190}
1191
1192// static
1193// Gets called once
1194void LLButton::setFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname)
1195{
1196	LLButton* button = dynamic_cast<LLButton*>(ctrl);
1197	if (!button)
1198		return;
1199	// Get the visibility control name for the floater
1200	std::string vis_control_name = LLFloaterReg::declareVisibilityControl(sdname.asString());
1201	// Set the button control value (toggle state) to the floater visibility control (Sets the value as well)
1202	button->setControlVariable(LLFloater::getControlGroup()->getControl(vis_control_name));
1203	// Set the clicked callback to toggle the floater
1204	button->setClickedCallback(boost::bind(&LLFloaterReg::toggleInstance, sdname, LLSD()));
1205}
1206
1207// static
1208void LLButton::setDockableFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname)
1209{
1210	LLButton* button = dynamic_cast<LLButton*>(ctrl);
1211	if (!button)
1212		return;
1213	// Get the visibility control name for the floater
1214	std::string vis_control_name = LLFloaterReg::declareVisibilityControl(sdname.asString());
1215	// Set the button control value (toggle state) to the floater visibility control (Sets the value as well)
1216	button->setControlVariable(LLFloater::getControlGroup()->getControl(vis_control_name));
1217	// Set the clicked callback to toggle the floater
1218	button->setClickedCallback(boost::bind(&LLDockableFloater::toggleInstance, sdname));
1219}
1220
1221// static
1222void LLButton::showHelp(LLUICtrl* ctrl, const LLSD& sdname)
1223{
1224	// search back through the button's parents for a panel
1225	// with a help_topic string defined
1226	std::string help_topic;
1227	if (LLUI::sHelpImpl &&
1228	    ctrl->findHelpTopic(help_topic))
1229	{
1230		LLUI::sHelpImpl->showTopic(help_topic);
1231		return; // success
1232	}
1233
1234	// display an error if we can't find a help_topic string.
1235	// fix this by adding a help_topic attribute to the xui file
1236	LLNotificationsUtil::add("UnableToFindHelpTopic");
1237}
1238
1239void LLButton::resetMouseDownTimer()
1240{
1241	mMouseDownTimer.stop();
1242	mMouseDownTimer.reset();
1243}
1244
1245BOOL LLButton::handleDoubleClick(S32 x, S32 y, MASK mask)
1246{
1247	// just treat a double click as a second click
1248	return handleMouseDown(x, y, mask);
1249}