Skip to content
Snippets Groups Projects
llbutton.cpp 33.9 KiB
Newer Older
James Cook's avatar
James Cook committed
/** 
 * @file llbutton.cpp
 * @brief LLButton base class
 *
 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
James Cook's avatar
James Cook committed
 */

#include "linden_common.h"

James Cook's avatar
James Cook committed
#include "llbutton.h"

// Linden library includes
#include "v4color.h"
#include "llstring.h"

// Project includes
#include "llkeyboard.h"
#include "llui.h"
#include "lluiconstants.h"
#include "llresmgr.h"
#include "llcriticaldamp.h"
#include "llfloater.h"
#include "llfloaterreg.h"
James Cook's avatar
James Cook committed
#include "llfocusmgr.h"
#include "llwindow.h"
#include "llrender.h"
#include "lluictrlfactory.h"
#include "llhelp.h"
Steven Bennetts's avatar
Steven Bennetts committed
#include "lldockablefloater.h"
#include "llviewereventrecorder.h"
James Cook's avatar
James Cook committed

static LLDefaultChildRegistry::Register<LLButton> r("button");
// Compiler optimization, generate extern template
template class LLButton* LLView::getChild<class LLButton>(
James Cook's avatar
James Cook committed
// globals loaded from settings.xml
James Cook's avatar
James Cook committed
S32 BTN_HEIGHT_SMALL= 0;
S32 BTN_HEIGHT		= 0;

LLButton::Params::Params()
:	label_selected("label_selected"),				// requires is_toggle true
	auto_resize("auto_resize", false),
	use_ellipses("use_ellipses", false),
	image_unselected("image_unselected"),
	image_selected("image_selected"),
	image_hover_selected("image_hover_selected"),
	image_hover_unselected("image_hover_unselected"),
	image_disabled_selected("image_disabled_selected"),
	image_disabled("image_disabled"),
	image_pressed("image_pressed"),
	image_pressed_selected("image_pressed_selected"),
	image_overlay("image_overlay"),
	image_overlay_alignment("image_overlay_alignment", std::string("center")),
	image_overlay_enable("image_overlay_enable", true),
	image_top_pad("image_top_pad"),
	image_bottom_pad("image_bottom_pad"),
	imgoverlay_label_space("imgoverlay_label_space", 1),
	label_color("label_color"),
	label_color_selected("label_color_selected"),	// requires is_toggle true
	label_color_disabled("label_color_disabled"),
	label_color_disabled_selected("label_color_disabled_selected"),
	image_color("image_color"),
	image_color_disabled("image_color_disabled"),
	image_overlay_color("image_overlay_color", LLColor4::white % 0.75f),
	image_overlay_disabled_color("image_overlay_disabled_color", LLColor4::white % 0.3f),
	image_overlay_selected_color("image_overlay_selected_color", LLColor4::white),
	flash_color("flash_color"),
	pad_right("pad_right", LLBUTTON_H_PAD),
	pad_left("pad_left", LLBUTTON_H_PAD),
	click_callback("click_callback"),
	mouse_down_callback("mouse_down_callback"),
	mouse_up_callback("mouse_up_callback"),
	mouse_held_callback("mouse_held_callback"),
	is_toggle("is_toggle", false),
	scale_image("scale_image", true),
	hover_glow_amount("hover_glow_amount"),
	commit_on_return("commit_on_return", true),
	display_pressed_state("display_pressed_state", true),
	use_draw_context_alpha("use_draw_context_alpha", true),
Leyla Farazha's avatar
Leyla Farazha committed
	badge("badge"),
	handle_right_mouse("handle_right_mouse"),
	button_flash_enable("button_flash_enable", false),
	button_flash_count("button_flash_count"),
	button_flash_rate("button_flash_rate")
James Cook's avatar
James Cook committed
{
	addSynonym(is_toggle, "toggle");
	changeDefault(initial_value, LLSD(false));
}


LLButton::LLButton(const LLButton::Params& p)
:	LLUICtrl(p),
	mMouseDownFrame(0),
	mMouseHeldDownCount(0),
James Cook's avatar
James Cook committed
	mBorderEnabled( FALSE ),
	mFlashing( FALSE ),
	mCurGlowStrength(0.f),
	mNeedsHighlight(FALSE),
	mUnselectedLabel(p.label()),
	mSelectedLabel(p.label_selected()),
	mGLFont(p.font),
	mHeldDownDelay(p.held_down_delay.seconds),			// seconds until held-down callback is called
	mHeldDownFrameDelay(p.held_down_delay.frames),
	mImageOverlayEnable(p.image_overlay_enable),
	mImageUnselected(p.image_unselected),
	mImageSelected(p.image_selected),
	mImageDisabled(p.image_disabled),
	mImageDisabledSelected(p.image_disabled_selected),
	mImagePressed(p.image_pressed),
	mImagePressedSelected(p.image_pressed_selected),
	mImageHoverSelected(p.image_hover_selected),
	mImageHoverUnselected(p.image_hover_unselected),
	mUnselectedLabelColor(p.label_color()),
	mSelectedLabelColor(p.label_color_selected()),
	mDisabledLabelColor(p.label_color_disabled()),
	mDisabledSelectedLabelColor(p.label_color_disabled_selected()),
	mImageColor(p.image_color()),
	mFlashBgColor(p.flash_color()),
	mDisabledImageColor(p.image_color_disabled()),
	mImageOverlay(p.image_overlay()),
	mImageOverlayColor(p.image_overlay_color()),
	mImageOverlayDisabledColor(p.image_overlay_disabled_color()),
	mImageOverlaySelectedColor(p.image_overlay_selected_color()),
	mImageOverlayAlignment(LLFontGL::hAlignFromName(p.image_overlay_alignment)),
	mImageOverlayTopPad(p.image_top_pad),
	mImageOverlayBottomPad(p.image_bottom_pad),
	mImgOverlayLabelSpace(p.imgoverlay_label_space),
	mIsToggle(p.is_toggle),
	mScaleImage(p.scale_image),
	mAutoResize(p.auto_resize),
	mHAlign(p.font_halign),
	mLeftHPad(p.pad_left),
	mRightHPad(p.pad_right),
	mHoverGlowStrength(p.hover_glow_amount),
	mCommitOnReturn(p.commit_on_return),
	mForcePressedState(false),
	mDisplayPressedState(p.display_pressed_state),
	mLastDrawCharsCount(0),
	mMouseDownSignal(NULL),
	mMouseUpSignal(NULL),
Leyla Farazha's avatar
Leyla Farazha committed
	mUseDrawContextAlpha(p.use_draw_context_alpha),
	mHandleRightMouse(p.handle_right_mouse),
	mFlashingTimer(NULL)
James Cook's avatar
James Cook committed
{
	if (p.button_flash_enable)
	{
		// If optional parameter "p.button_flash_count" is not provided, LLFlashTimer will be
		// used instead it a "default" value from gSavedSettings.getS32("FlashCount")).
		// Likewise, missing "p.button_flash_rate" is replaced by gSavedSettings.getF32("FlashPeriod").
		// Note: flashing should be allowed in settings.xml (boolean key "EnableButtonFlashing").
		S32 flash_count = p.button_flash_count.isProvided()? p.button_flash_count : 0;
		F32 flash_rate = p.button_flash_rate.isProvided()? p.button_flash_rate : 0.0;
		mFlashingTimer = new LLFlashTimer ((LLFlashTimer::callback_t)NULL, flash_count, flash_rate);
	}
	else
	{
		mButtonFlashCount = p.button_flash_count;
		mButtonFlashRate = p.button_flash_rate;
	}
	static LLUICachedControl<S32> llbutton_orig_h_pad ("UIButtonOrigHPad", 0);
	static Params default_params(LLUICtrlFactory::getDefaultParams<LLButton>());
	if (!p.label_selected.isProvided())
James Cook's avatar
James Cook committed
	{
		mSelectedLabel = mUnselectedLabel;
James Cook's avatar
James Cook committed
	}

	// Hack to make sure there is space for at least one character
	if (getRect().getWidth() - (mRightHPad + mLeftHPad) < mGLFont->getWidth(std::string(" ")))
James Cook's avatar
James Cook committed
	{
		// Use old defaults
		mLeftHPad = llbutton_orig_h_pad;
		mRightHPad = llbutton_orig_h_pad;
James Cook's avatar
James Cook committed
	}
	
	mMouseDownTimer.stop();

	// if custom unselected button image provided...
	if (p.image_unselected != default_params.image_unselected)
	{
		//...fade it out for disabled image by default...
		if (p.image_disabled() == default_params.image_disabled() )
		{
			mImageDisabled = p.image_unselected;
			mFadeWhenDisabled = TRUE;
		}

		if (p.image_pressed_selected == default_params.image_pressed_selected)
		{
			mImagePressedSelected = mImageUnselected;
		}
James Cook's avatar
James Cook committed

	// if custom selected button image provided...
	if (p.image_selected != default_params.image_selected)
James Cook's avatar
James Cook committed
	{
		//...fade it out for disabled image by default...
		if (p.image_disabled_selected() == default_params.image_disabled_selected())
		{
			mImageDisabledSelected = p.image_selected;
			mFadeWhenDisabled = TRUE;
		}

		if (p.image_pressed == default_params.image_pressed)
		{
			mImagePressed = mImageSelected;
		}
	}

	if (!p.image_pressed.isProvided())
	{
		mImagePressed = mImageSelected;
	}

	if (!p.image_pressed_selected.isProvided())
	{
		mImagePressedSelected = mImageUnselected;
		LL_WARNS() << "Button: " << getName() << " with no image!" << LL_ENDL;
James Cook's avatar
James Cook committed
	}
	
	if (p.click_callback.isProvided())
	{
		setCommitCallback(initCommitCallback(p.click_callback)); // alias -> commit_callback
	}
	if (p.mouse_down_callback.isProvided())
	{
		setMouseDownCallback(initCommitCallback(p.mouse_down_callback));
	}
	if (p.mouse_up_callback.isProvided())
	{
		setMouseUpCallback(initCommitCallback(p.mouse_up_callback));
	}
	if (p.mouse_held_callback.isProvided())
	{
		setHeldDownCallback(initCommitCallback(p.mouse_held_callback));
	}
	if (p.badge.isProvided())
	{
		LLBadgeOwner::initBadgeParams(p.badge());
}

LLButton::~LLButton()
{
	delete mMouseDownSignal;
	delete mMouseUpSignal;
	delete mHeldDownSignal;
James Cook's avatar
James Cook committed
}

// HACK: Committing a button is the same as instantly clicking it.
// virtual
void LLButton::onCommit()
{
	// WARNING: Sometimes clicking a button destroys the floater or
	// panel containing it.  Therefore we need to call 	LLUICtrl::onCommit()
James Cook's avatar
James Cook committed
	// LAST, otherwise this becomes deleted memory.

	if (mMouseDownSignal) (*mMouseDownSignal)(this, LLSD());
James Cook's avatar
James Cook committed
	
	if (mMouseUpSignal) (*mMouseUpSignal)(this, LLSD());
James Cook's avatar
James Cook committed

James Cook's avatar
James Cook committed
	{
		make_ui_sound("UISndClick");
	}

James Cook's avatar
James Cook committed
	{
		make_ui_sound("UISndClickRelease");
	}
Jon Wolk's avatar
Jon Wolk committed
	if (mIsToggle)
	{
		toggleState();
	}

	// do this last, as it can result in destroying this button
boost::signals2::connection LLButton::setClickedCallback(const CommitCallbackParam& cb)
{
	return setClickedCallback(initCommitCallback(cb));
}
boost::signals2::connection LLButton::setMouseDownCallback(const CommitCallbackParam& cb)
{
	return setMouseDownCallback(initCommitCallback(cb));
}
boost::signals2::connection LLButton::setMouseUpCallback(const CommitCallbackParam& cb)
{
	return setMouseUpCallback(initCommitCallback(cb));
}
boost::signals2::connection LLButton::setHeldDownCallback(const CommitCallbackParam& cb)
{
	return setHeldDownCallback(initCommitCallback(cb));
}


boost::signals2::connection LLButton::setClickedCallback( const commit_signal_t::slot_type& cb )
	if (!mCommitSignal) mCommitSignal = new commit_signal_t();
	return mCommitSignal->connect(cb);
boost::signals2::connection LLButton::setMouseDownCallback( const commit_signal_t::slot_type& cb )
	if (!mMouseDownSignal) mMouseDownSignal = new commit_signal_t();
	return mMouseDownSignal->connect(cb);
boost::signals2::connection LLButton::setMouseUpCallback( const commit_signal_t::slot_type& cb )
	if (!mMouseUpSignal) mMouseUpSignal = new commit_signal_t();
	return mMouseUpSignal->connect(cb);
boost::signals2::connection LLButton::setHeldDownCallback( const commit_signal_t::slot_type& cb )
	if (!mHeldDownSignal) mHeldDownSignal = new commit_signal_t();
	return mHeldDownSignal->connect(cb);
James Cook's avatar
James Cook committed

// *TODO: Deprecate (for backwards compatibility only)
boost::signals2::connection LLButton::setClickedCallback( button_callback_t cb, void* data )
{
	return setClickedCallback(boost::bind(cb, data));
}
boost::signals2::connection LLButton::setMouseDownCallback( button_callback_t cb, void* data )
{
	return setMouseDownCallback(boost::bind(cb, data));
}
boost::signals2::connection LLButton::setMouseUpCallback( button_callback_t cb, void* data )
{
	return setMouseUpCallback(boost::bind(cb, data));
}
boost::signals2::connection LLButton::setHeldDownCallback( button_callback_t cb, void* data )
{
	return setHeldDownCallback(boost::bind(cb, data));
}
BOOL LLButton::postBuild()
{
	autoResize();
BOOL LLButton::handleUnicodeCharHere(llwchar uni_char)
James Cook's avatar
James Cook committed
{
	BOOL handled = FALSE;
	if(' ' == uni_char 
		&& !gKeyboard->getKeyRepeated(' '))
James Cook's avatar
James Cook committed
	{
Jon Wolk's avatar
Jon Wolk committed
		if (mIsToggle)
		{
			toggleState();
		}

		handled = TRUE;		
James Cook's avatar
James Cook committed
	}
	return handled;	
}

BOOL LLButton::handleKeyHere(KEY key, MASK mask )
James Cook's avatar
James Cook committed
{
	BOOL handled = FALSE;
	if( mCommitOnReturn && KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key))
James Cook's avatar
James Cook committed
	{
James Cook's avatar
James Cook committed
		{
James Cook's avatar
James Cook committed
	}
	return handled;
}


BOOL LLButton::handleMouseDown(S32 x, S32 y, MASK mask)
{
	if (!childrenHandleMouseDown(x, y, mask))
James Cook's avatar
James Cook committed
	{
		// Route future Mouse messages here preemptively.  (Release on mouse up.)
		gFocusMgr.setMouseCapture( this );
James Cook's avatar
James Cook committed

		if (hasTabStop() && !getIsChrome())
		{
			setFocus(TRUE);
		}
James Cook's avatar
James Cook committed

		if (!mFunctionName.empty())
		{
			LL_DEBUGS("UIUsage") << "calling mouse down function " << mFunctionName << LL_ENDL;
		}

Steven Bennetts's avatar
Steven Bennetts committed
		/*
		 * ATTENTION! This call fires another mouse down callback.
		 * If you wish to remove this call emit that signal directly
		 * by calling LLUICtrl::mMouseDownSignal(x, y, mask);
		 */
		LLUICtrl::handleMouseDown(x, y, mask);

#if AL_VIEWER_EVENT_RECORDER
		if(LLViewerEventRecorder::getLoggingStatus())
		{
			LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname());
		}
#endif
		if(mMouseDownSignal) (*mMouseDownSignal)(this, LLSD());
James Cook's avatar
James Cook committed

		mMouseDownTimer.start();
		mMouseDownFrame = (S32) LLFrameTimer::getFrameCount();
		mMouseHeldDownCount = 0;
Steven Bennetts's avatar
Steven Bennetts committed

		
		if (getSoundFlags() & MOUSE_DOWN)
		{
			make_ui_sound("UISndClick");
		}
	}
James Cook's avatar
James Cook committed
	return TRUE;
}


BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask)
{
	// We only handle the click if the click both started and ended within us
James Cook's avatar
James Cook committed
	{
		// Always release the mouse
		gFocusMgr.setMouseCapture( NULL );

Steven Bennetts's avatar
Steven Bennetts committed
		/*
		 * ATTENTION! This call fires another mouse up callback.
		 * If you wish to remove this call emit that signal directly
		 * by calling LLUICtrl::mMouseUpSignal(x, y, mask);
		 */
		LLUICtrl::handleMouseUp(x, y, mask);

#if AL_VIEWER_EVENT_RECORDER
		if (LLViewerEventRecorder::getLoggingStatus())
		{
			LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname()); 
		}
#endif
Steven Bennetts's avatar
Steven Bennetts committed

James Cook's avatar
James Cook committed
		// Regardless of where mouseup occurs, handle callback
		if(mMouseUpSignal) (*mMouseUpSignal)(this, LLSD());
James Cook's avatar
James Cook committed

James Cook's avatar
James Cook committed
		// DO THIS AT THE VERY END to allow the button to be destroyed as a result of being clicked.
		// If mouseup in the widget, it's been clicked
		if (pointInView(x, y))
		{
James Cook's avatar
James Cook committed
			{
				make_ui_sound("UISndClickRelease");
			}

Jon Wolk's avatar
Jon Wolk committed
			if (mIsToggle)
			{
				toggleState();
			}

James Cook's avatar
James Cook committed
		}
	}
James Cook's avatar
James Cook committed

	return TRUE;
}

BOOL	LLButton::handleRightMouseDown(S32 x, S32 y, MASK mask)
{
Leyla Farazha's avatar
Leyla Farazha committed
	if (mHandleRightMouse && !childrenHandleRightMouseDown(x, y, mask))
		// Route future Mouse messages here preemptively.  (Release on mouse up.)
		gFocusMgr.setMouseCapture( this );
		if (hasTabStop() && !getIsChrome())
		{
			setFocus(TRUE);
		}
//		if (pointInView(x, y))
//		{
//		}
Leyla Farazha's avatar
Leyla Farazha committed
		// send the mouse down signal
		LLUICtrl::handleRightMouseDown(x,y,mask);
		// *TODO: Return result of LLUICtrl call above?  Should defer to base class
		// but this might change the mouse handling of existing buttons in a bad way
		// if they are not mouse opaque.
Leyla Farazha's avatar
Leyla Farazha committed

	return TRUE;
}

BOOL	LLButton::handleRightMouseUp(S32 x, S32 y, MASK mask)
James Cook's avatar
James Cook committed
{
Leyla Farazha's avatar
Leyla Farazha committed
	if (mHandleRightMouse)
Leyla Farazha's avatar
Leyla Farazha committed
		// We only handle the click if the click both started and ended within us
		if( hasMouseCapture() )
		{
			// Always release the mouse
			gFocusMgr.setMouseCapture( NULL );
James Cook's avatar
James Cook committed

Leyla Farazha's avatar
Leyla Farazha committed
	//		if (pointInView(x, y))
	//		{
	//			mRightMouseUpSignal(this, x,y,mask);
	//		}
		}
		else 
		{
			childrenHandleRightMouseUp(x, y, mask);
		}
	
		// send the mouse up signal
		LLUICtrl::handleRightMouseUp(x,y,mask);
		// *TODO: Return result of LLUICtrl call above?  Should defer to base class
		// but this might change the mouse handling of existing buttons in a bad way.
		// if they are not mouse opaque.
	return TRUE;
}

void LLButton::onMouseLeave(S32 x, S32 y, MASK mask)
{
Steven Bennetts's avatar
Steven Bennetts committed
	LLUICtrl::onMouseLeave(x, y, mask);

void LLButton::setHighlight(bool b)
{
	mNeedsHighlight = b;
}

BOOL LLButton::handleHover(S32 x, S32 y, MASK mask)
{
	if (isInEnabledChain() 
		&& (!gFocusMgr.getMouseCapture() || gFocusMgr.getMouseCapture() == this))
		mNeedsHighlight = TRUE;

James Cook's avatar
James Cook committed
	{
James Cook's avatar
James Cook committed
		{
			F32 elapsed = getHeldDownTime();
			if( mHeldDownDelay <= elapsed && mHeldDownFrameDelay <= (S32)LLFrameTimer::getFrameCount() - mMouseDownFrame)
			{
				LLSD param;
				param["count"] = mMouseHeldDownCount++;
				if (mHeldDownSignal) (*mHeldDownSignal)(this, param);
		// We only handle the click if the click both started and ended within us
		getWindow()->setCursor(UI_CURSOR_ARROW);
		LL_DEBUGS("UserInput") << "hover handled by " << getName() << LL_ENDL;
void LLButton::getOverlayImageSize(S32& overlay_width, S32& overlay_height)
{
	overlay_width = mImageOverlay->getWidth();
	overlay_height = mImageOverlay->getHeight();

	F32 scale_factor = llmin((F32)getRect().getWidth() / (F32)overlay_width, (F32)getRect().getHeight() / (F32)overlay_height, 1.f);
	overlay_width = ll_round((F32)overlay_width * scale_factor);
	overlay_height = ll_round((F32)overlay_height * scale_factor);
James Cook's avatar
James Cook committed

// virtual
void LLButton::draw()
{
	static const LLUICachedControl<bool> sEnableButtonFlashing("EnableButtonFlashing", true);
	F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency();
	bool pressed_by_keyboard = FALSE;
	if (hasFocus())
	{
		pressed_by_keyboard = gKeyboard->getKeyDown(' ') || (mCommitOnReturn && gKeyboard->getKeyDown(KEY_RETURN));
	}
James Cook's avatar
James Cook committed

	bool mouse_pressed_and_over = false;
	if (hasMouseCapture())
	{
		S32 local_mouse_x ;
		S32 local_mouse_y;
		LLUI::getMousePositionLocal(this, &local_mouse_x, &local_mouse_y);
		mouse_pressed_and_over = pointInView(local_mouse_x, local_mouse_y);
	}
	bool enabled = isInEnabledChain();

	bool pressed = pressed_by_keyboard 
	bool selected = getToggleState();
	bool use_glow_effect = FALSE;
	LLColor4 glow_color = LLColor4::white;
	LLRender::eBlendType glow_type = LLRender::BT_ADD_WITH_ALPHA;
    LLUIImage* imagep = NULL;
    LLUIImage* image_glow = NULL;

    //  Cancel sticking of color, if the button is pressed,
	//  or when a flashing of the previously selected button is ended
	if (mFlashingTimer
		&& ((selected && !mFlashingTimer->isFlashingInProgress() && !mForceFlashing) || pressed))
		imagep = selected ? mImagePressedSelected : mImagePressed;
	}
	else if ( mNeedsHighlight )
	{
		if (selected)
James Cook's avatar
James Cook committed
		{
			if (mImageHoverSelected)
James Cook's avatar
James Cook committed
			{
				imagep = mImageHoverSelected;
James Cook's avatar
James Cook committed
			}
			else
			{
				use_glow_effect = TRUE;
			if (mImageHoverUnselected)
James Cook's avatar
James Cook committed
			{
				imagep = mImageHoverUnselected;
James Cook's avatar
James Cook committed
			}
			else
			{
				imagep = mImageUnselected;
				use_glow_effect = TRUE;
James Cook's avatar
James Cook committed
			}
		}
		imagep = selected ? mImageSelected : mImageUnselected;

	// Override if more data is available
	// HACK: Use gray checked state to mean either:
	//   enabled and tentative
	// or
	//   disabled but checked
	if (!mImageDisabledSelected.isNull() 
		&& 
			( (enabled && getTentative()) 
			|| (!enabled && selected ) ) )
	{
		imagep = mImageDisabledSelected;
	}
	else if (!mImageDisabled.isNull() 
		&& !enabled 
		&& !selected)
	image_glow = imagep;

		if (flash && mImageFlash)
			// if button should flash and we have icon for flashing, use it as image for button
			image_glow = mImageFlash;

		// provide fade-in and fade-out via flash_color
		if (mFlashingTimer)
		{
			LLColor4 flash_color = mFlashBgColor.get();
			use_glow_effect = TRUE;
			glow_type = LLRender::BT_ALPHA; // blend the glow
			if (mFlashingTimer->isCurrentlyHighlighted() || !mFlashingTimer->isFlashingInProgress())
            else
            {
                // will fade from highlight color
                glow_color = flash_color;
            }
	if (mNeedsHighlight && !imagep)
James Cook's avatar
James Cook committed

	// Figure out appropriate color for the text
	LLColor4 label_color;

	// label changes when button state changes, not when pressed
James Cook's avatar
James Cook committed
		{
			label_color = mSelectedLabelColor.get();
James Cook's avatar
James Cook committed
		}
		else
		{
			label_color = mUnselectedLabelColor.get();
James Cook's avatar
James Cook committed
		}
			label_color = mDisabledSelectedLabelColor.get();
James Cook's avatar
James Cook committed
		{
			label_color = mDisabledLabelColor.get();
James Cook's avatar
James Cook committed
		}
	// Highlight if needed
	if( ll::ui::SearchableControl::getHighlighted() )
		label_color = ll::ui::SearchableControl::getHighlightColor();

	// Unselected label assignments
James Cook's avatar
James Cook committed

	// overlay with keyboard focus border
	if (hasFocus())
	{
		F32 lerp_amt = gFocusMgr.getFocusFlashAmt();
		drawBorder(imagep, gFocusMgr.getFocusColor() % alpha, ll_round(lerp(1.f, 3.f, lerp_amt)));
		mCurGlowStrength = lerp(mCurGlowStrength,
Richard Linden's avatar
Richard Linden committed
					mFlashing ? (mFlashingTimer->isCurrentlyHighlighted() || !mFlashingTimer->isFlashingInProgress() || mNeedsHighlight? 1.f : 0.f) : mHoverGlowStrength,
					LLSmoothInterpolation::getInterpolant(0.05f));
		mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLSmoothInterpolation::getInterpolant(0.05f));
	}

	// Draw button image, if available.
	// Otherwise draw basic rectangular button.
		// apply automatic 50% alpha fade to disabled image
		LLColor4 disabled_color = mFadeWhenDisabled ? mDisabledImageColor.get() % 0.5f : mDisabledImageColor.get();
James Cook's avatar
James Cook committed
		{
			imagep->draw(getLocalRect(), (enabled ? mImageColor.get() : disabled_color) % alpha  );
James Cook's avatar
James Cook committed
			if (mCurGlowStrength > 0.01f)
			{
				gGL.setSceneBlendType(glow_type);
				image_glow->drawSolid(0, 0, getRect().getWidth(), getRect().getHeight(), glow_color % (mCurGlowStrength * alpha));
				gGL.setSceneBlendType(LLRender::BT_ALPHA);
			S32 y = getLocalRect().getHeight() - imagep->getHeight();
			imagep->draw(0, y, (enabled ? mImageColor.get() : disabled_color) % alpha);
James Cook's avatar
James Cook committed
			if (mCurGlowStrength > 0.01f)
			{
				gGL.setSceneBlendType(glow_type);
				image_glow->drawSolid(0, y, glow_color % (mCurGlowStrength * alpha));
				gGL.setSceneBlendType(LLRender::BT_ALPHA);
James Cook's avatar
James Cook committed
			}
		}
		LL_DEBUGS() << "No image for button " << getName() << LL_ENDL;
		// draw it in pink so we can find it
		gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4::pink1 % alpha, FALSE);
	// let overlay image and text play well together
	S32 text_left = mLeftHPad;
	S32 text_right = getRect().getWidth() - mRightHPad;
	S32 text_width = getRect().getWidth() - mLeftHPad - mRightHPad;
	if (mImageOverlayEnable && mImageOverlay.notNull())
	{
		// get max width and height (discard level 0)
		S32 overlay_width;
		S32 overlay_height;
		getOverlayImageSize(overlay_width, overlay_height);
		S32 center_x = getLocalRect().getCenterX();
		S32 center_y = getLocalRect().getCenterY();
		//FUGLY HACK FOR "DEPRESSED" BUTTONS
		center_y += (mImageOverlayBottomPad - mImageOverlayTopPad);
		// fade out overlay images on disabled buttons
		LLColor4 overlay_color = mImageOverlayColor.get();
James Cook's avatar
James Cook committed
		{
			overlay_color = mImageOverlayDisabledColor.get();
		else if (getToggleState())
			overlay_color = mImageOverlaySelectedColor.get();
James Cook's avatar
James Cook committed

		switch(mImageOverlayAlignment)
		{
		case LLFontGL::LEFT:
			text_left += overlay_width + mImgOverlayLabelSpace;
			text_width -= overlay_width + mImgOverlayLabelSpace;
				center_y - (overlay_height / 2), 
				overlay_width, 
				overlay_height, 
				overlay_color);
			break;
		case LLFontGL::HCENTER:
			mImageOverlay->draw(
				center_x - (overlay_width / 2), 
				center_y - (overlay_height / 2), 
				overlay_width, 
				overlay_height, 
				overlay_color);
			break;
		case LLFontGL::RIGHT:
			text_right -= overlay_width + mImgOverlayLabelSpace;
			text_width -= overlay_width + mImgOverlayLabelSpace;
				getRect().getWidth() - mRightHPad - overlay_width,
				center_y - (overlay_height / 2), 
				overlay_width, 
				overlay_height, 
				overlay_color);
			break;
		default:
			// draw nothing
			break;
		}
	}
James Cook's avatar
James Cook committed

	// Draw label
	if( !label.empty() )
	{
		LLWStringUtil::trim(label);
James Cook's avatar
James Cook committed

		S32 x;
		switch( mHAlign )
		{
		case LLFontGL::RIGHT:
			x = text_right;
			break;
		case LLFontGL::HCENTER:
			break;
		case LLFontGL::LEFT:
		default:
			x = text_left;
			break;
		S32 y_offset = 2 + (getRect().getHeight() - 20)/2;
	
James Cook's avatar
James Cook committed
		{
James Cook's avatar
James Cook committed
		}
		// *NOTE: mantipov: before mUseEllipses is implemented in EXT-279 U32_MAX has been passed as
		// max_chars.
		// LLFontGL::render expects S32 max_chars variable but process in a separate way -1 value.
		// Due to U32_MAX is equal to S32 -1 value I have rest this value for non-ellipses mode.
		// Not sure if it is really needed. Probably S32_MAX should be always passed as max_chars.
		mLastDrawCharsCount = mGLFont->render(label, 0,
			(F32)x,
			(F32)(getRect().getHeight() / 2 + mBottomVPad),
			mDropShadowedText ? LLFontGL::DROP_SHADOW : LLFontGL::NO_SHADOW,
void LLButton::drawBorder(LLUIImage* imagep, const LLColor4& color, S32 size)
James Cook's avatar
James Cook committed
{
	if (imagep == NULL) return;
James Cook's avatar
James Cook committed
	if (mScaleImage)
	{
		imagep->drawBorder(getLocalRect(), color, size);
James Cook's avatar
James Cook committed
	}
	else
	{
		S32 y = getLocalRect().getHeight() - imagep->getHeight();
		imagep->drawBorder(0, y, color, size);
BOOL LLButton::getToggleState() const
James Cook's avatar
James Cook committed
{
    return getValue().asBoolean();
James Cook's avatar
James Cook committed
}

void LLButton::setToggleState(BOOL b)
{