/** 
 * @file llpanelface.cpp
 * @brief Panel in the tools floater for editing face textures, colors, etc.
 *
 * $LicenseInfo:firstyear=2001&license=viewergpl$
 * 
 * Copyright (c) 2001-2009, Linden Research, Inc.
 * 
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 * 
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at
 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
 * 
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 * 
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */

#include "llviewerprecompiledheaders.h"

// file include
#include "llpanelface.h"
 
// library includes
#include "llcalc.h"
#include "llerror.h"
#include "llfocusmgr.h"
#include "llrect.h"
#include "llstring.h"
#include "llfontgl.h"

// project includes
#include "llagent.h"
#include "llbutton.h"
#include "llcheckboxctrl.h"
#include "llcolorswatch.h"
#include "llcombobox.h"
#include "lldrawpoolbump.h"
#include "llface.h"
#include "llinventorymodel.h" //Perms check for texture params
#include "lllineeditor.h"
#include "llmaterialmgr.h"
#include "llmediaentry.h"
#include "llnotificationsutil.h"
#include "llresmgr.h"
#include "llselectmgr.h"
#include "llspinctrl.h"
#include "lltextbox.h"
#include "lltexturectrl.h"
#include "lltextureentry.h"
#include "lltooldraganddrop.h"
#include "lltrans.h"
#include "llui.h"
#include "llviewercontrol.h"
#include "llviewermedia.h"
#include "llviewerobject.h"
#include "llviewerregion.h"
#include "llviewerstats.h"
#include "llvovolume.h"
#include "lluictrlfactory.h"
#include "llpluginclassmedia.h"
#include "llviewertexturelist.h"// Update sel manager as to which channel we're editing so it can reflect the correct overlay UI

//
// Constant definitions for comboboxes
// Must match the commbobox definitions in panel_tools_texture.xml
//
const S32 MATMEDIA_MATERIAL = 0;	// Material
const S32 MATMEDIA_MEDIA = 1;		// Media
const S32 MATTYPE_DIFFUSE = 0;		// Diffuse material texture
const S32 MATTYPE_NORMAL = 1;		// Normal map
const S32 MATTYPE_SPECULAR = 2;		// Specular map
const S32 ALPHAMODE_NONE = 0;		// No alpha mask applied
const S32 ALPHAMODE_BLEND = 1;		// Alpha blending mode
const S32 ALPHAMODE_MASK = 2;		// Alpha masking mode
const S32 BUMPY_TEXTURE = 18;		// use supplied normal map
const S32 SHINY_TEXTURE = 4;		// use supplied specular map

//
// "Use texture" label for normal/specular type comboboxes
// Filled in at initialization from translated strings
//
std::string USE_TEXTURE;

LLRender::eTexIndex LLPanelFace::getTextureChannelToEdit()
{
// <alchemy>
	LLRender::eTexIndex channel_to_edit = (mComboMatMedia && mComboMatMedia->getCurrentIndex() == MATMEDIA_MATERIAL) ?
													  (mComboMatType ? (LLRender::eTexIndex)mComboMatType->getCurrentIndex() : LLRender::DIFFUSE_MAP) : LLRender::DIFFUSE_MAP;

	channel_to_edit = (channel_to_edit == LLRender::NORMAL_MAP)		? (getCurrentNormalMap().isNull()		? LLRender::DIFFUSE_MAP : channel_to_edit) : channel_to_edit;
	channel_to_edit = (channel_to_edit == LLRender::SPECULAR_MAP)	? (getCurrentSpecularMap().isNull()		? LLRender::DIFFUSE_MAP : channel_to_edit) : channel_to_edit;
	return channel_to_edit;
// </alchemy>
}

// Things the UI provides...
//
// <alchemy>
LLUUID	LLPanelFace::getCurrentNormalMap()			{ return mBumpyTextureCtrl->getImageAssetID();	}
LLUUID	LLPanelFace::getCurrentSpecularMap()		{ return mShinyTextureCtrl->getImageAssetID();	}
U8			LLPanelFace::getCurrentDiffuseAlphaMode()	{ return (U8)mComboAlpha->getCurrentIndex();	}
// </alchemy>

//
// Methods
//

BOOL	LLPanelFace::postBuild()
{
	// Media button caching
	mMediaInfo = getChildView("media_info");
	mMediaAdd = getChildView("add_media");
	mMediaDelete = getChildView("delete_media");

	// Label caching
	mLabelGlossy = getChildView("label glossiness");
	mLabelEnvironment = getChildView("label environment");
	mLabelShinyColor = getChildView("label shinycolor");
	mLabelAlphaMode = getChildView("label alphamode");
	mLabelMaskCutoff = getChildView("label maskcutoff");
	mLabelBumpy = getChildView("label bumpiness");
	mLabelShiny = getChildView("label shininess");
	mLabelColor = getChildView("color label");
	mLabelGlow = getChildView("glow label");
	mLabelTexGen = getChildView("tex gen");

	// UICtrl Caching
	mComboShiny = getChild<LLComboBox>("combobox shininess");
	mComboBumpy = getChild<LLComboBox>("combobox bumpiness");
	mComboAlpha = getChild<LLComboBox>("combobox alphamode");
	mCtrlTexScaleU = getChild<LLSpinCtrl>("TexScaleU");
	mCtrlFlipTexScaleU = getChild<LLUICtrl>("flipTextureScaleU");
	mCtrlTexScaleV = getChild<LLSpinCtrl>("TexScaleV");
	mCtrlFlipTexScaleV = getChild<LLUICtrl>("flipTextureScaleV");
	mCtrlTexRot = getChild<LLSpinCtrl>("TexRot");
	mCtrlRpt = getChild<LLUICtrl>("rptctrl");
	mCtrlPlanar = getChild<LLUICtrl>("checkbox planar align");
	mCtrlTexOffsetU = getChild<LLSpinCtrl>("TexOffsetU");
	mCtrlTexOffsetV = getChild<LLSpinCtrl>("TexOffsetV");
	mBumpyScaleU = getChild<LLUICtrl>("bumpyScaleU");
	mBumpyScaleV = getChild<LLUICtrl>("bumpyScaleV");
	mBumpyRot = getChild<LLUICtrl>("bumpyRot");
	mBumpyOffsetU = getChild<LLUICtrl>("bumpyOffsetU");
	mBumpyOffsetV = getChild<LLUICtrl>("bumpyOffsetV");
	mShinyScaleU = getChild<LLUICtrl>("shinyScaleU");
	mShinyScaleV = getChild<LLUICtrl>("shinyScaleV");
	mShinyRot = getChild<LLUICtrl>("shinyRot");
	mShinyOffsetU = getChild<LLUICtrl>("shinyOffsetU");
	mShinyOffsetV = getChild<LLUICtrl>("shinyOffsetV");
	mGlossyCtrl = getChild<LLUICtrl>("glossiness");
	mEnvironmentCtrl = getChild<LLUICtrl>("environment");
	mCtrlMaskCutoff = getChild<LLUICtrl>("maskcutoff");
	mCtrlAlign = getChild<LLUICtrl>("button align");
	mCtrlMapsSync = getChild<LLUICtrl>("checkbox maps sync");
	mCtrlCopy = getChild<LLUICtrl>("copytextures");
	mCtrlPaste = getChild<LLUICtrl>("pastetextures");

	mComboShiny->setCommitCallback(boost::bind(&LLPanelFace::sendShiny, this, boost::bind(&LLComboBox::getCurrentIndex, mComboShiny)));
	mComboBumpy->setCommitCallback(boost::bind(&LLPanelFace::sendBump, this, boost::bind(&LLComboBox::getCurrentIndex, mComboBumpy)));
	mComboAlpha->setCommitCallback(boost::bind(&LLPanelFace::onCommitAlphaMode, this));
	mCtrlTexScaleU->setCommitCallback(boost::bind(&LLPanelFace::onCommitTextureInfo, this));
	mCtrlFlipTexScaleU->setCommitCallback(boost::bind(&LLPanelFace::onCommitFlip, this, true));
	mCtrlTexScaleV->setCommitCallback(boost::bind(&LLPanelFace::onCommitTextureInfo, this));
	mCtrlFlipTexScaleV->setCommitCallback(boost::bind(&LLPanelFace::onCommitFlip, this, false));
	mCtrlTexRot->setCommitCallback(boost::bind(&LLPanelFace::onCommitTextureInfo, this));
	mCtrlRpt->setCommitCallback(boost::bind(&LLPanelFace::onCommitRepeatsPerMeter, this, _1));
	mCtrlPlanar->setCommitCallback(boost::bind(&LLPanelFace::onCommitPlanarAlign, this));
	mCtrlTexOffsetU->setCommitCallback(boost::bind(&LLPanelFace::onCommitTextureInfo, this));
	mCtrlTexOffsetV->setCommitCallback(boost::bind(&LLPanelFace::onCommitTextureInfo, this));

	mBumpyScaleU->setCommitCallback(boost::bind(&LLPanelFace::onCommitMaterialBumpyScaleX, this, _2));
	mBumpyScaleV->setCommitCallback(boost::bind(&LLPanelFace::onCommitMaterialBumpyScaleY, this, _2));
	mBumpyRot->setCommitCallback(boost::bind(&LLPanelFace::onCommitMaterialBumpyRot, this, _2));
	mBumpyOffsetU->setCommitCallback(boost::bind(LLSelectedTEMaterial::setNormalOffsetX, this, _2));
	mBumpyOffsetV->setCommitCallback(boost::bind(LLSelectedTEMaterial::setNormalOffsetY, this, _2));
	mShinyScaleU->setCommitCallback(boost::bind(&LLPanelFace::onCommitMaterialShinyScaleX, this, _2));
	mShinyScaleV->setCommitCallback(boost::bind(&LLPanelFace::onCommitMaterialShinyScaleY, this, _2));
	mShinyRot->setCommitCallback(boost::bind(&LLPanelFace::onCommitMaterialShinyRot, this, _2));
	mShinyOffsetU->setCommitCallback(boost::bind(LLSelectedTEMaterial::setSpecularOffsetX, this, _2));
	mShinyOffsetV->setCommitCallback(boost::bind(LLSelectedTEMaterial::setSpecularOffsetY, this, _2));
	mGlossyCtrl->setCommitCallback(boost::bind(LLSelectedTEMaterial::setSpecularLightExponent, this, boost::bind(&LLSD::asInteger, _2)));
	mEnvironmentCtrl->setCommitCallback(boost::bind(LLSelectedTEMaterial::setEnvironmentIntensity, this, boost::bind(&LLSD::asInteger, _2)));
	mCtrlMaskCutoff->setCommitCallback(boost::bind(LLSelectedTEMaterial::setAlphaMaskCutoff, this, boost::bind(&LLSD::asInteger, _2)));

	mCtrlAlign->setCommitCallback(boost::bind(&LLPanelFace::onClickAutoFix,this));

	mCtrlMapsSync->setCommitCallback(boost::bind(&LLPanelFace::onClickMapsSync, this));
	mCtrlCopy->setCommitCallback(boost::bind(&LLPanelFace::onClickCopy,this));
	mCtrlPaste->setCommitCallback(boost::bind(&LLPanelFace::onClickPaste,this));

	setMouseOpaque(FALSE);

	mTextureCtrl = getChild<LLTextureCtrl>("texture control");
	if(mTextureCtrl)
	{
		mTextureCtrl->setDefaultImageAssetID(LLUUID( gSavedSettings.getString( "DefaultObjectTexture" )));
		mTextureCtrl->setCommitCallback( boost::bind(&LLPanelFace::onCommitTexture, this, _2) );
		mTextureCtrl->setOnCancelCallback( boost::bind(&LLPanelFace::onCancelTexture, this, _2) );
		mTextureCtrl->setOnSelectCallback( boost::bind(&LLPanelFace::onSelectTexture, this, _2) );
		mTextureCtrl->setDragCallback(boost::bind(&LLPanelFace::onDragTexture, this, _2));
		mTextureCtrl->setOnTextureSelectedCallback(boost::bind(&LLPanelFace::onTextureSelectionChanged, this, _1));
		mTextureCtrl->setOnCloseCallback( boost::bind(&LLPanelFace::onCloseTexturePicker, this, _2) );

		mTextureCtrl->setFollowsTop();
		mTextureCtrl->setFollowsLeft();
		mTextureCtrl->setImmediateFilterPermMask(PERM_NONE);
		mTextureCtrl->setDnDFilterPermMask(PERM_COPY | PERM_TRANSFER);
	}

	mShinyTextureCtrl = getChild<LLTextureCtrl>("shinytexture control");
	if(mShinyTextureCtrl)
	{
		mShinyTextureCtrl->setDefaultImageAssetID(LLUUID( gSavedSettings.getString( "DefaultObjectSpecularTexture" )));
		mShinyTextureCtrl->setCommitCallback( boost::bind(&LLPanelFace::onCommitSpecularTexture, this, _2) );
		mShinyTextureCtrl->setOnCancelCallback( boost::bind(&LLPanelFace::onCancelSpecularTexture, this, _2) );
		mShinyTextureCtrl->setOnSelectCallback( boost::bind(&LLPanelFace::onSelectSpecularTexture, this, _2) );
		mShinyTextureCtrl->setOnCloseCallback( boost::bind(&LLPanelFace::onCloseTexturePicker, this, _2) );

		mShinyTextureCtrl->setDragCallback(boost::bind(&LLPanelFace::onDragTexture, this, _2));
		mShinyTextureCtrl->setOnTextureSelectedCallback(boost::bind(&LLPanelFace::onTextureSelectionChanged, this, _1));
		mShinyTextureCtrl->setFollowsTop();
		mShinyTextureCtrl->setFollowsLeft();
		mShinyTextureCtrl->setImmediateFilterPermMask(PERM_NONE);
		mShinyTextureCtrl->setDnDFilterPermMask(PERM_COPY | PERM_TRANSFER);
	}

	mBumpyTextureCtrl = getChild<LLTextureCtrl>("bumpytexture control");
	if(mBumpyTextureCtrl)
	{
		mBumpyTextureCtrl->setDefaultImageAssetID(LLUUID( gSavedSettings.getString( "DefaultObjectNormalTexture" )));
		mBumpyTextureCtrl->setBlankImageAssetID(LLUUID( gSavedSettings.getString( "DefaultBlankNormalTexture" )));
		mBumpyTextureCtrl->setCommitCallback( boost::bind(&LLPanelFace::onCommitNormalTexture, this, _2) );
		mBumpyTextureCtrl->setOnCancelCallback( boost::bind(&LLPanelFace::onCancelNormalTexture, this, _2) );
		mBumpyTextureCtrl->setOnSelectCallback( boost::bind(&LLPanelFace::onSelectNormalTexture, this, _2) );
		mBumpyTextureCtrl->setOnCloseCallback( boost::bind(&LLPanelFace::onCloseTexturePicker, this, _2) );

		mBumpyTextureCtrl->setDragCallback(boost::bind(&LLPanelFace::onDragTexture, this, _2));
		mBumpyTextureCtrl->setOnTextureSelectedCallback(boost::bind(&LLPanelFace::onTextureSelectionChanged, this, _1));
		mBumpyTextureCtrl->setFollowsTop();
		mBumpyTextureCtrl->setFollowsLeft();
		mBumpyTextureCtrl->setImmediateFilterPermMask(PERM_NONE);
		mBumpyTextureCtrl->setDnDFilterPermMask(PERM_COPY | PERM_TRANSFER);
	}

	mColorSwatch = getChild<LLColorSwatchCtrl>("colorswatch");
	if(mColorSwatch)
	{
		mColorSwatch->setCommitCallback(boost::bind(&LLPanelFace::sendColor, this));
		mColorSwatch->setOnCancelCallback(boost::bind(&LLPanelFace::onCancelColor, this, _2));
		mColorSwatch->setOnSelectCallback(boost::bind(&LLPanelFace::onSelectColor, this, _2));
		mColorSwatch->setFollowsTop();
		mColorSwatch->setFollowsLeft();
		mColorSwatch->setCanApplyImmediately(TRUE);
	}

	mShinyColorSwatch = getChild<LLColorSwatchCtrl>("shinycolorswatch");
	if(mShinyColorSwatch)
	{
		mShinyColorSwatch->setCommitCallback(boost::bind(&LLPanelFace::onCommitShinyColor, this, _2));
		mShinyColorSwatch->setFollowsTop();
		mShinyColorSwatch->setFollowsLeft();
		mShinyColorSwatch->setCanApplyImmediately(TRUE);
	}

	mLabelColorTransp = getChild<LLTextBox>("color trans");
	if(mLabelColorTransp)
	{
		mLabelColorTransp->setFollowsTop();
		mLabelColorTransp->setFollowsLeft();
	}

	mCtrlColorTransp = getChild<LLSpinCtrl>("ColorTrans");
	if(mCtrlColorTransp)
	{
		mCtrlColorTransp->setCommitCallback(boost::bind(&LLPanelFace::sendAlpha, this));
		mCtrlColorTransp->setPrecision(0);
		mCtrlColorTransp->setFollowsTop();
		mCtrlColorTransp->setFollowsLeft();
	}

	mCheckFullbright = getChild<LLCheckBoxCtrl>("checkbox fullbright");
	if (mCheckFullbright)
	{
		mCheckFullbright->setCommitCallback(boost::bind(&LLPanelFace::sendFullbright, this));
	}

	mComboTexGen = getChild<LLComboBox>("combobox texgen");
	if(mComboTexGen)
	{
		mComboTexGen->setCommitCallback(boost::bind(&LLPanelFace::sendTexGen, this));
		mComboTexGen->setFollows(FOLLOWS_LEFT | FOLLOWS_TOP);	
	}

	mComboMatMedia = getChild<LLComboBox>("combobox matmedia");
	if(mComboMatMedia)
	{
		mComboMatMedia->setCommitCallback(boost::bind(&LLPanelFace::onCommitMaterialsMedia,this));
		mComboMatMedia->selectNthItem(MATMEDIA_MATERIAL);
	}

	mComboMatType = getChild<LLComboBox>("combobox mattype");
	if(mComboMatType)
	{
		mComboMatType->setCommitCallback(boost::bind(&LLPanelFace::onCommitMaterialType, this));
		mComboMatType->selectNthItem(MATTYPE_DIFFUSE);
	}

	mCtrlGlow = getChild<LLSpinCtrl>("glow");
	if(mCtrlGlow)
	{
		mCtrlGlow->setCommitCallback(boost::bind(&LLPanelFace::sendGlow, this));
	}


	clearCtrls();

	return TRUE;
}

LLPanelFace::LLPanelFace(const std::string& name)
:	LLPanel(name),
// <alchemy>
	mMediaInfo(NULL),
	mMediaAdd(NULL),
	mMediaDelete(NULL),
	mLabelGlossy(NULL),
	mLabelEnvironment(NULL),
	mLabelShinyColor(NULL),
	mLabelAlphaMode(NULL),
	mLabelMaskCutoff(NULL),
	mLabelBumpy(NULL),
	mLabelShiny(NULL),
	mLabelColor(NULL),
	mLabelGlow(NULL),
	mLabelTexGen(NULL),
	mComboShiny(NULL),
	mComboBumpy(NULL),
	mComboAlpha(NULL),
	mCtrlTexScaleU(NULL),
	mCtrlFlipTexScaleU(NULL),
	mCtrlTexScaleV(NULL),
	mCtrlFlipTexScaleV(NULL),
	mCtrlTexRot(NULL),
	mCtrlRpt(NULL),
	mCtrlPlanar(NULL),
	mCtrlTexOffsetU(NULL),
	mCtrlTexOffsetV(NULL),
	mBumpyScaleU(NULL),
	mBumpyScaleV(NULL),
	mBumpyRot(NULL),
	mBumpyOffsetU(NULL),
	mBumpyOffsetV(NULL),
	mShinyScaleU(NULL),
	mShinyScaleV(NULL),
	mShinyRot(NULL),
	mShinyOffsetU(NULL),
	mShinyOffsetV(NULL),
	mGlossyCtrl(NULL),
	mEnvironmentCtrl(NULL),
	mCtrlMaskCutoff(NULL),
	mCtrlAlign(NULL),
	mCtrlMapsSync(NULL),
	mCtrlCopy(NULL),
	mCtrlPaste(NULL),
	mTextureCtrl(NULL),
	mShinyTextureCtrl(NULL),
	mBumpyTextureCtrl(NULL),
	mColorSwatch(NULL),
	mShinyColorSwatch(NULL),
	mComboTexGen(NULL),
	mComboMatMedia(NULL),
	mComboMatType(NULL),
	mCheckFullbright(NULL),
	mLabelColorTransp(NULL),
	mCtrlColorTransp(NULL), // transparency = 1 - alpha
	mCtrlGlow(NULL),
// </alchemy>
	mIsAlpha(false)
{
	USE_TEXTURE = LLTrans::getString("use_texture");
}


LLPanelFace::~LLPanelFace()
{
	// Children all cleaned up by default view destructor.
}


void LLPanelFace::sendTexture()
{
	if(!mTextureCtrl) return;
	if( !mTextureCtrl->getTentative() )
	{
		// we grab the item id first, because we want to do a
		// permissions check in the selection manager. ARGH!
		LLUUID id = mTextureCtrl->getImageItemID();
		if(id.isNull())
		{
			id = mTextureCtrl->getImageAssetID();
		}
		LLSelectMgr::getInstance()->selectionSetImage(id);
	}
}

void LLPanelFace::sendBump(U32 bumpiness)
{	
	LLTextureCtrl* bumpytexture_ctrl = mBumpyTextureCtrl;
	if (bumpiness < BUMPY_TEXTURE)
	{
		LL_DEBUGS("Materials") << "clearing bumptexture control" << LL_ENDL;
		bumpytexture_ctrl->clear();
		bumpytexture_ctrl->setImageAssetID(LLUUID());
	}

	updateBumpyControls(bumpiness == BUMPY_TEXTURE, true);

	LLUUID current_normal_map = bumpytexture_ctrl->getImageAssetID();

	U8 bump = (U8) bumpiness & TEM_BUMP_MASK;

	// Clear legacy bump to None when using an actual normal map
	//
	if (!current_normal_map.isNull())
		bump = 0;

	// Set the normal map or reset it to null as appropriate
	//
	LLSelectedTEMaterial::setNormalID(this, current_normal_map);

	LLSelectMgr::getInstance()->selectionSetBumpmap( bump );
}

void LLPanelFace::sendTexGen()
{
	if(!mComboTexGen)return;
	U8 tex_gen = (U8) mComboTexGen->getCurrentIndex() << TEM_TEX_GEN_SHIFT;
	LLSelectMgr::getInstance()->selectionSetTexGen( tex_gen );
}

void LLPanelFace::sendShiny(U32 shininess)
{
	LLTextureCtrl* texture_ctrl = mShinyTextureCtrl;

	if (shininess < SHINY_TEXTURE)
	{
		texture_ctrl->clear();
		texture_ctrl->setImageAssetID(LLUUID());
	}

	LLUUID specmap = getCurrentSpecularMap();

	U8 shiny = (U8) shininess & TEM_SHINY_MASK;
	if (!specmap.isNull())
		shiny = 0;

	LLSelectedTEMaterial::setSpecularID(this, specmap);

	LLSelectMgr::getInstance()->selectionSetShiny( shiny );

	updateShinyControls(!specmap.isNull(), true);

}

void LLPanelFace::sendFullbright()
{
	if(!mCheckFullbright)return;
	U8 fullbright = mCheckFullbright->get() ? TEM_FULLBRIGHT_MASK : 0;
	LLSelectMgr::getInstance()->selectionSetFullbright( fullbright );
}

void LLPanelFace::sendColor()
{
	if(!mColorSwatch)return;
	LLColor4 color = mColorSwatch->get();

	LLSelectMgr::getInstance()->selectionSetColorOnly( color );
}

void LLPanelFace::sendAlpha()
{	
	if(!mCtrlColorTransp)return;
	F32 alpha = (100.f - mCtrlColorTransp->get()) / 100.f;

	LLSelectMgr::getInstance()->selectionSetAlphaOnly( alpha );
}


void LLPanelFace::sendGlow()
{
	llassert(mCtrlGlow);
	if (mCtrlGlow)
	{
		F32 glow = mCtrlGlow->get();
		LLSelectMgr::getInstance()->selectionSetGlow( glow );
	}
}

struct LLPanelFaceSetTEFunctor : public LLSelectedTEFunctor
{
	LLPanelFaceSetTEFunctor(LLPanelFace* panel) : mPanel(panel) {}
	virtual bool apply(LLViewerObject* object, S32 te)
	{
		BOOL valid;
		F32 value;
		LLSpinCtrl*	ctrlTexScaleS = mPanel->mCtrlTexScaleU;
		LLSpinCtrl*	ctrlTexScaleT = mPanel->mCtrlTexScaleV;
		LLSpinCtrl*	ctrlTexOffsetS = mPanel->mCtrlTexOffsetU;
		LLSpinCtrl*	ctrlTexOffsetT = mPanel->mCtrlTexOffsetV;
		LLSpinCtrl*	ctrlTexRotation = mPanel->mCtrlTexRot;
		LLComboBox*		comboTexGen = mPanel->mComboTexGen;
		llassert(comboTexGen);
		llassert(object);

		if (ctrlTexScaleS)
		{
			valid = !ctrlTexScaleS->getTentative();
			if (valid)
			{
				value = ctrlTexScaleS->get();
				if (comboTexGen &&
				    comboTexGen->getCurrentIndex() == 1)
				{
					value *= 0.5f;
				}
				object->setTEScaleS( te, value );
			}
		}

		if (ctrlTexScaleT)
		{
			valid = !ctrlTexScaleT->getTentative();
			if (valid)
			{
				value = ctrlTexScaleT->get();
				if (comboTexGen &&
				    comboTexGen->getCurrentIndex() == 1)
				{
					value *= 0.5f;
				}
				object->setTEScaleT( te, value );
			}
		}

		if (ctrlTexOffsetS)
		{
			valid = !ctrlTexOffsetS->getTentative();
			if (valid)
			{
				value = ctrlTexOffsetS->get();
				object->setTEOffsetS( te, value );
			}
		}

		if (ctrlTexOffsetT)
		{
			valid = !ctrlTexOffsetT->getTentative();
			if (valid)
			{
				value = ctrlTexOffsetT->get();
				object->setTEOffsetT( te, value );
			}
		}

		if (ctrlTexRotation)
		{
			valid = !ctrlTexRotation->getTentative();
			if (valid)
			{
				value = ctrlTexRotation->get() * DEG_TO_RAD;
				object->setTERotation( te, value );
			}
		}
		return true;
	}
private:
	LLPanelFace* mPanel;
};

// Functor that aligns a face to mCenterFace
struct LLPanelFaceSetAlignedTEFunctor : public LLSelectedTEFunctor
{
	LLPanelFaceSetAlignedTEFunctor(LLPanelFace* panel, LLFace* center_face) :
		mPanel(panel),
		mCenterFace(center_face) {}

	virtual bool apply(LLViewerObject* object, S32 te)
	{
		LLFace* facep = object->mDrawable->getFace(te);
		if (!facep)
		{
			return true;
		}

		if (facep->getViewerObject()->getVolume()->getNumVolumeFaces() <= te)
		{
			return true;
		}

		bool set_aligned = true;
		if (facep == mCenterFace)
		{
			set_aligned = false;
		}
		if (set_aligned)
		{
			LLVector2 uv_offset, uv_scale;
			F32 uv_rot;
			set_aligned = facep->calcAlignedPlanarTE(mCenterFace, &uv_offset, &uv_scale, &uv_rot);
			if (set_aligned)
			{
				object->setTEOffset(te, uv_offset.mV[VX], uv_offset.mV[VY]);
				object->setTEScale(te, uv_scale.mV[VX], uv_scale.mV[VY]);
				object->setTERotation(te, uv_rot);
			}
		}
		if (!set_aligned)
		{
			LLPanelFaceSetTEFunctor setfunc(mPanel);
			setfunc.apply(object, te);
		}
		return true;
	}
private:
	LLPanelFace* mPanel;
	LLFace* mCenterFace;
};

// Functor that tests if a face is aligned to mCenterFace
struct LLPanelFaceGetIsAlignedTEFunctor : public LLSelectedTEFunctor
{
	LLPanelFaceGetIsAlignedTEFunctor(LLFace* center_face) :
		mCenterFace(center_face) {}

	virtual bool apply(LLViewerObject* object, S32 te)
	{
		LLFace* facep = object->mDrawable->getFace(te);
		if (!facep)
		{
			return false;
		}

		if (facep->getViewerObject()->getVolume()->getNumVolumeFaces() <= te)
		{ //volume face does not exist, can't be aligned
			return false;
		}

		if (facep == mCenterFace)
		{
			return true;
		}
		
		LLVector2 aligned_st_offset, aligned_st_scale;
		F32 aligned_st_rot;
		if ( facep->calcAlignedPlanarTE(mCenterFace, &aligned_st_offset, &aligned_st_scale, &aligned_st_rot) )
		{
			const LLTextureEntry* tep = facep->getTextureEntry();
			LLVector2 st_offset, st_scale;
			tep->getOffset(&st_offset.mV[VX], &st_offset.mV[VY]);
			tep->getScale(&st_scale.mV[VX], &st_scale.mV[VY]);
			F32 st_rot = tep->getRotation();
			// needs a fuzzy comparison, because of fp errors
			if (is_approx_equal_fraction(st_offset.mV[VX], aligned_st_offset.mV[VX], 12) &&
				is_approx_equal_fraction(st_offset.mV[VY], aligned_st_offset.mV[VY], 12) &&
				is_approx_equal_fraction(st_scale.mV[VX], aligned_st_scale.mV[VX], 12) &&
				is_approx_equal_fraction(st_scale.mV[VY], aligned_st_scale.mV[VY], 12) &&
				is_approx_equal_fraction(st_rot, aligned_st_rot, 14))
			{
				return true;
			}
		}
		return false;
	}
private:
	LLFace* mCenterFace;
};

struct LLPanelFaceSendFunctor : public LLSelectedObjectFunctor
{
	virtual bool apply(LLViewerObject* object)
	{
		object->sendTEUpdate();
		return true;
	}
};

void LLPanelFace::sendTextureInfo()
{
	if ((bool)mCtrlPlanar->getValue().asBoolean())
	{
		LLFace* last_face = NULL;
		bool identical_face = false;
		LLSelectedTE::getFace(last_face, identical_face);
		LLPanelFaceSetAlignedTEFunctor setfunc(this, last_face);
		LLSelectMgr::getInstance()->getSelection()->applyToTEs(&setfunc);
	}
	else
	{
		LLPanelFaceSetTEFunctor setfunc(this);
		LLSelectMgr::getInstance()->getSelection()->applyToTEs(&setfunc);
	}

	LLPanelFaceSendFunctor sendfunc;
	LLSelectMgr::getInstance()->getSelection()->applyToObjects(&sendfunc);
}

void LLPanelFace::getState()
{
	updateUI();
}

void LLPanelFace::updateUI()
{ //set state of UI to match state of texture entry(ies)  (calls setEnabled, setValue, etc, but NOT setVisible)
	LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject();

	if( objectp
		&& objectp->getPCode() == LL_PCODE_VOLUME
		&& objectp->permModify())
	{
		BOOL editable = objectp->permModify() && !objectp->isPermanentEnforced();

		// only turn on auto-adjust button if there is a media renderer and the media is loaded
		mCtrlAlign->setEnabled(editable);

		bool enable_material_controls = (!gSavedSettings.getBOOL("FSSynchronizeTextureMaps"));
		S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount();
		BOOL single_volume = (LLSelectMgr::getInstance()->selectionAllPCode( LL_PCODE_VOLUME ))
						 && (selected_count == 1);
		
		mCtrlCopy->setEnabled(single_volume && editable);
		mCtrlPaste->setEnabled(editable);

		LLComboBox* combobox_matmedia = mComboMatMedia;
		if (combobox_matmedia)
		{
			if (combobox_matmedia->getCurrentIndex() < MATMEDIA_MATERIAL)
			{
				combobox_matmedia->selectNthItem(MATMEDIA_MATERIAL);
			}
		}
		mComboMatMedia->setEnabled(editable);

		LLComboBox* combobox_mattype = mComboMatType;
		if (combobox_mattype)
		{
			if (combobox_mattype->getCurrentIndex() < MATTYPE_DIFFUSE)
			{
				combobox_mattype->selectNthItem(MATTYPE_DIFFUSE);
			}
		}
		mComboMatType->setEnabled(editable);

		updateVisibility();

		bool identical			= true;	// true because it is anded below
		bool identical_diffuse	= false;
		bool identical_norm		= false;
		bool identical_spec		= false;

		LLTextureCtrl*	texture_ctrl = mTextureCtrl;
		texture_ctrl->setFallbackImageName( "" );	//Singu Note: Don't show the 'locked' image when the texid is null.
		LLTextureCtrl*	shinytexture_ctrl = mShinyTextureCtrl;
		shinytexture_ctrl->setFallbackImageName( "" );	//Singu Note: Don't show the 'locked' image when the texid is null.
		LLTextureCtrl*	bumpytexture_ctrl = mBumpyTextureCtrl;
		bumpytexture_ctrl->setFallbackImageName( "" );	//Singu Note: Don't show the 'locked' image when the texid is null.

		LLUUID id;
		LLUUID normmap_id;
		LLUUID specmap_id;

		// Color swatch
		{
			mLabelColor->setEnabled(editable);
		}

		LLColor4 color					= LLColor4::white;
		bool		identical_color	=false;

		if(mColorSwatch)
		{
			LLSelectedTE::getColor(color, identical_color);

			mColorSwatch->setOriginal(color);
			mColorSwatch->set(color, TRUE);

			mColorSwatch->setValid(editable);
			mColorSwatch->setEnabled( editable );
			mColorSwatch->setCanApplyImmediately( editable );
		}

		// Color transparency
		mLabelColorTransp->setEnabled(editable);

		F32 transparency = (1.f - color.mV[VALPHA]) * 100.f;
		mCtrlColorTransp->setValue(editable ? transparency : 0);
		mCtrlColorTransp->setEnabled(editable);

		// Specular map
		LLSelectedTEMaterial::getSpecularID(specmap_id, identical_spec);

		U8 shiny = 0;
		bool identical_shiny = false;

		// Shiny
		LLSelectedTE::getShiny(shiny, identical_shiny);
		identical = identical && identical_shiny;

		shiny = specmap_id.isNull() ? shiny : SHINY_TEXTURE;

		if (mComboShiny)
		{
			mComboShiny->selectNthItem((S32)shiny);
		}

		mLabelShiny->setEnabled(editable);
		mComboShiny->setEnabled(editable);

		mLabelGlossy->setEnabled(editable);
		mGlossyCtrl->setEnabled(editable);

		mLabelEnvironment->setEnabled(editable);
		mEnvironmentCtrl->setEnabled(editable);
		mLabelShinyColor->setEnabled(editable);

		mComboShiny->setTentative(!identical_spec);
		mGlossyCtrl->setTentative(!identical_spec);
		mEnvironmentCtrl->setTentative(!identical_spec);
		mShinyColorSwatch->setTentative(!identical_spec);

		if(mShinyColorSwatch)
		{
			mShinyColorSwatch->setValid(editable);
			mShinyColorSwatch->setEnabled( editable );
			mShinyColorSwatch->setCanApplyImmediately( editable );
		}

		U8 bumpy = 0;
		// Bumpy
		{
			bool identical_bumpy = false;
			LLSelectedTE::getBumpmap(bumpy,identical_bumpy);

			LLUUID norm_map_id = getCurrentNormalMap();
			LLCtrlSelectionInterface* combobox_bumpiness = mComboBumpy;

			bumpy = norm_map_id.isNull() ? bumpy : BUMPY_TEXTURE;

			if (combobox_bumpiness)
			{
				combobox_bumpiness->selectNthItem((S32)bumpy);
			}

			mComboBumpy->setEnabled(editable);
			mComboBumpy->setTentative(!identical_bumpy);
			mLabelBumpy->setEnabled(editable);
		}

		// Texture
		{
			LLSelectedTE::getTexId(id,identical_diffuse);

			// Normal map
			LLSelectedTEMaterial::getNormalID(normmap_id, identical_norm);

			mIsAlpha = FALSE;
			LLGLenum image_format = GL_RGB;
			bool identical_image_format = false;
			LLSelectedTE::getImageFormat(image_format, identical_image_format);

			mIsAlpha = FALSE;
			switch (image_format)
			{
			case GL_RGBA:
			case GL_ALPHA:
				{
					mIsAlpha = TRUE;
				}
				break;

			case GL_RGB: break;
			default:
				{
					LL_WARNS("Materials") << "Unexpected tex format in LLPanelFace...resorting to no alpha" << LL_ENDL;
				}
				break;
			}

			if(LLViewerMedia::textureHasMedia(id))
			{
				mCtrlAlign->setEnabled(editable);
			}

			// Diffuse Alpha Mode

			// Init to the default that is appropriate for the alpha content of the asset
			//
			U8 alpha_mode = mIsAlpha ? LLMaterial::DIFFUSE_ALPHA_MODE_BLEND : LLMaterial::DIFFUSE_ALPHA_MODE_NONE;

			bool identical_alpha_mode = false;

			// See if that's been overridden by a material setting for same...
			//
			LLSelectedTEMaterial::getCurrentDiffuseAlphaMode(alpha_mode, identical_alpha_mode, mIsAlpha);

			LLCtrlSelectionInterface* combobox_alphamode = mComboAlpha;
			if (combobox_alphamode)
			{
				//it is invalid to have any alpha mode other than blend if transparency is greater than zero ...
				// Want masking? Want emissive? Tough! You get BLEND!
				alpha_mode = (transparency > 0.f) ? LLMaterial::DIFFUSE_ALPHA_MODE_BLEND : alpha_mode;

				// ... unless there is no alpha channel in the texture, in which case alpha mode MUST be none
				alpha_mode = mIsAlpha ? alpha_mode : LLMaterial::DIFFUSE_ALPHA_MODE_NONE;

				combobox_alphamode->selectNthItem(alpha_mode);
			}

			updateAlphaControls();

			if (texture_ctrl)
			{
				if (identical_diffuse)
				{
					texture_ctrl->setTentative( FALSE );
					texture_ctrl->setEnabled( editable );
					texture_ctrl->setImageAssetID( id );
					mComboAlpha->setEnabled(editable && mIsAlpha && transparency <= 0.f);
					mLabelAlphaMode->setEnabled(editable && mIsAlpha);
					mCtrlMaskCutoff->setEnabled(editable && mIsAlpha);
					mLabelMaskCutoff->setEnabled(editable && mIsAlpha);
				}
				else if (id.isNull())
				{
					// None selected
					texture_ctrl->setTentative( FALSE );
					texture_ctrl->setEnabled( FALSE );
					texture_ctrl->setImageAssetID( LLUUID::null );
					mComboAlpha->setEnabled( FALSE );
					mLabelAlphaMode->setEnabled( FALSE );
					mCtrlMaskCutoff->setEnabled( FALSE);
					mLabelMaskCutoff->setEnabled( FALSE );
				}
				else
				{
					// Tentative: multiple selected with different textures
					texture_ctrl->setTentative( TRUE );
					texture_ctrl->setEnabled( editable );
					texture_ctrl->setImageAssetID( id );
					mComboAlpha->setEnabled(editable && mIsAlpha && transparency <= 0.f);
					mLabelAlphaMode->setEnabled(editable && mIsAlpha);
					mCtrlMaskCutoff->setEnabled(editable && mIsAlpha);
					mLabelMaskCutoff->setEnabled(editable && mIsAlpha);
				}
			}

			if (shinytexture_ctrl)
			{
				if (identical_spec && (shiny == SHINY_TEXTURE))
				{
					shinytexture_ctrl->setTentative( FALSE );
					shinytexture_ctrl->setEnabled( editable );
					shinytexture_ctrl->setImageAssetID( specmap_id );
				}
				else if (specmap_id.isNull())
				{
					shinytexture_ctrl->setTentative( FALSE );
					shinytexture_ctrl->setEnabled( editable );
					shinytexture_ctrl->setImageAssetID( LLUUID::null );
				}
				else
				{
					shinytexture_ctrl->setTentative( TRUE );
					shinytexture_ctrl->setEnabled( editable );
					shinytexture_ctrl->setImageAssetID( specmap_id );
				}
			}

			if (bumpytexture_ctrl)
			{
				if (identical_norm && (bumpy == BUMPY_TEXTURE))
				{
					bumpytexture_ctrl->setTentative( FALSE );
					bumpytexture_ctrl->setEnabled( editable );
					bumpytexture_ctrl->setImageAssetID( normmap_id );
				}
				else if (normmap_id.isNull())
				{
					bumpytexture_ctrl->setTentative( FALSE );
					bumpytexture_ctrl->setEnabled( editable );
					bumpytexture_ctrl->setImageAssetID( LLUUID::null );
				}
				else
				{
					bumpytexture_ctrl->setTentative( TRUE );
					bumpytexture_ctrl->setEnabled( editable );
					bumpytexture_ctrl->setImageAssetID( normmap_id );
				}
			}
		}

		// planar align
		bool align_planar = false;
		bool identical_planar_aligned = false;
		{
			LLUICtrl* cb_planar_align = mCtrlPlanar;
			align_planar = (cb_planar_align && cb_planar_align->getValue().asBoolean());

			bool enabled = (editable && isIdenticalPlanarTexgen());
			mCtrlPlanar->setValue(align_planar && enabled);
			mCtrlPlanar->setEnabled(enabled);

			if (align_planar && enabled)
			{
				LLFace* last_face = NULL;
				bool identical_face = false;
				LLSelectedTE::getFace(last_face, identical_face);

				LLPanelFaceGetIsAlignedTEFunctor get_is_aligend_func(last_face);
				// this will determine if the texture param controls are tentative:
				identical_planar_aligned = LLSelectMgr::getInstance()->getSelection()->applyToTEs(&get_is_aligend_func);
			}
		}
		
		// Needs to be public and before tex scale settings below to properly reflect
		// behavior when in planar vs default texgen modes in the
		// NORSPEC-84 et al
		//
		LLTextureEntry::e_texgen selected_texgen = LLTextureEntry::TEX_GEN_DEFAULT;
		bool identical_texgen = true;
		bool identical_planar_texgen = false;

		{
			LLSelectedTE::getTexGen(selected_texgen, identical_texgen);
			identical_planar_texgen = (identical_texgen && (selected_texgen == LLTextureEntry::TEX_GEN_PLANAR));
		}

		// Texture scale
		{
			bool identical_diff_scale_s = false;
			bool identical_spec_scale_s = false;
			bool identical_norm_scale_s = false;

			identical = align_planar ? identical_planar_aligned : identical;

			F32 diff_scale_s = 1.f;
			F32 spec_scale_s = 1.f;
			F32 norm_scale_s = 1.f;

			LLSelectedTE::getScaleS(diff_scale_s, identical_diff_scale_s);
			LLSelectedTEMaterial::getSpecularRepeatX(spec_scale_s, identical_spec_scale_s);
			LLSelectedTEMaterial::getNormalRepeatX(norm_scale_s, identical_norm_scale_s);

			diff_scale_s = editable ? diff_scale_s : 1.0f;
			diff_scale_s *= identical_planar_texgen ? 2.0f : 1.0f;

			norm_scale_s = editable ? norm_scale_s : 1.0f;
			norm_scale_s *= identical_planar_texgen ? 2.0f : 1.0f;

			spec_scale_s = editable ? spec_scale_s : 1.0f;
			spec_scale_s *= identical_planar_texgen ? 2.0f : 1.0f;

			mCtrlTexScaleU->setValue(diff_scale_s);
			mShinyScaleU->setValue(spec_scale_s);
			mBumpyScaleU->setValue(norm_scale_s);

			mCtrlTexScaleU->setEnabled(editable);
			mShinyScaleU->setEnabled(editable && specmap_id.notNull()
										 && enable_material_controls); // <FS:CR> Materials alignment
			mBumpyScaleU->setEnabled(editable && normmap_id.notNull()
										 && enable_material_controls); // <FS:CR> Materials alignment

			BOOL diff_scale_tentative = !(identical && identical_diff_scale_s);
			BOOL norm_scale_tentative = !(identical && identical_norm_scale_s);
			BOOL spec_scale_tentative = !(identical && identical_spec_scale_s);

			mCtrlTexScaleU->setTentative(  LLSD(diff_scale_tentative));
			mShinyScaleU->setTentative(LLSD(spec_scale_tentative));
			mBumpyScaleU->setTentative(LLSD(norm_scale_tentative));

			// <FS:CR> FIRE-11407 - Materials alignment
			mCtrlMapsSync->setEnabled(editable && (specmap_id.notNull() || normmap_id.notNull()));
			// </FS:CR>
		}

		{
			bool identical_diff_scale_t = false;
			bool identical_spec_scale_t = false;
			bool identical_norm_scale_t = false;

			F32 diff_scale_t = 1.f;
			F32 spec_scale_t = 1.f;
			F32 norm_scale_t = 1.f;

			LLSelectedTE::getScaleT(diff_scale_t, identical_diff_scale_t);
			LLSelectedTEMaterial::getSpecularRepeatY(spec_scale_t, identical_spec_scale_t);
			LLSelectedTEMaterial::getNormalRepeatY(norm_scale_t, identical_norm_scale_t);

			diff_scale_t = editable ? diff_scale_t : 1.0f;
			diff_scale_t *= identical_planar_texgen ? 2.0f : 1.0f;

			norm_scale_t = editable ? norm_scale_t : 1.0f;
			norm_scale_t *= identical_planar_texgen ? 2.0f : 1.0f;

			spec_scale_t = editable ? spec_scale_t : 1.0f;
			spec_scale_t *= identical_planar_texgen ? 2.0f : 1.0f;

			BOOL diff_scale_tentative = !identical_diff_scale_t;
			BOOL norm_scale_tentative = !identical_norm_scale_t;
			BOOL spec_scale_tentative = !identical_spec_scale_t;

			mCtrlTexScaleV->setEnabled(editable);
			mShinyScaleV->setEnabled(editable && specmap_id.notNull()
										 && enable_material_controls); // <FS:CR> Materials alignment
			mBumpyScaleV->setEnabled(editable && normmap_id.notNull()
										 && enable_material_controls); // <FS:CR> Materials alignment

			mCtrlTexScaleV->setValue(diff_scale_t);
			mShinyScaleV->setValue(norm_scale_t);
			mBumpyScaleV->setValue(spec_scale_t);

			mCtrlTexScaleV->setTentative(LLSD(diff_scale_tentative));
			mShinyScaleV->setTentative(LLSD(norm_scale_tentative));
			mBumpyScaleV->setTentative(LLSD(spec_scale_tentative));
		}

		// Texture offset
		{
			bool identical_diff_offset_s = false;
			bool identical_norm_offset_s = false;
			bool identical_spec_offset_s = false;

			F32 diff_offset_s = 0.0f;
			F32 norm_offset_s = 0.0f;
			F32 spec_offset_s = 0.0f;

			LLSelectedTE::getOffsetS(diff_offset_s, identical_diff_offset_s);
			LLSelectedTEMaterial::getNormalOffsetX(norm_offset_s, identical_norm_offset_s);
			LLSelectedTEMaterial::getSpecularOffsetX(spec_offset_s, identical_spec_offset_s);

			BOOL diff_offset_u_tentative = !(align_planar ? identical_planar_aligned : identical_diff_offset_s);
			BOOL norm_offset_u_tentative = !(align_planar ? identical_planar_aligned : identical_norm_offset_s);
			BOOL spec_offset_u_tentative = !(align_planar ? identical_planar_aligned : identical_spec_offset_s);

			mCtrlTexOffsetU->setValue(  editable ? diff_offset_s : 0.0f);
			mBumpyOffsetU->setValue(editable ? norm_offset_s : 0.0f);
			mShinyOffsetU->setValue(editable ? spec_offset_s : 0.0f);

			mCtrlTexOffsetU->setTentative(LLSD(diff_offset_u_tentative));
			mShinyOffsetU->setTentative(LLSD(norm_offset_u_tentative));
			mBumpyOffsetU->setTentative(LLSD(spec_offset_u_tentative));

			mCtrlTexOffsetU->setEnabled(editable);
			mShinyOffsetU->setEnabled(editable && specmap_id.notNull()
										  && enable_material_controls); // <FS:CR> Materials alignment
			mBumpyOffsetU->setEnabled(editable && normmap_id.notNull()
										  && enable_material_controls); // <FS:CR> Materials alignment
		}

		{
			bool identical_diff_offset_t = false;
			bool identical_norm_offset_t = false;
			bool identical_spec_offset_t = false;

			F32 diff_offset_t = 0.0f;
			F32 norm_offset_t = 0.0f;
			F32 spec_offset_t = 0.0f;

			LLSelectedTE::getOffsetT(diff_offset_t, identical_diff_offset_t);
			LLSelectedTEMaterial::getNormalOffsetY(norm_offset_t, identical_norm_offset_t);
			LLSelectedTEMaterial::getSpecularOffsetY(spec_offset_t, identical_spec_offset_t);

			BOOL diff_offset_v_tentative = !(align_planar ? identical_planar_aligned : identical_diff_offset_t);
			BOOL norm_offset_v_tentative = !(align_planar ? identical_planar_aligned : identical_norm_offset_t);
			BOOL spec_offset_v_tentative = !(align_planar ? identical_planar_aligned : identical_spec_offset_t);

			mCtrlTexOffsetV->setValue(  editable ? diff_offset_t : 0.0f);
			mBumpyOffsetV->setValue(editable ? norm_offset_t : 0.0f);
			mShinyOffsetV->setValue(editable ? spec_offset_t : 0.0f);

			mCtrlTexOffsetV->setTentative(LLSD(diff_offset_v_tentative));
			mShinyOffsetV->setTentative(LLSD(norm_offset_v_tentative));
			mBumpyOffsetV->setTentative(LLSD(spec_offset_v_tentative));

			mCtrlTexOffsetV->setEnabled(editable);
			mShinyOffsetV->setEnabled(editable && specmap_id.notNull()
										  && enable_material_controls); // <FS:CR> Materials alignment
			mBumpyOffsetV->setEnabled(editable && normmap_id.notNull()
										  && enable_material_controls); // <FS:CR> Materials alignment
		}

		// Texture rotation
		{
			bool identical_diff_rotation = false;
			bool identical_norm_rotation = false;
			bool identical_spec_rotation = false;

			F32 diff_rotation = 0.f;
			F32 norm_rotation = 0.f;
			F32 spec_rotation = 0.f;

			LLSelectedTE::getRotation(diff_rotation,identical_diff_rotation);
			LLSelectedTEMaterial::getSpecularRotation(spec_rotation,identical_spec_rotation);
			LLSelectedTEMaterial::getNormalRotation(norm_rotation,identical_norm_rotation);

			BOOL diff_rot_tentative = !(align_planar ? identical_planar_aligned : identical_diff_rotation);
			BOOL norm_rot_tentative = !(align_planar ? identical_planar_aligned : identical_norm_rotation);
			BOOL spec_rot_tentative = !(align_planar ? identical_planar_aligned : identical_spec_rotation);

			F32 diff_rot_deg = diff_rotation * RAD_TO_DEG;
			F32 norm_rot_deg = norm_rotation * RAD_TO_DEG;
			F32 spec_rot_deg = spec_rotation * RAD_TO_DEG;

			mCtrlTexRot->setEnabled(editable);
			mShinyRot->setEnabled(editable && specmap_id.notNull()
									  && enable_material_controls); // <FS:CR> Materials alignment
			mBumpyRot->setEnabled(editable && normmap_id.notNull()
									  && enable_material_controls); // <FS:CR> Materials alignment

			mCtrlTexRot->setTentative(diff_rot_tentative);
			mShinyRot->setTentative(LLSD(norm_rot_tentative));
			mBumpyRot->setTentative(LLSD(spec_rot_tentative));

			mCtrlTexRot->setValue(  editable ? diff_rot_deg : 0.0f);
			mShinyRot->setValue(editable ? spec_rot_deg : 0.0f);
			mBumpyRot->setValue(editable ? norm_rot_deg : 0.0f);
		}

		{
			F32 glow = 0.f;
			bool identical_glow = false;
			LLSelectedTE::getGlow(glow,identical_glow);
			mCtrlGlow->setValue(glow);
			mCtrlGlow->setTentative(!identical_glow);
			mCtrlGlow->setEnabled(editable);
			mLabelGlow->setEnabled(editable);
		}

		{
			LLCtrlSelectionInterface* combobox_texgen = mComboTexGen;
			if (combobox_texgen)
			{
				// Maps from enum to combobox entry index
				combobox_texgen->selectNthItem(((S32)selected_texgen) >> 1);
			}

			mComboTexGen->setEnabled(editable);
			mComboTexGen->setTentative(!identical);
			mLabelTexGen->setEnabled(editable);

			/* Singu Note: Dead code
			if (selected_texgen == LLTextureEntry::TEX_GEN_PLANAR)
			{
				// EXP-1507 (change label based on the mapping mode)
				getChild<LLUICtrl>("rpt")->setValue(getString("string repeats per meter"));
			}
			else
			if (selected_texgen == LLTextureEntry::TEX_GEN_DEFAULT)
			{
				getChild<LLUICtrl>("rpt")->setValue(getString("string repeats per face"));
			}
			*/
		}

		{
			U8 fullbright_flag = 0;
			bool identical_fullbright = false;

			LLSelectedTE::getFullbright(fullbright_flag,identical_fullbright);

			mCheckFullbright->setValue((S32)(fullbright_flag != 0));
			mCheckFullbright->setEnabled(editable);
			mCheckFullbright->setTentative(!identical_fullbright);
		}


		// Repeats per meter
		{
			F32 repeats_diff = 1.f;
			F32 repeats_norm = 1.f;
			F32 repeats_spec = 1.f;

			bool identical_diff_repeats = false;
			bool identical_norm_repeats = false;
			bool identical_spec_repeats = false;

			LLSelectedTE::getMaxDiffuseRepeats(repeats_diff, identical_diff_repeats);
			LLSelectedTEMaterial::getMaxNormalRepeats(repeats_norm, identical_norm_repeats);
			LLSelectedTEMaterial::getMaxSpecularRepeats(repeats_spec, identical_spec_repeats);

			if (mComboTexGen)
			{
				S32 index = mComboTexGen ? mComboTexGen->getCurrentIndex() : 0;
				BOOL enabled = editable && (index != 1);
				BOOL identical_repeats = true;
				F32  repeats = 1.0f;

				U32 material_type = (combobox_matmedia->getCurrentIndex() == MATMEDIA_MATERIAL) ? combobox_mattype->getCurrentIndex() : MATTYPE_DIFFUSE;

				LLSelectMgr::getInstance()->setTextureChannel(LLRender::eTexIndex(material_type));

				switch (material_type)
				{
					default:
					case MATTYPE_DIFFUSE:
					{
						enabled = editable && !id.isNull();
						identical_repeats = identical_diff_repeats;
						repeats = repeats_diff;
					}
					break;

					case MATTYPE_SPECULAR:
					{
						enabled = (editable && ((shiny == SHINY_TEXTURE) && !specmap_id.isNull())
								   && enable_material_controls);	// <FS:CR> Materials Alignment
						identical_repeats = identical_spec_repeats;
						repeats = repeats_spec;
					}
					break;

					case MATTYPE_NORMAL:
					{
						enabled = (editable && ((bumpy == BUMPY_TEXTURE) && !normmap_id.isNull())
								   && enable_material_controls); // <FS:CR> Materials Alignment
						identical_repeats = identical_norm_repeats;
						repeats = repeats_norm;
					}
					break;
				}

				BOOL repeats_tentative = !identical_repeats;

				mCtrlRpt->setEnabled(identical_planar_texgen ? FALSE : enabled);
				mCtrlRpt->setValue(editable ? repeats : 1.0f);
				mCtrlRpt->setTentative(LLSD(repeats_tentative));

				// <FS:CR> FIRE-11407 - Flip buttons
				mCtrlFlipTexScaleU->setEnabled(enabled);
				mCtrlFlipTexScaleV->setEnabled(enabled);
				// </FS:CR>
			}
		}

		// Materials
		{
			LLMaterialPtr material;
			LLSelectedTEMaterial::getCurrent(material, identical);

			if (material && editable)
			{
				LL_DEBUGS("Materials: OnMaterialsLoaded:") << material->asLLSD() << LL_ENDL;

				// Alpha
				LLCtrlSelectionInterface* combobox_alphamode =
					mComboAlpha;
				if (combobox_alphamode)
				{
					U32 alpha_mode = material->getDiffuseAlphaMode();

					if (transparency > 0.f)
					{ //it is invalid to have any alpha mode other than blend if transparency is greater than zero ...
						alpha_mode = LLMaterial::DIFFUSE_ALPHA_MODE_BLEND;
					}

					if (!mIsAlpha)
					{ // ... unless there is no alpha channel in the texture, in which case alpha mode MUST ebe none
						alpha_mode = LLMaterial::DIFFUSE_ALPHA_MODE_NONE;
					}

					combobox_alphamode->selectNthItem(alpha_mode);
				}
				mCtrlMaskCutoff->setValue(material->getAlphaMaskCutoff());
				updateAlphaControls();

				identical_planar_texgen = isIdenticalPlanarTexgen();

				// Shiny (specular)
				F32 offset_x, offset_y, repeat_x, repeat_y, rot;
				LLTextureCtrl* texture_ctrl = mShinyTextureCtrl;
				texture_ctrl->setImageAssetID(material->getSpecularID());

				if (!material->getSpecularID().isNull() && (shiny == SHINY_TEXTURE))
				{
					material->getSpecularOffset(offset_x,offset_y);
					material->getSpecularRepeat(repeat_x,repeat_y);

					if (identical_planar_texgen)
					{
						repeat_x *= 2.0f;
						repeat_y *= 2.0f;
					}

					rot = material->getSpecularRotation();
					mShinyScaleU->setValue(repeat_x);
					mShinyScaleV->setValue(repeat_y);
					mShinyRot->setValue(rot*RAD_TO_DEG);
					mShinyOffsetU->setValue(offset_x);
					mShinyOffsetV->setValue(offset_y);
					mGlossyCtrl->setValue(material->getSpecularLightExponent());
					mEnvironmentCtrl->setValue(material->getEnvironmentIntensity());

					updateShinyControls(!material->getSpecularID().isNull(), true);
				}

				// Assert desired colorswatch color to match material AFTER updateShinyControls
				// to avoid getting overwritten with the default on some UI state changes.
				//
				if (!material->getSpecularID().isNull())
				{
					mShinyColorSwatch->setOriginal(material->getSpecularLightColor());
					mShinyColorSwatch->set(material->getSpecularLightColor(),TRUE);
				}

				// Bumpy (normal)
				texture_ctrl = mBumpyTextureCtrl;
				texture_ctrl->setImageAssetID(material->getNormalID());

				if (!material->getNormalID().isNull())
				{
					material->getNormalOffset(offset_x,offset_y);
					material->getNormalRepeat(repeat_x,repeat_y);

					if (identical_planar_texgen)
					{
						repeat_x *= 2.0f;
						repeat_y *= 2.0f;
					}

					rot = material->getNormalRotation();
					mBumpyScaleU->setValue(repeat_x);
					mBumpyScaleV->setValue(repeat_y);
					mBumpyRot->setValue(rot*RAD_TO_DEG);
					mBumpyOffsetU->setValue(offset_x);
					mBumpyOffsetV->setValue(offset_y);

					updateBumpyControls(!material->getNormalID().isNull(), true);
				}
			}
		}

		// Set variable values for numeric expressions
		LLCalc* calcp = LLCalc::getInstance();
		calcp->setVar(LLCalc::TEX_U_SCALE, mCtrlTexScaleU->getValue().asReal());
		calcp->setVar(LLCalc::TEX_V_SCALE, mCtrlTexScaleV->getValue().asReal());
		calcp->setVar(LLCalc::TEX_U_OFFSET, mCtrlTexOffsetU->getValue().asReal());
		calcp->setVar(LLCalc::TEX_V_OFFSET, mCtrlTexOffsetV->getValue().asReal());
		calcp->setVar(LLCalc::TEX_ROTATION, mCtrlTexRot->getValue().asReal());
		calcp->setVar(LLCalc::TEX_TRANSPARENCY, mCtrlColorTransp->getValue().asReal());
		calcp->setVar(LLCalc::TEX_GLOW, mCtrlGlow->getValue().asReal());
	}
	else
	{
		// Disable all UICtrls
		clearCtrls();

		// Disable non-UICtrls
		LLTextureCtrl*	texture_ctrl = mTextureCtrl;
		if(texture_ctrl)
		{
			texture_ctrl->setImageAssetID( LLUUID::null );
			texture_ctrl->setEnabled( FALSE );  // this is a LLUICtrl, but we don't want it to have keyboard focus so we add it as a child, not a ctrl.
// 			texture_ctrl->setValid(FALSE);
		}
		if(mColorSwatch)
		{
			mColorSwatch->setEnabled( FALSE );			
			mColorSwatch->setFallbackImageName("locked_image.j2c" );
			mColorSwatch->setValid(FALSE);
		}
		mLabelColorTransp->setEnabled(FALSE);
		/* Singu Note: This is missing in xml
		getChildView("rpt")->setEnabled(FALSE);
		getChildView("tex offset")->setEnabled(FALSE);
		*/
		mLabelTexGen->setEnabled(FALSE);
		mLabelShiny->setEnabled(FALSE);
		mLabelBumpy->setEnabled(FALSE);
		mCtrlAlign->setEnabled(FALSE);
		//getChildView("has media")->setEnabled(FALSE);
		//getChildView("media info set")->setEnabled(FALSE);

		updateVisibility();

		// Set variable values for numeric expressions
		LLCalc* calcp = LLCalc::getInstance();
		calcp->clearVar(LLCalc::TEX_U_SCALE);
		calcp->clearVar(LLCalc::TEX_V_SCALE);
		calcp->clearVar(LLCalc::TEX_U_OFFSET);
		calcp->clearVar(LLCalc::TEX_V_OFFSET);
		calcp->clearVar(LLCalc::TEX_ROTATION);
		calcp->clearVar(LLCalc::TEX_TRANSPARENCY);
		calcp->clearVar(LLCalc::TEX_GLOW);
	}
}


void LLPanelFace::refresh()
{
	LL_DEBUGS("Materials") << LL_ENDL;
	getState();
}

//
// Static functions
//

// static
F32 LLPanelFace::valueGlow(LLViewerObject* object, S32 face)
{
	return (F32)(object->getTE(face)->getGlow());
}


void LLPanelFace::onCommitShinyColor(const LLSD& data)
{
	LLSelectedTEMaterial::setSpecularLightColor(this, mShinyColorSwatch->get());
}

void LLPanelFace::onCancelColor(const LLSD& data)
{
	LLSelectMgr::getInstance()->selectionRevertColors();
}

void LLPanelFace::onSelectColor(const LLSD& data)
{
	LLSelectMgr::getInstance()->saveSelectedObjectColors();
	sendColor();
}

void LLPanelFace::onCommitMaterialsMedia()
{
	// Force to default states to side-step problems with menu contents
	// and generally reflecting old state when switching tabs or objects
	//
	updateShinyControls(false,true);
	updateBumpyControls(false,true);
	updateUI();
}

// static
void LLPanelFace::updateVisibility()
{
	LLComboBox* combo_matmedia = mComboMatMedia;
	LLComboBox* combo_mattype = mComboMatType;
	LLComboBox* combo_shininess = mComboShiny;
	LLComboBox* combo_bumpiness = mComboBumpy;
	if (!combo_mattype || !combo_matmedia || !combo_shininess || !combo_bumpiness)
	{
		LL_WARNS("Materials") << "Combo box not found...exiting." << LL_ENDL;
		return;
	}
	U32 materials_media = combo_matmedia->getCurrentIndex();
	U32 material_type = combo_mattype->getCurrentIndex();
	bool show_media = (materials_media == MATMEDIA_MEDIA) && combo_matmedia->getEnabled();
	bool show_texture = (show_media || ((material_type == MATTYPE_DIFFUSE) && combo_matmedia->getEnabled()));
	bool show_bumpiness = (!show_media) && (material_type == MATTYPE_NORMAL) && combo_matmedia->getEnabled();
	bool show_shininess = (!show_media) && (material_type == MATTYPE_SPECULAR) && combo_matmedia->getEnabled();
	mComboMatType->setVisible(!show_media);
	// <FS:CR> FIRE-11407 - Be consistant and hide this with the other controls
	//mCtrlRpt->setVisible(true);
	mCtrlRpt->setVisible(combo_matmedia->getEnabled());
	// and other additions...
	mCtrlFlipTexScaleU->setVisible(combo_matmedia->getEnabled());
	mCtrlFlipTexScaleV->setVisible(combo_matmedia->getEnabled());
	// </FS:CR>

	// Media controls
	mMediaInfo->setVisible(show_media);
	mMediaAdd->setVisible(show_media);
	mMediaDelete->setVisible(show_media);
	mCtrlAlign->setVisible(show_media);

	// Diffuse texture controls
	mTextureCtrl->setVisible(show_texture && !show_media);
	mLabelAlphaMode->setVisible(show_texture && !show_media);
	mComboAlpha->setVisible(show_texture && !show_media);
	mLabelMaskCutoff->setVisible(false);
	mCtrlMaskCutoff->setVisible(false);
	if (show_texture && !show_media)
	{
		updateAlphaControls();
	}
	mCtrlTexScaleU->setVisible(show_texture);
	mCtrlTexScaleV->setVisible(show_texture);
	mCtrlTexRot->setVisible(show_texture);
	mCtrlTexOffsetU->setVisible(show_texture);
	mCtrlTexOffsetV->setVisible(show_texture);

	// Specular map controls
	mShinyTextureCtrl->setVisible(show_shininess);
	mComboShiny->setVisible(show_shininess);
	mLabelShiny->setVisible(show_shininess);
	mLabelGlossy->setVisible(false);
	mGlossyCtrl->setVisible(false);
	mLabelEnvironment->setVisible(false);
	mEnvironmentCtrl->setVisible(false);
	mLabelShinyColor->setVisible(false);
	mShinyColorSwatch->setVisible(false);
	if (show_shininess)
	{
		updateShinyControls();
	}
	mShinyScaleU->setVisible(show_shininess);
	mShinyScaleV->setVisible(show_shininess);
	mShinyRot->setVisible(show_shininess);
	mShinyOffsetU->setVisible(show_shininess);
	mShinyOffsetV->setVisible(show_shininess);

	// Normal map controls
	if (show_bumpiness)
	{
		updateBumpyControls();
	}
	mBumpyTextureCtrl->setVisible(show_bumpiness);
	mComboBumpy->setVisible(show_bumpiness);
	mLabelBumpy->setVisible(show_bumpiness);
	mBumpyScaleU->setVisible(show_bumpiness);
	mBumpyScaleV->setVisible(show_bumpiness);
	mBumpyRot->setVisible(show_bumpiness);
	mBumpyOffsetU->setVisible(show_bumpiness);
	mBumpyOffsetV->setVisible(show_bumpiness);
}

void LLPanelFace::onCommitMaterialType()
{
	// Force to default states to side-step problems with menu contents
	// and generally reflecting old state when switching tabs or objects
	//
	updateShinyControls(false,true);
	updateBumpyControls(false,true);
	updateUI();
}

void LLPanelFace::updateShinyControls(bool is_setting_texture, bool mess_with_shiny_combobox)
{
	LLTextureCtrl* texture_ctrl = mShinyTextureCtrl;
	LLUUID shiny_texture_ID = texture_ctrl->getImageAssetID();
	LL_DEBUGS("Materials") << "Shiny texture selected: " << shiny_texture_ID << LL_ENDL;
	LLComboBox* comboShiny = mComboShiny;

	if(mess_with_shiny_combobox)
	{
		if (!comboShiny)
		{
			return;
		}
		if (!shiny_texture_ID.isNull() && is_setting_texture)
		{
			if (!comboShiny->itemExists(USE_TEXTURE))
			{
				comboShiny->add(USE_TEXTURE);
			}
			comboShiny->setSimple(USE_TEXTURE);
		}
		else
		{
			if (comboShiny->itemExists(USE_TEXTURE))
			{
				comboShiny->remove(SHINY_TEXTURE);
				comboShiny->selectFirstItem();
			}
		}
	}

	LLComboBox* combo_matmedia = mComboMatMedia;
	LLComboBox* combo_mattype =mComboMatType;
	U32 materials_media = combo_matmedia->getCurrentIndex();
	U32 material_type = combo_mattype->getCurrentIndex();
	bool show_media = (materials_media == MATMEDIA_MEDIA) && combo_matmedia->getEnabled();
	bool show_shininess = (!show_media) && (material_type == MATTYPE_SPECULAR) && combo_matmedia->getEnabled();
	U32 shiny_value = comboShiny->getCurrentIndex();
	bool show_shinyctrls = (shiny_value == SHINY_TEXTURE) && show_shininess; // Use texture
	mLabelGlossy->setVisible(show_shinyctrls);
	mGlossyCtrl->setVisible(show_shinyctrls);
	mLabelEnvironment->setVisible(show_shinyctrls);
	mEnvironmentCtrl->setVisible(show_shinyctrls);
	mLabelShinyColor->setVisible(show_shinyctrls);
	mShinyColorSwatch->setVisible(show_shinyctrls);
}

void LLPanelFace::updateBumpyControls(bool is_setting_texture, bool mess_with_combobox)
{
	LLTextureCtrl* texture_ctrl = mBumpyTextureCtrl;
	LLUUID bumpy_texture_ID = texture_ctrl->getImageAssetID();
	LL_DEBUGS("Materials") << "texture: " << bumpy_texture_ID << (mess_with_combobox ? "" : " do not") << " update combobox" << LL_ENDL;
	LLComboBox* comboBumpy = mComboBumpy;
	if (!comboBumpy)
	{
		return;
	}

	if (mess_with_combobox)
	{
		LLTextureCtrl* texture_ctrl = mBumpyTextureCtrl;
		LLUUID bumpy_texture_ID = texture_ctrl->getImageAssetID();
		LL_DEBUGS("Materials") << "texture: " << bumpy_texture_ID << (mess_with_combobox ? "" : " do not") << " update combobox" << LL_ENDL;

		if (!bumpy_texture_ID.isNull() && is_setting_texture)
		{
			if (!comboBumpy->itemExists(USE_TEXTURE))
			{
				comboBumpy->add(USE_TEXTURE);
			}
			comboBumpy->setSimple(USE_TEXTURE);
		}
		else
		{
			if (comboBumpy->itemExists(USE_TEXTURE))
			{
				comboBumpy->remove(BUMPY_TEXTURE);
				comboBumpy->selectFirstItem();
			}
		}
	}
}

void LLPanelFace::updateAlphaControls()
{
	LLComboBox* comboAlphaMode = mComboAlpha;
	if (!comboAlphaMode)
	{
		return;
	}
	U32 alpha_value = comboAlphaMode->getCurrentIndex();
	bool show_alphactrls = (alpha_value == ALPHAMODE_MASK); // Alpha masking

	LLComboBox* combobox_matmedia = mComboMatMedia;
	U32 mat_media = MATMEDIA_MATERIAL;
	if (combobox_matmedia)
	{
		mat_media = combobox_matmedia->getCurrentIndex();
	}

	LLComboBox* combobox_mattype = mComboMatType;
	U32 mat_type = MATTYPE_DIFFUSE;
	if (combobox_mattype)
	{
		mat_type = combobox_mattype->getCurrentIndex();
	}

	show_alphactrls = show_alphactrls && (mat_media == MATMEDIA_MATERIAL);
	show_alphactrls = show_alphactrls && (mat_type == MATTYPE_DIFFUSE);

	mLabelMaskCutoff->setVisible(show_alphactrls);
	mCtrlMaskCutoff->setVisible(show_alphactrls);
}

void LLPanelFace::onCommitAlphaMode()
{
	updateAlphaControls();
	LLSelectedTEMaterial::setDiffuseAlphaMode(this, getCurrentDiffuseAlphaMode());
}

// static
BOOL LLPanelFace::onDragTexture(LLUICtrl*, LLInventoryItem* item)
{
	BOOL accept = TRUE;
	for (LLObjectSelection::root_iterator iter = LLSelectMgr::getInstance()->getSelection()->root_begin();
		 iter != LLSelectMgr::getInstance()->getSelection()->root_end(); iter++)
	{
		LLSelectNode* node = *iter;
		LLViewerObject* obj = node->getObject();
		if(!LLToolDragAndDrop::isInventoryDropAcceptable(obj, item))
		{
			accept = FALSE;
			break;
		}
	}
	return accept;
}

void LLPanelFace::onCommitTexture( const LLSD& data )
{
	LLViewerStats::getInstance()->incStat(LLViewerStats::ST_EDIT_TEXTURE_COUNT );
	sendTexture();
}

void LLPanelFace::onCancelTexture(const LLSD& data)
{
	LLSelectMgr::getInstance()->selectionRevertTextures();
}

void LLPanelFace::onSelectTexture(const LLSD& data)
{
	LLSelectMgr::getInstance()->saveSelectedObjectTextures();
	sendTexture();

	LLGLenum image_format;
	bool identical_image_format = false;
	LLSelectedTE::getImageFormat(image_format, identical_image_format);

	LLCtrlSelectionInterface* combobox_alphamode =
		mComboAlpha;

	U32 alpha_mode = LLMaterial::DIFFUSE_ALPHA_MODE_NONE;
	if (combobox_alphamode)
	{
		switch (image_format)
		{
		case GL_RGBA:
		case GL_ALPHA:
			{
				alpha_mode = LLMaterial::DIFFUSE_ALPHA_MODE_BLEND;
			}
			break;

		case GL_RGB: break;
		default:
			{
				llwarns << "Unexpected tex format in LLPanelFace...resorting to no alpha" << llendl;
			}
			break;
		}

		combobox_alphamode->selectNthItem(alpha_mode);
	}
	LLSelectedTEMaterial::setDiffuseAlphaMode(this, getCurrentDiffuseAlphaMode());
}

void LLPanelFace::onCloseTexturePicker(const LLSD& data)
{
	LL_DEBUGS("Materials") << data << LL_ENDL;
	updateUI();
}

void LLPanelFace::onCommitSpecularTexture( const LLSD& data )
{
	LL_DEBUGS("Materials") << data << LL_ENDL;
	sendShiny(SHINY_TEXTURE);
}

void LLPanelFace::onCommitNormalTexture( const LLSD& data )
{
	LL_DEBUGS("Materials") << data << LL_ENDL;
	LLUUID nmap_id = getCurrentNormalMap();
	sendBump(nmap_id.isNull() ? 0 : BUMPY_TEXTURE);
}

void LLPanelFace::onCancelSpecularTexture(const LLSD& data)
{
	U8 shiny = 0;
	bool identical_shiny = false;
	LLSelectedTE::getShiny(shiny, identical_shiny);
	LLUUID spec_map_id = mShinyTextureCtrl->getImageAssetID();
	shiny = spec_map_id.isNull() ? shiny : SHINY_TEXTURE;
	sendShiny(shiny);
}

void LLPanelFace::onCancelNormalTexture(const LLSD& data)
{
	U8 bumpy = 0;
	bool identical_bumpy = false;
	LLSelectedTE::getBumpmap(bumpy, identical_bumpy);
	sendBump(bumpy);
}

void LLPanelFace::onSelectSpecularTexture(const LLSD& data)
{
	LL_DEBUGS("Materials") << data << LL_ENDL;
	sendShiny(SHINY_TEXTURE);
}

void LLPanelFace::onSelectNormalTexture(const LLSD& data)
{
	LL_DEBUGS("Materials") << data << LL_ENDL;
	LLUUID nmap_id = getCurrentNormalMap();
	sendBump(nmap_id.isNull() ? 0 : BUMPY_TEXTURE);
}

void LLPanelFace::onCommitMaterialBumpyScaleX(const LLSD& value)
{
	F32 bumpy_scale_u = value.asReal();
	if (isIdenticalPlanarTexgen())
	{
		bumpy_scale_u *= 0.5f;
	}
	LLSelectedTEMaterial::setNormalRepeatX(this, bumpy_scale_u);
}

void LLPanelFace::onCommitMaterialBumpyScaleY(const LLSD& value)
{
	F32 bumpy_scale_v = value.asReal();
	if (isIdenticalPlanarTexgen())
	{
		bumpy_scale_v *= 0.5f;
	}
	LLSelectedTEMaterial::setNormalRepeatY(this, bumpy_scale_v);
}

void LLPanelFace::onCommitMaterialShinyScaleX(const LLSD& value)
{
	F32 shiny_scale_u = value.asReal();
	if (isIdenticalPlanarTexgen())
	{
		shiny_scale_u *= 0.5f;
	}
	LLSelectedTEMaterial::setSpecularRepeatX(this, shiny_scale_u);
}

void LLPanelFace::onCommitMaterialShinyScaleY(const LLSD& value)
{
	F32 shiny_scale_v = value.asReal();
	if (isIdenticalPlanarTexgen())
	{
		shiny_scale_v *= 0.5f;
	}
	LLSelectedTEMaterial::setSpecularRepeatY(this, shiny_scale_v);
}

void LLPanelFace::onCommitMaterialBumpyRot(const LLSD& value)
{
	LLSelectedTEMaterial::setNormalRotation(this, value.asReal() * DEG_TO_RAD);
}

void LLPanelFace::onCommitMaterialShinyRot(const LLSD& value)
{
	LLSelectedTEMaterial::setSpecularRotation(this, value.asReal() * DEG_TO_RAD);
}

void LLPanelFace::onCommitTextureInfo()
{
	sendTextureInfo();
	// <FS:CR> Materials alignment
	if (gSavedSettings.getBOOL("FSSynchronizeTextureMaps"))
	{
		alignMaterialsProperties();
	}
	// </FS:CR>
}

// Commit the number of repeats per meter
void LLPanelFace::onCommitRepeatsPerMeter(LLUICtrl* repeats_ctrl)
{
	LLComboBox* combo_matmedia = mComboMatMedia;
	LLComboBox* combo_mattype = mComboMatType;

	U32 materials_media = combo_matmedia->getCurrentIndex();


	U32 material_type	= (materials_media == MATMEDIA_MATERIAL) ? combo_mattype->getCurrentIndex() : 0;
	F32 repeats_per_meter	= repeats_ctrl->getValue().asReal();

	F32 obj_scale_s = 1.0f;
	F32 obj_scale_t = 1.0f;

	bool identical_scale_s = false;
	bool identical_scale_t = false;

	LLSelectedTE::getObjectScaleS(obj_scale_s, identical_scale_s);
	LLSelectedTE::getObjectScaleS(obj_scale_t, identical_scale_t);

	switch (material_type)
	{
		case MATTYPE_DIFFUSE:
		{
			LLSelectMgr::getInstance()->selectionTexScaleAutofit( repeats_per_meter );
		}
		break;

		case MATTYPE_NORMAL:
		{
			LLUICtrl* bumpy_scale_u = mBumpyScaleU;
			LLUICtrl* bumpy_scale_v = mBumpyScaleV;

			bumpy_scale_u->setValue(obj_scale_s * repeats_per_meter);
			bumpy_scale_v->setValue(obj_scale_t * repeats_per_meter);

			LLSelectedTEMaterial::setNormalRepeatX(this,obj_scale_s * repeats_per_meter);
			LLSelectedTEMaterial::setNormalRepeatY(this,obj_scale_t * repeats_per_meter);
		}
		break;

		case MATTYPE_SPECULAR:
		{
			LLUICtrl* shiny_scale_u = mShinyScaleU;
			LLUICtrl* shiny_scale_v = mShinyScaleV;

			shiny_scale_u->setValue(obj_scale_s * repeats_per_meter);
			shiny_scale_v->setValue(obj_scale_t * repeats_per_meter);

			LLSelectedTEMaterial::setSpecularRepeatX(this,obj_scale_s * repeats_per_meter);
			LLSelectedTEMaterial::setSpecularRepeatY(this,obj_scale_t * repeats_per_meter);
		}
		break;

		default:
			llassert(false);
		break;
	}
}

struct LLPanelFaceSetMediaFunctor : public LLSelectedTEFunctor
{
	virtual bool apply(LLViewerObject* object, S32 te)
	{
		viewer_media_t pMediaImpl;
				
		const LLTextureEntry* tep = object->getTE(te);
		const LLMediaEntry* mep = tep->hasMedia() ? tep->getMediaData() : NULL;
		if ( mep )
		{
			pMediaImpl = LLViewerMedia::getMediaImplFromTextureID(mep->getMediaID());
		}
		
		if ( pMediaImpl.isNull())
		{
			// If we didn't find face media for this face, check whether this face is showing parcel media.
			pMediaImpl = LLViewerMedia::getMediaImplFromTextureID(tep->getID());
		}
		
		if ( pMediaImpl.notNull())
		{
			LLPluginClassMedia *media = pMediaImpl->getMediaPlugin();
			if(media)
			{
				S32 media_width = media->getWidth();
				S32 media_height = media->getHeight();
				S32 texture_width = media->getTextureWidth();
				S32 texture_height = media->getTextureHeight();
				F32 scale_s = (F32)media_width / (F32)texture_width;
				F32 scale_t = (F32)media_height / (F32)texture_height;

				// set scale and adjust offset
				object->setTEScaleS( te, scale_s );
				object->setTEScaleT( te, scale_t );	// don't need to flip Y anymore since QT does this for us now.
				object->setTEOffsetS( te, -( 1.0f - scale_s ) / 2.0f );
				object->setTEOffsetT( te, -( 1.0f - scale_t ) / 2.0f );
			}
		}
		return true;
	};
};

void LLPanelFace::onClickAutoFix()
{
	LLPanelFaceSetMediaFunctor setfunc;
	LLSelectMgr::getInstance()->getSelection()->applyToTEs(&setfunc);

	LLPanelFaceSendFunctor sendfunc;
	LLSelectMgr::getInstance()->getSelection()->applyToObjects(&sendfunc);
}



// TODO: I don't know who put these in or what these are for???
void LLPanelFace::setMediaURL(const std::string& url)
{
}
void LLPanelFace::setMediaType(const std::string& mime_type)
{
}

void LLPanelFace::onCommitPlanarAlign()
{
	getState();
	sendTextureInfo();
}

void LLPanelFace::onTextureSelectionChanged(LLInventoryItem* itemp)
{
	LL_DEBUGS("Materials") << "item asset " << itemp->getAssetUUID() << LL_ENDL;
	LLComboBox* combo_mattype = mComboMatType;
	if (!combo_mattype)
	{
		return;
	}
	U32 mattype = combo_mattype->getCurrentIndex();
	LLTextureCtrl* texture_ctrl = mTextureCtrl;
	switch (mattype)
	{
		case MATTYPE_SPECULAR:
			texture_ctrl = mShinyTextureCtrl;
			break;
		case MATTYPE_NORMAL:
			texture_ctrl = mBumpyTextureCtrl;
			break;
		// no default needed
	}
	if (texture_ctrl)
	{
		LLUUID obj_owner_id;
		std::string obj_owner_name;
		LLSelectMgr::instance().selectGetOwner(obj_owner_id, obj_owner_name);

		LLSaleInfo sale_info;
		LLSelectMgr::instance().selectGetSaleInfo(sale_info);

		bool can_copy = itemp->getPermissions().allowCopyBy(gAgentID); // do we have perm to copy this texture?
		bool can_transfer = itemp->getPermissions().allowOperationBy(PERM_TRANSFER, gAgentID); // do we have perm to transfer this texture?
		bool is_object_owner = gAgentID == obj_owner_id; // does object for which we are going to apply texture belong to the agent?
		bool not_for_sale = !sale_info.isForSale(); // is object for which we are going to apply texture not for sale?

		if (can_copy && can_transfer)
		{
			texture_ctrl->setCanApply(true, true);
			return;
		}

		// if texture has (no-transfer) attribute it can be applied only for object which we own and is not for sale
		texture_ctrl->setCanApply(false, can_transfer ? true : is_object_owner && not_for_sale);

		if (gSavedSettings.getBOOL("ApplyTextureImmediately"))
		{
			LLNotificationsUtil::add("LivePreviewUnavailable");
		}
	}
}

bool LLPanelFace::isIdenticalPlanarTexgen()
{
	LLTextureEntry::e_texgen selected_texgen = LLTextureEntry::TEX_GEN_DEFAULT;
	bool identical_texgen = false;
	LLSelectedTE::getTexGen(selected_texgen, identical_texgen);
	return (identical_texgen && (selected_texgen == LLTextureEntry::TEX_GEN_PLANAR));
}

void LLPanelFace::LLSelectedTE::getFace(LLFace*& face_to_return, bool& identical_face)
{
	struct LLSelectedTEGetFace : public LLSelectedTEGetFunctor<LLFace *>
	{
		LLFace* get(LLViewerObject* object, S32 te)
		{
			return (object->mDrawable) ? object->mDrawable->getFace(te): NULL;
		}
	} get_te_face_func;
	identical_face = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&get_te_face_func, face_to_return);
}

void LLPanelFace::LLSelectedTE::getImageFormat(LLGLenum& image_format_to_return, bool& identical_face)
{
	LLGLenum image_format;
	struct LLSelectedTEGetImageFormat : public LLSelectedTEGetFunctor<LLGLenum>
	{
		LLGLenum get(LLViewerObject* object, S32 te_index)
		{
			LLViewerTexture* image = object->getTEImage(te_index);
			return image ? image->getPrimaryFormat() : GL_RGB;
		}
	} get_glenum;
	identical_face = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&get_glenum, image_format);
	image_format_to_return = image_format;
}

void LLPanelFace::LLSelectedTE::getTexId(LLUUID& id, bool& identical)
{
	struct LLSelectedTEGetTexId : public LLSelectedTEGetFunctor<LLUUID>
	{
		LLUUID get(LLViewerObject* object, S32 te_index)
		{
			LLUUID id;
			LLViewerTexture* image = object->getTEImage(te_index);
			if (image)
			{
				id = image->getID();
			}

			if (!id.isNull() && LLViewerMedia::textureHasMedia(id))
			{
				LLTextureEntry *te = object->getTE(te_index);
				if (te)
				{
					LLViewerTexture* tex = te->getID().notNull() ? gTextureList.findImage(te->getID()) : NULL;
					if(!tex)
					{
						tex = LLViewerFetchedTexture::sDefaultImagep;
					}
					if (tex)
					{
						id = tex->getID();
					}
				}
			}
			return id;
		}
	} func;
	identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, id );
}

void LLPanelFace::LLSelectedTEMaterial::getCurrent(LLMaterialPtr& material_ptr, bool& identical_material)
{
	struct MaterialFunctor : public LLSelectedTEGetFunctor<LLMaterialPtr>
	{
		LLMaterialPtr get(LLViewerObject* object, S32 te_index)
		{
			return object->getTE(te_index)->getMaterialParams();
		}
	} func;
	identical_material = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, material_ptr);
}

void LLPanelFace::LLSelectedTEMaterial::getMaxSpecularRepeats(F32& repeats, bool& identical)
{
	struct LLSelectedTEGetMaxSpecRepeats : public LLSelectedTEGetFunctor<F32>
	{
		F32 get(LLViewerObject* object, S32 face)
		{
			LLMaterial* mat = object->getTE(face)->getMaterialParams().get();
			U32 s_axis = VX;
			U32 t_axis = VY;
			F32 repeats_s = 1.0f;
			F32 repeats_t = 1.0f;
			if (mat)
			{
				mat->getSpecularRepeat(repeats_s, repeats_t);
				repeats_s /= object->getScale().mV[s_axis];
				repeats_t /= object->getScale().mV[t_axis];
			}
			return llmax(repeats_s, repeats_t);
		}

	} max_spec_repeats_func;
	identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &max_spec_repeats_func, repeats);
}

void LLPanelFace::LLSelectedTEMaterial::getMaxNormalRepeats(F32& repeats, bool& identical)
{
	struct LLSelectedTEGetMaxNormRepeats : public LLSelectedTEGetFunctor<F32>
	{
		F32 get(LLViewerObject* object, S32 face)
		{
			LLMaterial* mat = object->getTE(face)->getMaterialParams().get();
			U32 s_axis = VX;
			U32 t_axis = VY;
			F32 repeats_s = 1.0f;
			F32 repeats_t = 1.0f;
			if (mat)
			{
				mat->getNormalRepeat(repeats_s, repeats_t);
				repeats_s /= object->getScale().mV[s_axis];
				repeats_t /= object->getScale().mV[t_axis];
			}
			return llmax(repeats_s, repeats_t);
		}

	} max_norm_repeats_func;
	identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &max_norm_repeats_func, repeats);
}

void LLPanelFace::LLSelectedTEMaterial::getCurrentDiffuseAlphaMode(U8& diffuse_alpha_mode, bool& identical, bool diffuse_texture_has_alpha)
{
	struct LLSelectedTEGetDiffuseAlphaMode : public LLSelectedTEGetFunctor<U8>
	{
		LLSelectedTEGetDiffuseAlphaMode() : _isAlpha(false) {}
		LLSelectedTEGetDiffuseAlphaMode(bool diffuse_texture_has_alpha) : _isAlpha(diffuse_texture_has_alpha) {}
		virtual ~LLSelectedTEGetDiffuseAlphaMode() {}

		U8 get(LLViewerObject* object, S32 face)
		{
			U8 diffuse_mode = _isAlpha ? LLMaterial::DIFFUSE_ALPHA_MODE_BLEND : LLMaterial::DIFFUSE_ALPHA_MODE_NONE;

			LLTextureEntry* tep = object->getTE(face);
			if (tep)
			{
				LLMaterial* mat = tep->getMaterialParams().get();
				if (mat)
				{
					diffuse_mode = mat->getDiffuseAlphaMode();
				}
			}

			return diffuse_mode;
		}
		bool _isAlpha; // whether or not the diffuse texture selected contains alpha information
	} get_diff_mode(diffuse_texture_has_alpha);
	identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &get_diff_mode, diffuse_alpha_mode);
}

void LLPanelFace::LLSelectedTE::getObjectScaleS(F32& scale_s, bool& identical)
{
	struct LLSelectedTEGetObjectScaleS : public LLSelectedTEGetFunctor<F32>
	{
		F32 get(LLViewerObject* object, S32 face)
		{
			U32 s_axis = VX;
			U32 t_axis = VY;
			LLPrimitive::getTESTAxes(face, &s_axis, &t_axis);
			return object->getScale().mV[s_axis];
		}

	} scale_s_func;
	identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &scale_s_func, scale_s );
}

void LLPanelFace::LLSelectedTE::getObjectScaleT(F32& scale_t, bool& identical)
{
	struct LLSelectedTEGetObjectScaleS : public LLSelectedTEGetFunctor<F32>
	{
		F32 get(LLViewerObject* object, S32 face)
		{
			U32 s_axis = VX;
			U32 t_axis = VY;
			LLPrimitive::getTESTAxes(face, &s_axis, &t_axis);
			return object->getScale().mV[t_axis];
		}

	} scale_t_func;
	identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &scale_t_func, scale_t );
}

void LLPanelFace::LLSelectedTE::getMaxDiffuseRepeats(F32& repeats, bool& identical)
{
	struct LLSelectedTEGetMaxDiffuseRepeats : public LLSelectedTEGetFunctor<F32>
	{
		F32 get(LLViewerObject* object, S32 face)
		{
			U32 s_axis = VX;
			U32 t_axis = VY;
			LLPrimitive::getTESTAxes(face, &s_axis, &t_axis);
			F32 repeats_s = object->getTE(face)->mScaleS / object->getScale().mV[s_axis];
			F32 repeats_t = object->getTE(face)->mScaleT / object->getScale().mV[t_axis];
			return llmax(repeats_s, repeats_t);
		}

	} max_diff_repeats_func;
	identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &max_diff_repeats_func, repeats );
}

static LLSD textures;

void LLPanelFace::onClickCopy()
{
	LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstRootObject();
	if(!objectp)
	{
		objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
		if (!objectp)
		{
			return;
		}
	}
	S32 te_count = objectp->getNumFaces();
	textures.clear();
	for (S32 i = 0; i < te_count; i++)
	{
		LLSD tex_params = objectp->getTE(i)->asLLSD();
		LLInventoryItem* itemp = gInventory.getItem(objectp->getTE(i)->getID());
		LLUUID tex = tex_params["imageid"];
		tex_params["imageid"] = LLUUID::null;
		//gSavedPerAccountSettings.setLLSD("Image.Settings", tex_params);
		if (itemp)
		{
			LLPermissions perms = itemp->getPermissions();
			//full perms
			if (perms.getMaskOwner() & PERM_ITEM_UNRESTRICTED)
			{
				tex_params["imageid"] = tex;
			}
		}
		llinfos << "Copying params on face " << i << "." << llendl;
		textures.append(tex_params);
	}
}

void LLPanelFace::onClickPaste()
{
	LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstRootObject();
	if(!objectp)
	{
		objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
		if (!objectp)
		{
			return;
		}
	}
	LLMessageSystem* msg = gMessageSystem;
	msg->newMessageFast(_PREHASH_ObjectImage);
	msg->nextBlockFast(_PREHASH_AgentData);
	msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
	msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
	
	msg->nextBlockFast(_PREHASH_ObjectData);
	msg->addU32Fast(_PREHASH_ObjectLocalID, objectp->getLocalID());
	msg->addStringFast(_PREHASH_MediaURL, NULL);
	
	LLPrimitive obj;
	obj.setNumTEs(U8(textures.size()));
	
	for (int i = 0; i < textures.size() && i < objectp->getNumTEs(); i++)
	{
		llinfos << "Pasting params on face " << i << "." << llendl;
		LLSD cur_tex = objectp->getTE(i)->asLLSD();
		if (textures[i]["imageid"].asUUID() == LLUUID::null)
			textures[i]["imageid"] = cur_tex["imageid"];
		LLTextureEntry tex;
		tex.fromLLSD(textures[i]);
		obj.setTE(U8(i), tex);
	}
	
	obj.packTEMessage(gMessageSystem);
	
	msg->sendReliable(gAgent.getRegion()->getHost());
}

// <FS:CR> Materials alignment
void LLPanelFace::onClickMapsSync()
{
	getState();
	if (gSavedSettings.getBOOL("FSSynchronizeTextureMaps"))
	{
		alignMaterialsProperties();
	}
}

void LLPanelFace::alignMaterialsProperties()
{
	F32 tex_scale_u =	mCtrlTexScaleU->getValue().asReal();
	F32 tex_scale_v =	mCtrlTexScaleV->getValue().asReal();
	F32 tex_offset_u =	mCtrlTexOffsetU->getValue().asReal();
	F32 tex_offset_v =	mCtrlTexOffsetV->getValue().asReal();
	F32 tex_rot =		mCtrlTexRot->getValue().asReal();

	mShinyScaleU->setValue(tex_scale_u);
	mShinyScaleV->setValue(tex_scale_v);
	mShinyOffsetU->setValue(tex_offset_u);
	mShinyOffsetV->setValue(tex_offset_v);
	mShinyRot->setValue(tex_rot);

	LLSelectedTEMaterial::setSpecularRepeatX(this, tex_scale_u);
	LLSelectedTEMaterial::setSpecularRepeatY(this, tex_scale_v);
	LLSelectedTEMaterial::setSpecularOffsetX(this, tex_offset_u);
	LLSelectedTEMaterial::setSpecularOffsetY(this, tex_offset_v);
	LLSelectedTEMaterial::setSpecularRotation(this, tex_rot * DEG_TO_RAD);

	mBumpyScaleU->setValue(tex_scale_u);
	mBumpyScaleV->setValue(tex_scale_v);
	mBumpyOffsetU->setValue(tex_offset_u);
	mBumpyOffsetV->setValue(tex_offset_v);
	mBumpyRot->setValue(tex_rot);

	LLSelectedTEMaterial::setNormalRepeatX(this, tex_scale_u);
	LLSelectedTEMaterial::setNormalRepeatY(this, tex_scale_v);
	LLSelectedTEMaterial::setNormalOffsetX(this, tex_offset_u);
	LLSelectedTEMaterial::setNormalOffsetY(this, tex_offset_v);
	LLSelectedTEMaterial::setNormalRotation(this, tex_rot * DEG_TO_RAD);
}

// <FS:CR> FIRE-11407 - Flip buttons
void LLPanelFace::onCommitFlip(bool flip_x)
{
	S32 mattype(mComboMatType->getCurrentIndex());
	LLUICtrl* spinner = NULL;
	switch (mattype)
	{
		case MATTYPE_DIFFUSE:
			spinner = flip_x ? mCtrlTexScaleU : mCtrlTexScaleV;
			break;
		case MATTYPE_NORMAL:
			spinner = flip_x ? mBumpyScaleU : mBumpyScaleV;
			break;
		case MATTYPE_SPECULAR:
			spinner = flip_x ? mShinyScaleU : mShinyScaleV;
			break;
		default:
			//llassert(mattype);
			return;
	}

	if (spinner)
	{
		F32 value = -(spinner->getValue().asReal());
		spinner->setValue(value);

		switch (mattype)
		{
			case MATTYPE_DIFFUSE:
				sendTextureInfo();
				if (gSavedSettings.getBOOL("FSSyncronizeTextureMaps"))
				{
					alignMaterialsProperties();
				}
				break;
			case MATTYPE_NORMAL:
				if (flip_x)
					LLSelectedTEMaterial::setNormalRepeatX(this, value);
				else
					LLSelectedTEMaterial::setNormalRepeatY(this, value);
				break;
			case MATTYPE_SPECULAR:
				if (flip_x)
					LLSelectedTEMaterial::setSpecularRepeatX(this, value);
				else
					LLSelectedTEMaterial::setSpecularRepeatY(this, value);
				break;
			default:
				//llassert(mattype);
				return;
		}
	}
}
// </FS:CR>
