/** 
 * @file lltexturectrl.cpp
 * @author Richard Nelson, James Cook
 * @brief LLTextureCtrl class implementation including related functions
 *
 * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
 * $License$
 */

#include "llviewerprecompiledheaders.h"

#include "lltexturectrl.h"

#include "llagent.h"
#include "llviewerimagelist.h"
#include "llcheckboxctrl.h"
#include "llcombobox.h"
#include "llbutton.h"
#include "lldraghandle.h"
#include "llfocusmgr.h"
#include "llviewerimage.h"
#include "llfolderview.h"
#include "llinventory.h"
#include "llinventorymodel.h"
#include "llinventoryview.h"
#include "lllineeditor.h"
#include "llui.h"
#include "llviewerinventory.h"
#include "llpermissions.h"
#include "llsaleinfo.h"
#include "llassetstorage.h"
#include "lltextbox.h"
#include "llresizehandle.h"
#include "llscrollcontainer.h"
#include "lltoolmgr.h"
#include "lltoolpipette.h"

#include "lltool.h"
#include "llviewerwindow.h"
#include "llviewerobject.h"
#include "llviewercontrol.h"
#include "llglheaders.h"
#include "llvieweruictrlfactory.h"


static const S32 CLOSE_BTN_WIDTH = 100;
const S32 PIPETTE_BTN_WIDTH = 32;
static const S32 HPAD = 4;
static const S32 VPAD = 4;
static const S32 LINE = 16;
static const S32 SMALL_BTN_WIDTH = 64;
static const S32 TEX_PICKER_MIN_WIDTH = 
	(HPAD +
	CLOSE_BTN_WIDTH +
	HPAD +
	CLOSE_BTN_WIDTH +
	HPAD + 
	SMALL_BTN_WIDTH +
	HPAD +
	SMALL_BTN_WIDTH +
	HPAD + 
	30 +
	RESIZE_HANDLE_WIDTH * 2);
static const S32 CLEAR_BTN_WIDTH = 50;
static const S32 TEX_PICKER_MIN_HEIGHT = 290;
static const S32 FOOTER_HEIGHT = 100;
static const S32 BORDER_PAD = HPAD;
static const S32 TEXTURE_INVENTORY_PADDING = 30;
static const F32 CONTEXT_CONE_IN_ALPHA = 0.0f;
static const F32 CONTEXT_CONE_OUT_ALPHA = 0.35f;


//static const char CURRENT_IMAGE_NAME[] = "Current Texture";
//static const char WHITE_IMAGE_NAME[] = "Blank Texture";
//static const char NO_IMAGE_NAME[] = "None";

//////////////////////////////////////////////////////////////////////////////////////////
// LLFloaterTexturePicker

class LLFloaterTexturePicker : public LLFloater
{
public:
	LLFloaterTexturePicker(
		LLTextureCtrl* owner,
		const LLRect& rect,
		const std::string& label,
		PermissionMask immediate_filter_perm_mask,
		PermissionMask non_immediate_filter_perm_mask,
		BOOL can_apply_immediately);
	virtual ~LLFloaterTexturePicker();

	// LLView overrides
	virtual BOOL	handleDragAndDrop(S32 x, S32 y, MASK mask,
						BOOL drop, EDragAndDropType cargo_type, void *cargo_data, 
						EAcceptance *accept,
						LLString& tooltip_msg);
	virtual void	draw();

	// LLFloater overrides
	virtual void	onClose(bool app_quitting);

	// New functions
	void setImageID( const LLUUID& image_asset_id);
	void updateImageStats();
	const LLUUID& getAssetID() { return mImageAssetID; }
	const LLUUID& findItemID(const LLUUID& asset_id, BOOL copyable_only);
	void			setCanApplyImmediately(BOOL b);

	void			setDirty( BOOL b ) { mIsDirty = b; }
	BOOL			isDirty() { return mIsDirty; }
	void			setActive( BOOL active );

	LLTextureCtrl*	getOwner() const { return mOwner; }
	void			setOwner(LLTextureCtrl* owner) { mOwner = owner; }
	
	void			stopUsingPipette();
	PermissionMask 	getFilterPermMask();
	void updateFilterPermMask();
	void commitIfImmediateSet();

	static void		onBtnSetToDefault( void* userdata );
	static void		onBtnSelect( void* userdata );
	static void		onBtnCancel( void* userdata );
	static void		onBtnPipette( void* userdata );
	//static void		onBtnRevert( void* userdata );
	static void		onBtnWhite( void* userdata );
	static void		onBtnNone( void* userdata );
	static void		onBtnClear( void* userdata );
	static void		onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action, void* data);
	static void		onShowFolders(LLUICtrl* ctrl, void* userdata);
	static void		onApplyImmediateCheck(LLUICtrl* ctrl, void* userdata);
	static void		onSearchEdit(const LLString& search_string, void* user_data );
	static void		onTextureSelect( const LLTextureEntry& te, void *data );

protected:
	LLPointer<LLViewerImage> mTexturep;
	LLTextureCtrl*		mOwner;

	LLUUID				mImageAssetID; // Currently selected texture

	LLUUID				mWhiteImageAssetID;
	LLUUID				mSpecialCurrentImageAssetID;  // Used when the asset id has no corresponding texture in the user's inventory.
	LLUUID				mOriginalImageAssetID;

	LLTextBox*			mTentativeLabel;
	LLTextBox*			mResolutionLabel;

	LLString			mPendingName;
	BOOL				mIsDirty;
	BOOL				mActive;

	LLSearchEditor*		mSearchEdit;
	LLInventoryPanel*	mInventoryPanel;
	PermissionMask		mImmediateFilterPermMask;
	PermissionMask		mNonImmediateFilterPermMask;
	BOOL				mCanApplyImmediately;
	BOOL				mNoCopyTextureSelected;
	F32					mContextConeOpacity;
};

LLFloaterTexturePicker::LLFloaterTexturePicker(	
	LLTextureCtrl* owner,
	const LLRect& rect,
	const std::string& label,
	PermissionMask immediate_filter_perm_mask,
	PermissionMask non_immediate_filter_perm_mask,
	BOOL can_apply_immediately)
	:
	LLFloater( "texture picker",
		rect,
		LLString( "Pick: " ) + label,
		TRUE,
		TEX_PICKER_MIN_WIDTH, TEX_PICKER_MIN_HEIGHT ),
	mOwner( owner ),
	mImageAssetID( owner->getImageAssetID() ),
	mWhiteImageAssetID( gSavedSettings.getString( "UIImgWhiteUUID" ) ),
	mOriginalImageAssetID(owner->getImageAssetID()),
	mTentativeLabel(NULL),
	mResolutionLabel(NULL),
	mIsDirty( FALSE ),
	mActive( TRUE ),
	mSearchEdit(NULL),
	mImmediateFilterPermMask(immediate_filter_perm_mask),
	mNonImmediateFilterPermMask(non_immediate_filter_perm_mask),
	mContextConeOpacity(0.f)
{
	gUICtrlFactory->buildFloater(this,"floater_texture_ctrl.xml");

	mTentativeLabel = LLUICtrlFactory::getTextBoxByName(this,"Multiple");

	mResolutionLabel = LLUICtrlFactory::getTextBoxByName(this,"unknown");


	childSetAction("Default",LLFloaterTexturePicker::onBtnSetToDefault,this);
	childSetAction("None", LLFloaterTexturePicker::onBtnNone,this);
	childSetAction("Blank", LLFloaterTexturePicker::onBtnWhite,this);

		
	childSetCommitCallback("show_folders_check", onShowFolders, this);
	childSetVisible("show_folders_check", FALSE);
	
	mSearchEdit = (LLSearchEditor*)getCtrlByNameAndType("inventory search editor", WIDGET_TYPE_SEARCH_EDITOR);
	mSearchEdit->setSearchCallback(onSearchEdit, this);
		
	mInventoryPanel = (LLInventoryPanel*)this->getCtrlByNameAndType("inventory panel", WIDGET_TYPE_INVENTORY_PANEL);

	if(mInventoryPanel)
	{
		U32 filter_types = 0x0;
		filter_types |= 0x1 << LLInventoryType::IT_TEXTURE;
		filter_types |= 0x1 << LLInventoryType::IT_SNAPSHOT;

		mInventoryPanel->setAutoSelectOverride(true);
		mInventoryPanel->setFilterTypes(filter_types);
		mInventoryPanel->setFilterPermMask(getFilterPermMask());
		mInventoryPanel->setSelectCallback(onSelectionChange, this);
		mInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS);
		mInventoryPanel->setAllowMultiSelect(FALSE);
		// Commented out to stop opening all folders with textures
		// mInventoryPanel->openDefaultFolderForType(LLAssetType::AT_TEXTURE);
		
		// don't put keyboard focus on selected item, because the selection callback
		// will assume that this was user input
		mInventoryPanel->setSelection(findItemID(mImageAssetID, FALSE), TAKE_FOCUS_NO);
	}

	mCanApplyImmediately = can_apply_immediately;
	mNoCopyTextureSelected = FALSE;
		
	childSetValue("apply_immediate_check", gSavedSettings.getBOOL("ApplyTextureImmediately"));
	childSetCommitCallback("apply_immediate_check", onApplyImmediateCheck, this);
	
	if (!can_apply_immediately)
	{
		childSetEnabled("show_folders_check", FALSE);
	}

	childSetAction("Pipette", LLFloaterTexturePicker::onBtnPipette,this);
	childSetAction("Cancel", LLFloaterTexturePicker::onBtnCancel,this);
	childSetAction("Select", LLFloaterTexturePicker::onBtnSelect,this);

	// update permission filter once UI is fully initialized
	updateFilterPermMask();

	setCanMinimize(FALSE);
}

LLFloaterTexturePicker::~LLFloaterTexturePicker()
{
}

void LLFloaterTexturePicker::setImageID(const LLUUID& image_id)
{
	if( mImageAssetID != image_id && mActive)
	{
		mNoCopyTextureSelected = FALSE;
		mIsDirty = TRUE;
		mImageAssetID = image_id; 
		LLUUID item_id = findItemID(mImageAssetID, FALSE);
		if (item_id.isNull())
		{
			mInventoryPanel->clearSelection();
		}
		else
		{
			LLInventoryItem* itemp = gInventory.getItem(image_id);
			if (itemp && !itemp->getPermissions().allowCopyBy(gAgent.getID()))
			{
				// no copy texture
				childSetValue("apply_immediate_check", FALSE);
				mNoCopyTextureSelected = TRUE;
			}
			mInventoryPanel->setSelection(item_id, TAKE_FOCUS_NO);
		}
	}
}

void LLFloaterTexturePicker::setActive( BOOL active )					
{
	if (!active && childGetValue("Pipette").asBoolean())
	{
		stopUsingPipette();
	}
	mActive = active; 
}

void LLFloaterTexturePicker::setCanApplyImmediately(BOOL b)
{
	mCanApplyImmediately = b;
	if (!mCanApplyImmediately)
	{
		childSetValue("apply_immediate_check", FALSE);
	}
	updateFilterPermMask();
}

void LLFloaterTexturePicker::stopUsingPipette()
{
	if (gToolMgr && gToolMgr->getCurrentTool(gKeyboard->currentMask(TRUE)) == gToolPipette)
	{
		gToolMgr->clearTransientTool();
	}
}

void LLFloaterTexturePicker::updateImageStats()
{
	if (mTexturep.notNull())
	{
		//RN: have we received header data for this image?
		if (mTexturep->getWidth(0) > 0 && mTexturep->getHeight(0) > 0)
		{
			std::ostringstream formatted_dims;
			formatted_dims << llformat("Dimensions: %d x %d", mTexturep->getWidth(0),mTexturep->getHeight(0));
			mResolutionLabel->setText(formatted_dims.str());
		}
		else
		{
			mResolutionLabel->setText("Dimensions: unknown");
		}
		if (gAgent.isGodlike())
		{
			LLString tstring = "Pick: " + mTexturep->getID().getString();
			setTitle(tstring);
		}
	}
}

// virtual
BOOL LLFloaterTexturePicker::handleDragAndDrop( 
		S32 x, S32 y, MASK mask,
		BOOL drop,
		EDragAndDropType cargo_type, void *cargo_data, 
		EAcceptance *accept,
		LLString& tooltip_msg)
{
	BOOL handled = FALSE;

	if (cargo_type == DAD_TEXTURE)
	{
		LLInventoryItem *item = (LLInventoryItem *)cargo_data;

		BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID());
		BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID());
		BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER,
															gAgent.getID());

		PermissionMask item_perm_mask = 0;
		if (copy) item_perm_mask |= PERM_COPY;
		if (mod)  item_perm_mask |= PERM_MODIFY;
		if (xfer) item_perm_mask |= PERM_TRANSFER;
		
		PermissionMask filter_perm_mask = getFilterPermMask();
		if ( (item_perm_mask & filter_perm_mask) == filter_perm_mask )
		{
			if (drop)
			{
				setImageID( item->getAssetUUID() );
				commitIfImmediateSet();
			}

			*accept = ACCEPT_YES_SINGLE;
		}
		else
		{
			*accept = ACCEPT_NO;
		}
	}
	else
	{
		*accept = ACCEPT_NO;
	}

	handled = TRUE;
	lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFloaterTexturePicker " << getName() << llendl;

	return handled;
}

// virtual
void LLFloaterTexturePicker::onClose(bool app_quitting)
{
	if (mOwner)
	{
		mOwner->onFloaterClose();
	}
	stopUsingPipette();
	destroy();
}

// virtual
void LLFloaterTexturePicker::draw()
{
	if (mOwner)
	{
		// draw cone of context pointing back to texture swatch	
		LLRect owner_rect;
		mOwner->localRectToOtherView(mOwner->getLocalRect(), &owner_rect, this);
		LLRect local_rect = getLocalRect();
		if (gFocusMgr.childHasKeyboardFocus(this) && mOwner->isInVisibleChain() && mContextConeOpacity > 0.001f)
		{
			LLGLSNoTexture no_texture;
			LLGLEnable(GL_CULL_FACE);
			glBegin(GL_QUADS);
			{
				glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
				glVertex2i(owner_rect.mLeft, owner_rect.mTop);
				glVertex2i(owner_rect.mRight, owner_rect.mTop);
				glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
				glVertex2i(local_rect.mRight, local_rect.mTop);
				glVertex2i(local_rect.mLeft, local_rect.mTop);

				glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
				glVertex2i(local_rect.mLeft, local_rect.mTop);
				glVertex2i(local_rect.mLeft, local_rect.mBottom);
				glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
				glVertex2i(owner_rect.mLeft, owner_rect.mBottom);
				glVertex2i(owner_rect.mLeft, owner_rect.mTop);

				glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
				glVertex2i(local_rect.mRight, local_rect.mBottom);
				glVertex2i(local_rect.mRight, local_rect.mTop);
				glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
				glVertex2i(owner_rect.mRight, owner_rect.mTop);
				glVertex2i(owner_rect.mRight, owner_rect.mBottom);


				glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
				glVertex2i(local_rect.mLeft, local_rect.mBottom);
				glVertex2i(local_rect.mRight, local_rect.mBottom);
				glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
				glVertex2i(owner_rect.mRight, owner_rect.mBottom);
				glVertex2i(owner_rect.mLeft, owner_rect.mBottom);
			}
			glEnd();
		}
	}

	if (gFocusMgr.childHasMouseCapture(mDragHandle))
	{
		mContextConeOpacity = lerp(mContextConeOpacity, 1.f, LLCriticalDamp::getInterpolant(0.1f));
	}
	else
	{
		mContextConeOpacity = lerp(mContextConeOpacity, 0.f, LLCriticalDamp::getInterpolant(0.2f));
	}

	updateImageStats();

	// if we're inactive, gray out "apply immediate" checkbox
	childSetEnabled("show_folders_check", mActive && mCanApplyImmediately && !mNoCopyTextureSelected);
	childSetEnabled("Select", mActive);
	childSetEnabled("Pipette", gToolMgr != NULL && mActive);
	childSetValue("Pipette", gToolMgr && gToolMgr->getCurrentTool(gKeyboard->currentMask(TRUE)) == gToolPipette);

	//RN: reset search bar to reflect actual search query (all caps, for example)
	mSearchEdit->setText(mInventoryPanel->getFilterSubString());

	//BOOL allow_copy = FALSE;
	if( getVisible() && mOwner) 
	{
		mTexturep = NULL;
		if(mImageAssetID.notNull())
		{
			mTexturep = gImageList.getImage(mImageAssetID, MIPMAP_YES, IMMEDIATE_NO);
			mTexturep->setBoostLevel(LLViewerImage::BOOST_PREVIEW);
		}

		if (mTentativeLabel)
		{
			mTentativeLabel->setVisible( FALSE  );
		}

		childSetEnabled("Default",  mImageAssetID != mOwner->getDefaultImageAssetID());
		childSetEnabled("Blank",   mImageAssetID != mWhiteImageAssetID );
		childSetEnabled("None", mOwner->getAllowNoTexture() && !mImageAssetID.isNull() );

		LLFloater::draw();

		if( isMinimized() )
		{
			return;
		}

		// Border
		LLRect border( BORDER_PAD, 
				mRect.getHeight() - LLFLOATER_HEADER_SIZE - BORDER_PAD, 
				((TEX_PICKER_MIN_WIDTH / 2) - TEXTURE_INVENTORY_PADDING - HPAD) - BORDER_PAD,
				BORDER_PAD + FOOTER_HEIGHT + (mRect.getHeight() - TEX_PICKER_MIN_HEIGHT));
		gl_rect_2d( border, LLColor4::black, FALSE );


		// Interior
		LLRect interior = border;
		interior.stretch( -1 ); 

		if( mTexturep )
		{
			if( mTexturep->getComponents() == 4 )
			{
				gl_rect_2d_checkerboard( interior );
			}

			gl_draw_scaled_image( interior.mLeft, interior.mBottom, interior.getWidth(), interior.getHeight(), mTexturep );

			// Pump the priority
			mTexturep->addTextureStats( (F32)(interior.getWidth() * interior.getHeight()) );

			// Draw Tentative Label over the image
			if( mOwner->getTentative() && !mIsDirty )
			{
				mTentativeLabel->setVisible( TRUE );
				drawChild(mTentativeLabel);
			}
		}
		else
		{
			gl_rect_2d( interior, LLColor4::grey, TRUE );

			// Draw X
			gl_draw_x(interior, LLColor4::black );
		}
	}
}

// static
/*
void LLFloaterTexturePicker::onSaveAnotherCopyDialog( S32 option, void* userdata )
{
	LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
	if( 0 == option )
	{
		self->copyToInventoryFinal();
	}
}
*/

const LLUUID& LLFloaterTexturePicker::findItemID(const LLUUID& asset_id, BOOL copyable_only)
{
	LLViewerInventoryCategory::cat_array_t cats;
	LLViewerInventoryItem::item_array_t items;
	LLAssetIDMatches asset_id_matches(asset_id);
	gInventory.collectDescendentsIf(LLUUID::null,
							cats,
							items,
							LLInventoryModel::INCLUDE_TRASH,
							asset_id_matches);

	if (items.count())
	{
		// search for copyable version first
		for (S32 i = 0; i < items.count(); i++)
		{
			LLInventoryItem* itemp = items[i];
			LLPermissions item_permissions = itemp->getPermissions();
			if (item_permissions.allowCopyBy(gAgent.getID(), gAgent.getGroupID()))
			{
				return itemp->getUUID();
			}
		}
		// otherwise just return first instance, unless copyable requested
		if (copyable_only)
		{
			return LLUUID::null;
		}
		else
		{
			return items[0]->getUUID();
		}
	}

	return LLUUID::null;
}

PermissionMask LLFloaterTexturePicker::getFilterPermMask()
{
	bool apply_immediate = childGetValue("apply_immediate_check").asBoolean();
	return apply_immediate ? mImmediateFilterPermMask : mNonImmediateFilterPermMask;
}

void LLFloaterTexturePicker::commitIfImmediateSet()
{
	bool apply_immediate = childGetValue("apply_immediate_check").asBoolean();
	if (!mNoCopyTextureSelected && apply_immediate && mOwner)
	{
		mOwner->onFloaterCommit(LLTextureCtrl::TEXTURE_CHANGE);
	}
}

// static
void LLFloaterTexturePicker::onBtnSetToDefault(void* userdata)
{
	LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
	if (self->mOwner)
	{
		self->setImageID( self->mOwner->getDefaultImageAssetID() );
	}
	self->commitIfImmediateSet();
}

// static
void LLFloaterTexturePicker::onBtnWhite(void* userdata)
{
	LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
	self->setImageID( self->mWhiteImageAssetID );
	self->commitIfImmediateSet();
}


// static
void LLFloaterTexturePicker::onBtnNone(void* userdata)
{
	LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
	self->setImageID( LLUUID::null );
	self->commitIfImmediateSet();
}

/*
// static
void LLFloaterTexturePicker::onBtnRevert(void* userdata)
{
	LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
	self->setImageID( self->mOriginalImageAssetID );
	// TODO: Change this to tell the owner to cancel.  It needs to be
	// smart enough to restore multi-texture selections.
	self->mOwner->onFloaterCommit();
	self->mIsDirty = FALSE;
}*/

// static
void LLFloaterTexturePicker::onBtnCancel(void* userdata)
{
	LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
	self->setImageID( self->mOriginalImageAssetID );
	if (self->mOwner)
	{
		self->mOwner->onFloaterCommit(LLTextureCtrl::TEXTURE_CANCEL);
	}
	self->mIsDirty = FALSE;
	self->close();
}

// static
void LLFloaterTexturePicker::onBtnSelect(void* userdata)
{
	LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
	if (self->mOwner)
	{
		self->mOwner->onFloaterCommit(LLTextureCtrl::TEXTURE_SELECT);
	}
	self->close();
}

// static
void LLFloaterTexturePicker::onBtnPipette( void* userdata )
{
	LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;

	if ( self && gToolMgr)
	{
		BOOL pipette_active = self->childGetValue("Pipette").asBoolean();
		pipette_active = !pipette_active;
		if (pipette_active)
		{
			gToolPipette->setSelectCallback(onTextureSelect, self);
			gToolMgr->setTransientTool(gToolPipette);
		}
		else
		{
			gToolMgr->clearTransientTool();
		}
	}

}

// static 
void LLFloaterTexturePicker::onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action, void* data)
{
	LLFloaterTexturePicker* self = (LLFloaterTexturePicker*)data;
	if (items.size())
	{
		LLFolderViewItem* first_item = items.front();
		LLInventoryItem* itemp = gInventory.getItem(first_item->getListener()->getUUID());
		self->mNoCopyTextureSelected = FALSE;
		if (itemp)
		{
			if (!itemp->getPermissions().allowCopyBy(gAgent.getID()))
			{
				self->mNoCopyTextureSelected = TRUE;
			}
			self->mImageAssetID = itemp->getAssetUUID();
			self->mIsDirty = TRUE;
			if (user_action)
			{
				// only commit intentional selections, not implicit ones
				self->commitIfImmediateSet();
			}
		}
	}
}

// static
void LLFloaterTexturePicker::onShowFolders(LLUICtrl* ctrl, void *user_data)
{
	LLCheckBoxCtrl* check_box = (LLCheckBoxCtrl*)ctrl;
	LLFloaterTexturePicker* picker = (LLFloaterTexturePicker*)user_data;

	if (check_box->get())
	{
		picker->mInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS);
	}
	else
	{
		picker->mInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_NO_FOLDERS);
	}
}

// static
void LLFloaterTexturePicker::onApplyImmediateCheck(LLUICtrl* ctrl, void *user_data)
{
	LLFloaterTexturePicker* picker = (LLFloaterTexturePicker*)user_data;

	LLCheckBoxCtrl* check_box = (LLCheckBoxCtrl*)ctrl;
	gSavedSettings.setBOOL("ApplyTextureImmediately", check_box->get());

	picker->updateFilterPermMask();
	picker->commitIfImmediateSet();
}

void LLFloaterTexturePicker::updateFilterPermMask()
{
	mInventoryPanel->setFilterPermMask( getFilterPermMask() );
}

void LLFloaterTexturePicker::onSearchEdit(const LLString& search_string, void* user_data )
{
	LLFloaterTexturePicker* picker = (LLFloaterTexturePicker*)user_data;

	std::string filter_text = search_string;

	if (filter_text.empty()&& picker->mInventoryPanel->getFilterSubString().empty())
	{
		// current filter and new filter empty, do nothing
		return;
	}
	std::string upper_case_search_string = filter_text;
	LLString::toUpper(upper_case_search_string);

	picker->mInventoryPanel->setFilterSubString(upper_case_search_string);

	LLFolderView* root_folder = picker->mInventoryPanel->getRootFolder();

	//if (search_string.size())
	//{
	//	LLSelectFirstFilteredItem filter;
	//	root_folder->applyFunctorRecursively(filter);
	//	//...and scroll to show it
	//	root_folder->scrollToShowSelection();
	//}

	KEY key = gKeyboard->currentKey();

	if ((key == KEY_RETURN || key == KEY_DOWN) && gKeyboard->currentMask(FALSE) == MASK_NONE)
	{
		if (search_string.size())
		{
			LLSelectFirstFilteredItem filter;
			root_folder->applyFunctorRecursively(filter);
			//...and scroll to show it
			root_folder->scrollToShowSelection();
		}

		if (!root_folder->getCurSelectedItem())
		{
			LLFolderViewItem* itemp = root_folder->getItemByID(gAgent.getInventoryRootID());
			if (itemp)
			{
				root_folder->setSelection(itemp, FALSE, FALSE);
			}
		}

		// move focus to inventory proper
		root_folder->setFocus(TRUE);
		root_folder->scrollToShowSelection();
	}
}

//static 
void LLFloaterTexturePicker::onTextureSelect( const LLTextureEntry& te, void *data )
{
	LLFloaterTexturePicker* self = (LLFloaterTexturePicker*)data;

	LLUUID inventory_item_id = self->findItemID(te.getID(), TRUE);
	if (self && inventory_item_id.notNull())
	{
		gToolPipette->setResult(TRUE, "");
		self->setImageID(te.getID());

		self->mNoCopyTextureSelected = FALSE;
		LLInventoryItem* itemp = gInventory.getItem(inventory_item_id);

		if (itemp && !itemp->getPermissions().allowCopyBy(gAgent.getID()))
		{
			// no copy texture
			self->mNoCopyTextureSelected = TRUE;
		}
		
		self->commitIfImmediateSet();
	}
	else
	{
		gToolPipette->setResult(FALSE, "You do not have a copy this \nof texture in your inventory");
	}
}

///////////////////////////////////////////////////////////////////////
// LLTextureCtrl

LLTextureCtrl::LLTextureCtrl(
	const std::string& name, 
	const LLRect &rect, 
	const std::string& label,
	const LLUUID &image_id, 
	const LLUUID &default_image_id, 
	const std::string& default_image_name )
	:	
	LLUICtrl(name, rect, TRUE, NULL, NULL, FOLLOWS_LEFT | FOLLOWS_TOP),
	mDragCallback(NULL),
	mDropCallback(NULL),
	mOnCancelCallback(NULL),
	mOnSelectCallback(NULL),
	mBorderColor( gColors.getColor("DefaultHighlightLight") ),
	mImageAssetID( image_id ),
	mDefaultImageAssetID( default_image_id ),
	mDefaultImageName( default_image_name ),
	mLabel( label ),
	mAllowNoTexture( FALSE ),
	mImmediateFilterPermMask( PERM_NONE ),
	mNonImmediateFilterPermMask( PERM_NONE ),
	mCanApplyImmediately( FALSE ),
	mNeedsRawImageData( FALSE ),
	mValid( TRUE )
{
	mCaption = new LLTextBox( label, 
		LLRect( 0, BTN_HEIGHT_SMALL, mRect.getWidth(), 0 ),
		NULL,
		LLFontGL::sSansSerifSmall );
	mCaption->setFollows( FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM );
	addChild( mCaption );

	S32 image_top = mRect.getHeight();
	S32 image_bottom = BTN_HEIGHT_SMALL;
	S32 image_middle = (image_top + image_bottom) / 2;
	S32 line_height = llround(LLFontGL::sSansSerifSmall->getLineHeight());

	mTentativeLabel = new LLTextBox( "Multiple", 
		LLRect( 
			0, image_middle + line_height / 2,
			mRect.getWidth(), image_middle - line_height / 2 ),
		NULL,
		LLFontGL::sSansSerifSmall );
	mTentativeLabel->setHAlign( LLFontGL::HCENTER );
	mTentativeLabel->setFollowsAll();
	addChild( mTentativeLabel );

	LLRect border_rect(0, mRect.getHeight(), mRect.getWidth(), 0);
	border_rect.mBottom += BTN_HEIGHT_SMALL;
	mBorder = new LLViewBorder("border", border_rect, LLViewBorder::BEVEL_IN);
	addChild(mBorder);

	setEnabled(TRUE); // for the tooltip
}


LLTextureCtrl::~LLTextureCtrl()
{
	closeFloater();
}

// virtual
LLXMLNodePtr LLTextureCtrl::getXML(bool save_children) const
{
	LLXMLNodePtr node = LLUICtrl::getXML();

	node->createChild("label", TRUE)->setStringValue(getLabel());

	node->createChild("default_image_name", TRUE)->setStringValue(getDefaultImageName());

	node->createChild("allow_no_texture", TRUE)->setBoolValue(mAllowNoTexture);

	node->createChild("can_apply_immediately", TRUE)->setBoolValue(mCanApplyImmediately );

	return node;
}

LLView* LLTextureCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
{
	LLString name("texture_picker");
	node->getAttributeString("name", name);

	LLRect rect;
	createRect(node, rect, parent);

	LLString label;
	node->getAttributeString("label", label);

	LLString image_id("");
	node->getAttributeString("image", image_id);

	LLString default_image_id("");
	node->getAttributeString("default_image", default_image_id);

	LLString default_image_name("Default");
	node->getAttributeString("default_image_name", default_image_name);

	BOOL allow_no_texture = FALSE;
	node->getAttributeBOOL("allow_no_texture", allow_no_texture);

	BOOL can_apply_immediately = FALSE;
	node->getAttributeBOOL("can_apply_immediately", can_apply_immediately);

	if (label.empty())
	{
		label.assign(node->getValue());
	}

	LLTextureCtrl* texture_picker = new LLTextureCtrl(
									name, 
									rect,
									label,
									LLUUID(image_id),
									LLUUID(default_image_id), 
									default_image_name );
	texture_picker->setAllowNoTexture(allow_no_texture);
	texture_picker->setCanApplyImmediately(can_apply_immediately);

	texture_picker->initFromXML(node, parent);

	return texture_picker;
}

void LLTextureCtrl::setCaption(const LLString& caption)
{
	mCaption->setText( caption );
}

void LLTextureCtrl::setCanApplyImmediately(BOOL b)
{
	mCanApplyImmediately = b; 
	LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)LLFloater::getFloaterByHandle(mFloaterHandle);
	if( floaterp )
	{
		floaterp->setCanApplyImmediately(b);
	}
}

void LLTextureCtrl::setVisible( BOOL visible ) 
{
	if( !visible )
	{
		closeFloater();
	}
	LLUICtrl::setVisible( visible );
}

void LLTextureCtrl::setEnabled( BOOL enabled )
{
	if( enabled )
	{
		setToolTip( "Click to choose a picture" );
	}
	else
	{
		setToolTip( "" );
		//FIXME: would be better to keep floater open
		// and show disabled state
		closeFloater();
	}

	LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)LLFloater::getFloaterByHandle(mFloaterHandle);
	if( floaterp )
	{
		floaterp->setActive(enabled);
	}

	mCaption->setEnabled( enabled );

	LLUICtrl::setEnabled( enabled );
}

void LLTextureCtrl::setValid(BOOL valid )
{
	mValid = valid;
	if (!valid)
	{
		LLFloaterTexturePicker* pickerp = (LLFloaterTexturePicker*)LLFloater::getFloaterByHandle(mFloaterHandle);
		if (pickerp)
		{
			pickerp->setActive(FALSE);
		}
	}
}


// virtual
void LLTextureCtrl::clear()
{
	setImageAssetID(LLUUID::null);
}

void LLTextureCtrl::setLabel(const LLString& label)
{
	mLabel = label;
	mCaption->setText(label);
}

void LLTextureCtrl::showPicker(BOOL take_focus)
{
	LLFloater* floaterp = LLFloater::getFloaterByHandle(mFloaterHandle);

	// Show the dialog
	if( floaterp )
	{
		floaterp->open( );
	}
	else
	{
		if( !mLastFloaterLeftTop.mX && !mLastFloaterLeftTop.mY )
		{
			gFloaterView->getNewFloaterPosition(&mLastFloaterLeftTop.mX, &mLastFloaterLeftTop.mY);
		}
		LLRect rect = gSavedSettings.getRect("TexturePickerRect");
		rect.translate( mLastFloaterLeftTop.mX - rect.mLeft, mLastFloaterLeftTop.mY - rect.mTop );

		floaterp = new LLFloaterTexturePicker(
			this,
			rect,
			mLabel,
			mImmediateFilterPermMask,
			mNonImmediateFilterPermMask,
			mCanApplyImmediately);
		mFloaterHandle = floaterp->getHandle();

		gFloaterView->getParentFloater(this)->addDependentFloater(floaterp);
		floaterp->open();
	}

	if (take_focus)
	{
		floaterp->setFocus(TRUE);
	}
}


void LLTextureCtrl::closeFloater()
{
	LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)LLFloater::getFloaterByHandle(mFloaterHandle);
	if( floaterp )
	{
		floaterp->setOwner(NULL);
		floaterp->close();
	}
}

// Allow us to download textures quickly when floater is shown
class LLTextureFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver
{
public:
	virtual void done()
	{
		// We need to find textures in all folders, so get the main
		// background download going.
		gInventory.startBackgroundFetch();
		gInventory.removeObserver(this);
		delete this;
	}
};

BOOL LLTextureCtrl::handleHover(S32 x, S32 y, MASK mask)
{
	getWindow()->setCursor(UI_CURSOR_HAND);
	return TRUE;
}


BOOL LLTextureCtrl::handleMouseDown(S32 x, S32 y, MASK mask)
{
	BOOL handled = LLUICtrl::handleMouseDown( x, y , mask );
	if( handled )
	{
		showPicker(FALSE);

		//grab textures first...
		gInventory.startBackgroundFetch(gInventory.findCategoryUUIDForType(LLAssetType::AT_TEXTURE));
		//...then start full inventory fetch.
		gInventory.startBackgroundFetch();
	}
	return handled;
}

void LLTextureCtrl::onFloaterClose()
{
	LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)LLFloater::getFloaterByHandle(mFloaterHandle);

	if (floaterp)
	{
		floaterp->setOwner(NULL);
		mLastFloaterLeftTop.set( floaterp->getRect().mLeft, floaterp->getRect().mTop );
	}

	mFloaterHandle.markDead();
}

void LLTextureCtrl::onFloaterCommit(ETexturePickOp op)
{
	LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)LLFloater::getFloaterByHandle(mFloaterHandle);

	if( floaterp && mEnabled)
	{
		if( floaterp->isDirty() )
		{
			setTentative( FALSE );
			mImageItemID = floaterp->findItemID(floaterp->getAssetID(), FALSE);
			lldebugs << "mImageItemID: " << mImageItemID << llendl;
			mImageAssetID = floaterp->getAssetID();
			lldebugs << "mImageAssetID: " << mImageAssetID << llendl;
			if (op == TEXTURE_SELECT && mOnSelectCallback)
			{
				mOnSelectCallback(this, mCallbackUserData);
			}
			else if (op == TEXTURE_CANCEL && mOnCancelCallback)
			{
				mOnCancelCallback(this, mCallbackUserData);
			}
			else
			{
				onCommit();
			}
		}
	}
}

void LLTextureCtrl::setImageAssetID( const LLUUID& asset_id )
{
	if( mImageAssetID != asset_id )
	{
		mImageItemID.setNull();
		mImageAssetID = asset_id;
		LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)LLFloater::getFloaterByHandle(mFloaterHandle);
		if( floaterp && getEnabled() )
		{
			floaterp->setImageID( asset_id );
			floaterp->setDirty( FALSE );
		}
	}
}

BOOL LLTextureCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask,
					  BOOL drop, EDragAndDropType cargo_type, void *cargo_data,
					  EAcceptance *accept,
					  LLString& tooltip_msg)
{
	BOOL handled = FALSE;

	// this downcast may be invalid - but if the second test below
	// returns true, then the cast was valid, and we can perform
	// the third test without problems.
	LLInventoryItem* item = (LLInventoryItem*)cargo_data; 
	if (mEnabled && (cargo_type == DAD_TEXTURE) && allowDrop(item))
	{
		if (drop)
		{
			if(doDrop(item))
			{
				// This removes the 'Multiple' overlay, since
				// there is now only one texture selected.
				setTentative( FALSE ); 
				onCommit();
			}
		}

		*accept = ACCEPT_YES_SINGLE;
	}
	else
	{
		*accept = ACCEPT_NO;
	}

	handled = TRUE;
	lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLTextureCtrl " << getName() << llendl;

	return handled;
}

void LLTextureCtrl::draw()
{
	if( getVisible() ) 
	{
		mBorder->setKeyboardFocusHighlight(hasFocus());

		if (mImageAssetID.isNull() || !mValid)
		{
			mTexturep = NULL;
		}
		else
		{
			mTexturep = gImageList.getImage(mImageAssetID, MIPMAP_YES, IMMEDIATE_NO);
			mTexturep->setBoostLevel(LLViewerImage::BOOST_PREVIEW);
		}
		
		// Border
		LLRect border( 0, mRect.getHeight(), mRect.getWidth(), BTN_HEIGHT_SMALL );
		gl_rect_2d( border, mBorderColor, FALSE );

		// Interior
		LLRect interior = border;
		interior.stretch( -1 ); 

		if( mTexturep )
		{
			if( mTexturep->getComponents() == 4 )
			{
				gl_rect_2d_checkerboard( interior );
			}
			
			gl_draw_scaled_image( interior.mLeft, interior.mBottom, interior.getWidth(), interior.getHeight(), mTexturep);
			mTexturep->addTextureStats( (F32)(interior.getWidth() * interior.getHeight()) );
		}
		else
		{
			gl_rect_2d( interior, LLColor4::grey, TRUE );

			// Draw X
			gl_draw_x( interior, LLColor4::black );
		}

		mTentativeLabel->setVisible( !mTexturep.isNull() && mTentative );

		LLUICtrl::draw();
	}
}

BOOL LLTextureCtrl::allowDrop(LLInventoryItem* item)
{
	BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID());
	BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID());
	BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER,
														gAgent.getID());

	PermissionMask item_perm_mask = 0;
	if (copy) item_perm_mask |= PERM_COPY;
	if (mod)  item_perm_mask |= PERM_MODIFY;
	if (xfer) item_perm_mask |= PERM_TRANSFER;
	
	PermissionMask filter_perm_mask = mCanApplyImmediately ? 
			mImmediateFilterPermMask : mNonImmediateFilterPermMask;
	if ( (item_perm_mask & filter_perm_mask) == filter_perm_mask )
	{
		if(mDragCallback)
		{
			return mDragCallback(this, item, mCallbackUserData);
		}
		else
		{
			return TRUE;
		}
	}
	else
	{
		return FALSE;
	}
}

BOOL LLTextureCtrl::doDrop(LLInventoryItem* item)
{
	// call the callback if it exists.
	if(mDropCallback)
	{
		// if it returns TRUE, we return TRUE, and therefore the
		// commit is called above.
		return mDropCallback(this, item, mCallbackUserData);
	}

	// no callback installed, so just set the image ids and carry on.
	setImageAssetID( item->getAssetUUID() );
	mImageItemID = item->getUUID();
	return TRUE;
}

BOOL LLTextureCtrl::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent)
{
	if( getVisible() && mEnabled && !called_from_parent && ' ' == uni_char )
	{
		showPicker(TRUE);
		return TRUE;
	}
	return LLUICtrl::handleUnicodeCharHere(uni_char, called_from_parent);
}

void LLTextureCtrl::setValue( LLSD value )
{
	setImageAssetID(value.asUUID());
}

LLSD LLTextureCtrl::getValue() const
{
	return LLSD(getImageAssetID());
}


/////////////////////////////////////////////////////////////////////////////////
// LLToolTexEyedropper

class LLToolTexEyedropper : public LLTool 
{
public:
	LLToolTexEyedropper( void (*callback)(const LLUUID& obj_id, const LLUUID& image_id, void* userdata ), void* userdata );
	virtual ~LLToolTexEyedropper();
	virtual BOOL		handleMouseDown(S32 x, S32 y, MASK mask);
	virtual BOOL		handleHover(S32 x, S32 y, MASK mask);

protected:
	void				(*mCallback)(const LLUUID& obj_id, const LLUUID& image_id, void* userdata );
	void*				mCallbackUserData;
};


LLToolTexEyedropper::LLToolTexEyedropper( 
	void (*callback)(const LLUUID& obj_id, const LLUUID& image_id, void* userdata ),
	void* userdata )
	: LLTool("texeyedropper"),
	  mCallback( callback ),
	  mCallbackUserData( userdata )
{
}

LLToolTexEyedropper::~LLToolTexEyedropper()
{
}


BOOL LLToolTexEyedropper::handleMouseDown(S32 x, S32 y, MASK mask)
{
	LLViewerObject* hit_obj	= gViewerWindow->lastObjectHit();
	if (hit_obj && 
		!hit_obj->isAvatar())
	{
		if( (0 <= gLastHitObjectFace) && (gLastHitObjectFace < hit_obj->getNumTEs()) )
		{
			LLViewerImage* image = hit_obj->getTEImage( gLastHitObjectFace );
			if( image )
			{
				if( mCallback )
				{
					mCallback( hit_obj->getID(), image->getID(), mCallbackUserData );
				}
			}
		}
	}
	return TRUE;
}

BOOL LLToolTexEyedropper::handleHover(S32 x, S32 y, MASK mask)
{
	lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolTexEyedropper" << llendl;
	gViewerWindow->getWindow()->setCursor(UI_CURSOR_CROSS);  // TODO: better cursor
	return TRUE;
}

