PageRenderTime 133ms CodeModel.GetById 23ms app.highlight 96ms RepoModel.GetById 2ms app.codeStats 0ms

/indra/newview/lltooldraganddrop.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2332 lines | 1792 code | 213 blank | 327 comment | 371 complexity | 15e4b703e8f763512171ef56912241da MD5 | raw file

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

   1/** 
   2 * @file lltooldraganddrop.cpp
   3 * @brief LLToolDragAndDrop class implementation
   4 *
   5 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
   6 * Second Life Viewer Source Code
   7 * Copyright (C) 2010, Linden Research, Inc.
   8 * 
   9 * This library is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public
  11 * License as published by the Free Software Foundation;
  12 * version 2.1 of the License only.
  13 * 
  14 * This library is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * Lesser General Public License for more details.
  18 * 
  19 * You should have received a copy of the GNU Lesser General Public
  20 * License along with this library; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  22 * 
  23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  24 * $/LicenseInfo$
  25 */
  26
  27#include "llviewerprecompiledheaders.h"
  28#include "lltooldraganddrop.h"
  29
  30// library headers
  31#include "llnotificationsutil.h"
  32// project headers
  33#include "llagent.h"
  34#include "llagentcamera.h"
  35#include "llagentwearables.h"
  36#include "llappearancemgr.h"
  37#include "lldictionary.h"
  38#include "llfloaterreg.h"
  39#include "llfloatertools.h"
  40#include "llgesturemgr.h"
  41#include "llgiveinventory.h"
  42#include "llhudmanager.h"
  43#include "llhudeffecttrail.h"
  44#include "llimview.h"
  45#include "llinventorybridge.h"
  46#include "llinventorydefines.h"
  47#include "llinventoryfunctions.h"
  48#include "llpreviewnotecard.h"
  49#include "llrootview.h"
  50#include "llselectmgr.h"
  51#include "lltoolmgr.h"
  52#include "lltooltip.h"
  53#include "lltrans.h"
  54#include "llviewerobjectlist.h"
  55#include "llviewerregion.h"
  56#include "llviewerstats.h"
  57#include "llviewerwindow.h"
  58#include "llvoavatarself.h"
  59#include "llworld.h"
  60#include "llclipboard.h"
  61
  62// syntactic sugar
  63#define callMemberFunction(object,ptrToMember)  ((object).*(ptrToMember))
  64
  65class LLNoPreferredType : public LLInventoryCollectFunctor
  66{
  67public:
  68	LLNoPreferredType() {}
  69	virtual ~LLNoPreferredType() {}
  70	virtual bool operator()(LLInventoryCategory* cat,
  71							LLInventoryItem* item)
  72	{
  73		if (cat && (cat->getPreferredType() == LLFolderType::FT_NONE))
  74		{
  75			return true;
  76		}
  77		return false;
  78	}
  79};
  80
  81class LLNoPreferredTypeOrItem : public LLInventoryCollectFunctor
  82{
  83public:
  84	LLNoPreferredTypeOrItem() {}
  85	virtual ~LLNoPreferredTypeOrItem() {}
  86	virtual bool operator()(LLInventoryCategory* cat,
  87							LLInventoryItem* item)
  88	{
  89		if (item) return true;
  90		if (cat && (cat->getPreferredType() == LLFolderType::FT_NONE))
  91		{
  92			return true;
  93		}
  94		return false;
  95	}
  96};
  97
  98class LLDroppableItem : public LLInventoryCollectFunctor
  99{
 100public:
 101	LLDroppableItem(BOOL is_transfer) :
 102		mCountLosing(0), mIsTransfer(is_transfer) {}
 103	virtual ~LLDroppableItem() {}
 104	virtual bool operator()(LLInventoryCategory* cat,
 105							LLInventoryItem* item);
 106	S32 countNoCopy() const { return mCountLosing; }
 107
 108protected:
 109	S32 mCountLosing;
 110	BOOL mIsTransfer;
 111};
 112
 113bool LLDroppableItem::operator()(LLInventoryCategory* cat,
 114				 LLInventoryItem* item)
 115{
 116	bool allowed = false;
 117	if (item)
 118	{
 119		allowed = itemTransferCommonlyAllowed(item);
 120
 121		if (allowed
 122		   && mIsTransfer
 123		   && !item->getPermissions().allowOperationBy(PERM_TRANSFER,
 124							       gAgent.getID()))
 125		{
 126			allowed = false;
 127		}
 128		if (allowed && !item->getPermissions().allowCopyBy(gAgent.getID()))
 129		{
 130			++mCountLosing;
 131		}
 132	}
 133	return allowed;
 134}
 135
 136class LLDropCopyableItems : public LLInventoryCollectFunctor
 137{
 138public:
 139	LLDropCopyableItems() {}
 140	virtual ~LLDropCopyableItems() {}
 141	virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
 142};
 143
 144
 145bool LLDropCopyableItems::operator()(
 146	LLInventoryCategory* cat,
 147	LLInventoryItem* item)
 148{
 149	bool allowed = false;
 150	if (item)
 151	{
 152		allowed = itemTransferCommonlyAllowed(item);
 153		if (allowed &&
 154		   !item->getPermissions().allowCopyBy(gAgent.getID()))
 155		{
 156			// whoops, can't copy it - don't allow it.
 157			allowed = false;
 158		}
 159	}
 160	return allowed;
 161}
 162
 163// Starts a fetch on folders and items.  This is really not used 
 164// as an observer in the traditional sense; we're just using it to
 165// request a fetch and we don't care about when/if the response arrives.
 166class LLCategoryFireAndForget : public LLInventoryFetchComboObserver
 167{
 168public:
 169	LLCategoryFireAndForget(const uuid_vec_t& folder_ids,
 170							const uuid_vec_t& item_ids) :
 171		LLInventoryFetchComboObserver(folder_ids, item_ids)
 172	{}
 173	~LLCategoryFireAndForget() {}
 174	virtual void done()
 175	{
 176		/* no-op: it's fire n forget right? */
 177		lldebugs << "LLCategoryFireAndForget::done()" << llendl;
 178	}
 179};
 180
 181class LLCategoryDropObserver : public LLInventoryFetchItemsObserver
 182{
 183public:
 184	LLCategoryDropObserver(
 185		const uuid_vec_t& ids,
 186		const LLUUID& obj_id, LLToolDragAndDrop::ESource src) :
 187		LLInventoryFetchItemsObserver(ids),
 188		mObjectID(obj_id),
 189		mSource(src)
 190	{}
 191	~LLCategoryDropObserver() {}
 192	virtual void done();
 193
 194protected:
 195	LLUUID mObjectID;
 196	LLToolDragAndDrop::ESource mSource;
 197};
 198
 199void LLCategoryDropObserver::done()
 200{
 201	gInventory.removeObserver(this);
 202	LLViewerObject* dst_obj = gObjectList.findObject(mObjectID);
 203	if (dst_obj)
 204	{
 205		// *FIX: coalesce these...
 206 		LLInventoryItem* item = NULL;
 207  		uuid_vec_t::iterator it = mComplete.begin();
 208  		uuid_vec_t::iterator end = mComplete.end();
 209  		for(; it < end; ++it)
 210  		{
 211 			item = gInventory.getItem(*it);
 212 			if (item)
 213 			{
 214 				LLToolDragAndDrop::dropInventory(
 215 					dst_obj,
 216 					item,
 217 					mSource,
 218 					LLUUID::null);
 219 			}
 220  		}
 221	}
 222	delete this;
 223}
 224/* Doesn't seem to be used anymore.
 225class LLCategoryDropDescendentsObserver : public LLInventoryFetchDescendentsObserver
 226{
 227public:
 228	LLCategoryDropDescendentsObserver(
 229		const LLUUID& obj_id, LLToolDragAndDrop::ESource src) :
 230		mObjectID(obj_id),
 231		mSource(src)
 232	{}
 233	~LLCategoryDropDescendentsObserver() {}
 234	virtual void done();
 235
 236protected:
 237	LLUUID mObjectID;
 238	LLToolDragAndDrop::ESource mSource;
 239};
 240
 241void LLCategoryDropDescendentsObserver::done()
 242{
 243
 244	gInventory.removeObserver(this);
 245	uuid_vec_t::iterator it = mComplete.begin();
 246	uuid_vec_t::iterator end = mComplete.end();
 247	LLViewerInventoryCategory::cat_array_t cats;
 248	LLViewerInventoryItem::item_array_t items;
 249	for(; it != end; ++it)
 250	{
 251		gInventory.collectDescendents(
 252			(*it),
 253			cats,
 254			items,
 255			LLInventoryModel::EXCLUDE_TRASH);
 256	}
 257
 258	S32 count = items.count();
 259	if (count)
 260	{
 261		std::set<LLUUID> unique_ids;
 262		for(S32 i = 0; i < count; ++i)
 263		{
 264			unique_ids.insert(items.get(i)->getUUID());
 265		}
 266		uuid_vec_t ids;
 267		std::back_insert_iterator<uuid_vec_t> copier(ids);
 268		std::copy(unique_ids.begin(), unique_ids.end(), copier);
 269		LLCategoryDropObserver* dropper;
 270		dropper = new LLCategoryDropObserver(ids, mObjectID, mSource);
 271		dropper->startFetch();
 272		if (dropper->isDone())
 273		{
 274			dropper->done();
 275		}
 276		else
 277		{
 278			gInventory.addObserver(dropper);
 279		}
 280	}
 281	delete this;
 282}
 283*/
 284
 285S32 LLToolDragAndDrop::sOperationId = 0;
 286
 287LLToolDragAndDrop::DragAndDropEntry::DragAndDropEntry(dragOrDrop3dImpl f_none,
 288													  dragOrDrop3dImpl f_self,
 289													  dragOrDrop3dImpl f_avatar,
 290													  dragOrDrop3dImpl f_object,
 291													  dragOrDrop3dImpl f_land) :
 292	LLDictionaryEntry("")
 293{
 294	mFunctions[DT_NONE] = f_none;
 295	mFunctions[DT_SELF] = f_self;
 296	mFunctions[DT_AVATAR] = f_avatar;
 297	mFunctions[DT_OBJECT] = f_object;
 298	mFunctions[DT_LAND] = f_land;
 299}
 300
 301LLToolDragAndDrop::dragOrDrop3dImpl LLToolDragAndDrop::LLDragAndDropDictionary::get(EDragAndDropType dad_type, LLToolDragAndDrop::EDropTarget drop_target)
 302{
 303	const DragAndDropEntry *entry = lookup(dad_type);
 304	if (entry)
 305	{
 306		return (entry->mFunctions[(U8)drop_target]);
 307	}
 308	return &LLToolDragAndDrop::dad3dNULL;
 309}
 310
 311LLToolDragAndDrop::LLDragAndDropDictionary::LLDragAndDropDictionary()
 312{
 313 	//       										 DT_NONE                         DT_SELF                                        DT_AVATAR                   					DT_OBJECT                       					DT_LAND		
 314	//      										|-------------------------------|----------------------------------------------|-----------------------------------------------|---------------------------------------------------|--------------------------------|
 315	addEntry(DAD_NONE, 			new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dNULL,						&LLToolDragAndDrop::dad3dNULL));
 316	addEntry(DAD_TEXTURE, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dGiveInventory,			&LLToolDragAndDrop::dad3dTextureObject,				&LLToolDragAndDrop::dad3dNULL));
 317	addEntry(DAD_SOUND, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dGiveInventory,			&LLToolDragAndDrop::dad3dUpdateInventory,			&LLToolDragAndDrop::dad3dNULL));
 318	addEntry(DAD_CALLINGCARD, 	new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dGiveInventory, 		&LLToolDragAndDrop::dad3dUpdateInventory, 			&LLToolDragAndDrop::dad3dNULL));
 319	addEntry(DAD_LANDMARK, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, 					&LLToolDragAndDrop::dad3dGiveInventory, 		&LLToolDragAndDrop::dad3dUpdateInventory, 			&LLToolDragAndDrop::dad3dNULL));
 320	addEntry(DAD_SCRIPT, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, 					&LLToolDragAndDrop::dad3dGiveInventory, 		&LLToolDragAndDrop::dad3dRezScript, 				&LLToolDragAndDrop::dad3dNULL));
 321	addEntry(DAD_CLOTHING, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dWearItem, 				&LLToolDragAndDrop::dad3dGiveInventory, 		&LLToolDragAndDrop::dad3dUpdateInventory, 			&LLToolDragAndDrop::dad3dNULL));
 322	addEntry(DAD_OBJECT, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dRezAttachmentFromInv,	&LLToolDragAndDrop::dad3dGiveInventoryObject,	&LLToolDragAndDrop::dad3dRezObjectOnObject, 		&LLToolDragAndDrop::dad3dRezObjectOnLand));
 323	addEntry(DAD_NOTECARD, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, 					&LLToolDragAndDrop::dad3dGiveInventory, 		&LLToolDragAndDrop::dad3dUpdateInventory, 			&LLToolDragAndDrop::dad3dNULL));
 324	addEntry(DAD_CATEGORY, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dWearCategory,			&LLToolDragAndDrop::dad3dGiveInventoryCategory,	&LLToolDragAndDrop::dad3dUpdateInventoryCategory,	&LLToolDragAndDrop::dad3dNULL));
 325	addEntry(DAD_ROOT_CATEGORY, new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dNULL,						&LLToolDragAndDrop::dad3dNULL));
 326	addEntry(DAD_BODYPART, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dWearItem,				&LLToolDragAndDrop::dad3dGiveInventory,			&LLToolDragAndDrop::dad3dUpdateInventory,			&LLToolDragAndDrop::dad3dNULL));
 327	addEntry(DAD_ANIMATION, 	new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dGiveInventory,			&LLToolDragAndDrop::dad3dUpdateInventory,			&LLToolDragAndDrop::dad3dNULL));
 328	addEntry(DAD_GESTURE, 		new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dActivateGesture,		&LLToolDragAndDrop::dad3dGiveInventory,			&LLToolDragAndDrop::dad3dUpdateInventory,			&LLToolDragAndDrop::dad3dNULL));
 329	addEntry(DAD_LINK, 			new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dNULL,						&LLToolDragAndDrop::dad3dNULL));
 330	addEntry(DAD_MESH, 			new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL,	&LLToolDragAndDrop::dad3dNULL,					&LLToolDragAndDrop::dad3dGiveInventory,			&LLToolDragAndDrop::dad3dMeshObject,				&LLToolDragAndDrop::dad3dNULL));
 331	// TODO: animation on self could play it?  edit it?
 332	// TODO: gesture on self could play it?  edit it?
 333};
 334
 335LLToolDragAndDrop::LLToolDragAndDrop()
 336:	 LLTool(std::string("draganddrop"), NULL),
 337	 mDragStartX(0),
 338	 mDragStartY(0),
 339	 mSource(SOURCE_AGENT),
 340	 mCursor(UI_CURSOR_NO),
 341	 mLastAccept(ACCEPT_NO),
 342	 mDrop(FALSE),
 343	 mCurItemIndex(0)
 344{
 345
 346}
 347
 348void LLToolDragAndDrop::setDragStart(S32 x, S32 y)
 349{
 350	mDragStartX = x;
 351	mDragStartY = y;
 352}
 353
 354BOOL LLToolDragAndDrop::isOverThreshold(S32 x,S32 y)
 355{
 356	static LLCachedControl<S32> drag_and_drop_threshold(gSavedSettings,"DragAndDropDistanceThreshold");
 357	
 358	S32 mouse_delta_x = x - mDragStartX;
 359	S32 mouse_delta_y = y - mDragStartY;
 360	
 361	return (mouse_delta_x * mouse_delta_x) + (mouse_delta_y * mouse_delta_y) > drag_and_drop_threshold * drag_and_drop_threshold;
 362}
 363
 364void LLToolDragAndDrop::beginDrag(EDragAndDropType type,
 365								  const LLUUID& cargo_id,
 366								  ESource source,
 367								  const LLUUID& source_id,
 368								  const LLUUID& object_id)
 369{
 370	if (type == DAD_NONE)
 371	{
 372		llwarns << "Attempted to start drag without a cargo type" << llendl;
 373		return;
 374	}
 375	mCargoTypes.clear();
 376	mCargoTypes.push_back(type);
 377	mCargoIDs.clear();
 378	mCargoIDs.push_back(cargo_id);
 379	mSource = source;
 380	mSourceID = source_id;
 381	mObjectID = object_id;
 382
 383	setMouseCapture( TRUE );
 384	LLToolMgr::getInstance()->setTransientTool( this );
 385	mCursor = UI_CURSOR_NO;
 386	if ((mCargoTypes[0] == DAD_CATEGORY)
 387	   && ((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY)))
 388	{
 389		LLInventoryCategory* cat = gInventory.getCategory(cargo_id);
 390		// go ahead and fire & forget the descendents if we are not
 391		// dragging a protected folder.
 392		if (cat)
 393		{
 394			LLViewerInventoryCategory::cat_array_t cats;
 395			LLViewerInventoryItem::item_array_t items;
 396			LLNoPreferredTypeOrItem is_not_preferred;
 397			uuid_vec_t folder_ids;
 398			uuid_vec_t item_ids;
 399			if (is_not_preferred(cat, NULL))
 400			{
 401				folder_ids.push_back(cargo_id);
 402			}
 403			gInventory.collectDescendentsIf(
 404				cargo_id,
 405				cats,
 406				items,
 407				LLInventoryModel::EXCLUDE_TRASH,
 408				is_not_preferred);
 409			S32 count = cats.count();
 410			S32 i;
 411			for(i = 0; i < count; ++i)
 412			{
 413				folder_ids.push_back(cats.get(i)->getUUID());
 414			}
 415			count = items.count();
 416			for(i = 0; i < count; ++i)
 417			{
 418				item_ids.push_back(items.get(i)->getUUID());
 419			}
 420			if (!folder_ids.empty() || !item_ids.empty())
 421			{
 422				LLCategoryFireAndForget *fetcher = new LLCategoryFireAndForget(folder_ids, item_ids);
 423				fetcher->startFetch();
 424				delete fetcher;
 425			}
 426		}
 427	}
 428}
 429
 430void LLToolDragAndDrop::beginMultiDrag(
 431	const std::vector<EDragAndDropType> types,
 432	const uuid_vec_t& cargo_ids,
 433	ESource source,
 434	const LLUUID& source_id)
 435{
 436	// assert on public api is evil
 437	//llassert( type != DAD_NONE );
 438
 439	std::vector<EDragAndDropType>::const_iterator types_it;
 440	for (types_it = types.begin(); types_it != types.end(); ++types_it)
 441	{
 442		if (DAD_NONE == *types_it)
 443		{
 444			llwarns << "Attempted to start drag without a cargo type" << llendl;
 445			return;
 446		}
 447	}
 448	mCargoTypes = types;
 449	mCargoIDs = cargo_ids;
 450	mSource = source;
 451	mSourceID = source_id;
 452
 453	setMouseCapture( TRUE );
 454	LLToolMgr::getInstance()->setTransientTool( this );
 455	mCursor = UI_CURSOR_NO;
 456	if ((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY))
 457	{
 458		// find categories (i.e. inventory folders) in the cargo.
 459		LLInventoryCategory* cat = NULL;
 460		S32 count = llmin(cargo_ids.size(), types.size());
 461		std::set<LLUUID> cat_ids;
 462		for(S32 i = 0; i < count; ++i)
 463		{
 464			cat = gInventory.getCategory(cargo_ids[i]);
 465			if (cat)
 466			{
 467				LLViewerInventoryCategory::cat_array_t cats;
 468				LLViewerInventoryItem::item_array_t items;
 469				LLNoPreferredType is_not_preferred;
 470				if (is_not_preferred(cat, NULL))
 471				{
 472					cat_ids.insert(cat->getUUID());
 473				}
 474				gInventory.collectDescendentsIf(
 475					cat->getUUID(),
 476					cats,
 477					items,
 478					LLInventoryModel::EXCLUDE_TRASH,
 479					is_not_preferred);
 480				S32 cat_count = cats.count();
 481				for(S32 i = 0; i < cat_count; ++i)
 482				{
 483					cat_ids.insert(cat->getUUID());
 484				}
 485			}
 486		}
 487		if (!cat_ids.empty())
 488		{
 489			uuid_vec_t folder_ids;
 490			uuid_vec_t item_ids;
 491			std::back_insert_iterator<uuid_vec_t> copier(folder_ids);
 492			std::copy(cat_ids.begin(), cat_ids.end(), copier);
 493			LLCategoryFireAndForget fetcher(folder_ids, item_ids);
 494		}
 495	}
 496}
 497
 498void LLToolDragAndDrop::endDrag()
 499{
 500	mEndDragSignal();
 501	LLSelectMgr::getInstance()->unhighlightAll();
 502	setMouseCapture(FALSE);
 503}
 504
 505void LLToolDragAndDrop::onMouseCaptureLost()
 506{
 507	// Called whenever the drag ends or if mouse capture is simply lost
 508	LLToolMgr::getInstance()->clearTransientTool();
 509	mCargoTypes.clear();
 510	mCargoIDs.clear();
 511	mSource = SOURCE_AGENT;
 512	mSourceID.setNull();
 513	mObjectID.setNull();
 514}
 515
 516BOOL LLToolDragAndDrop::handleMouseUp( S32 x, S32 y, MASK mask )
 517{
 518	if (hasMouseCapture())
 519	{
 520		EAcceptance acceptance = ACCEPT_NO;
 521		dragOrDrop( x, y, mask, TRUE, &acceptance );
 522		endDrag();
 523	}
 524	return TRUE;
 525}
 526
 527ECursorType LLToolDragAndDrop::acceptanceToCursor( EAcceptance acceptance )
 528{
 529	switch (acceptance)
 530	{
 531	case ACCEPT_YES_MULTI:
 532		if (mCargoIDs.size() > 1)
 533		{
 534			mCursor = UI_CURSOR_ARROWDRAGMULTI;
 535		}
 536		else
 537		{
 538			mCursor = UI_CURSOR_ARROWDRAG;
 539		}
 540		break;
 541	case ACCEPT_YES_SINGLE:
 542		if (mCargoIDs.size() > 1)
 543		{
 544			mToolTipMsg = LLTrans::getString("TooltipMustSingleDrop");
 545			mCursor = UI_CURSOR_NO;
 546		}
 547		else
 548		{
 549			mCursor = UI_CURSOR_ARROWDRAG;
 550		}
 551		break;
 552
 553	case ACCEPT_NO_LOCKED:
 554		mCursor = UI_CURSOR_NOLOCKED;
 555		break;
 556
 557	case ACCEPT_NO:
 558		mCursor = UI_CURSOR_NO;
 559		break;
 560
 561	case ACCEPT_YES_COPY_MULTI:
 562		if (mCargoIDs.size() > 1)
 563		{
 564			mCursor = UI_CURSOR_ARROWCOPYMULTI;
 565		}
 566		else
 567		{
 568			mCursor = UI_CURSOR_ARROWCOPY;
 569		}
 570		break;
 571	case ACCEPT_YES_COPY_SINGLE:
 572		if (mCargoIDs.size() > 1)
 573		{
 574			mToolTipMsg = LLTrans::getString("TooltipMustSingleDrop");
 575			mCursor = UI_CURSOR_NO;
 576		}
 577		else
 578		{
 579			mCursor = UI_CURSOR_ARROWCOPY;
 580		}
 581		break;
 582	case ACCEPT_POSTPONED:
 583		break;
 584	default:
 585		llassert( FALSE );
 586	}
 587
 588	return mCursor;
 589}
 590
 591BOOL LLToolDragAndDrop::handleHover( S32 x, S32 y, MASK mask )
 592{
 593	EAcceptance acceptance = ACCEPT_NO;
 594	dragOrDrop( x, y, mask, FALSE, &acceptance );
 595
 596	ECursorType cursor = acceptanceToCursor(acceptance);
 597	gViewerWindow->getWindow()->setCursor( cursor );
 598
 599	lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolDragAndDrop" << llendl;
 600	return TRUE;
 601}
 602
 603BOOL LLToolDragAndDrop::handleKey(KEY key, MASK mask)
 604{
 605	if (key == KEY_ESCAPE)
 606	{
 607		// cancel drag and drop operation
 608		endDrag();
 609		return TRUE;
 610	}
 611
 612	return FALSE;
 613}
 614
 615BOOL LLToolDragAndDrop::handleToolTip(S32 x, S32 y, MASK mask)
 616{
 617	if (!mToolTipMsg.empty())
 618	{
 619		LLToolTipMgr::instance().unblockToolTips();
 620		LLToolTipMgr::instance().show(LLToolTip::Params()
 621			.message(mToolTipMsg)
 622			.delay_time(gSavedSettings.getF32( "DragAndDropToolTipDelay" )));
 623		return TRUE;
 624	}
 625	return FALSE;
 626}
 627
 628void LLToolDragAndDrop::handleDeselect()
 629{
 630	mToolTipMsg.clear();
 631
 632	LLToolTipMgr::instance().blockToolTips();
 633}
 634
 635// protected
 636void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, 
 637								   EAcceptance* acceptance)
 638{
 639	*acceptance = ACCEPT_YES_MULTI;
 640
 641	BOOL handled = FALSE;
 642
 643	LLView* top_view = gFocusMgr.getTopCtrl();
 644	LLViewerInventoryItem* item;
 645	LLViewerInventoryCategory* cat;
 646
 647	mToolTipMsg.clear();
 648
 649	// Increment the operation id for every drop
 650	if (drop)
 651	{
 652		sOperationId++;
 653	}
 654
 655	if (top_view)
 656	{
 657		handled = TRUE;
 658
 659		for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++)
 660		{
 661			LLInventoryObject* cargo = locateInventory(item, cat);
 662
 663			if (cargo)
 664			{
 665				S32 local_x, local_y;
 666				top_view->screenPointToLocal( x, y, &local_x, &local_y );
 667				EAcceptance item_acceptance = ACCEPT_NO;
 668				handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, FALSE,
 669													mCargoTypes[mCurItemIndex],
 670													(void*)cargo,
 671													&item_acceptance,
 672													mToolTipMsg);
 673				if (handled)
 674				{
 675					// use sort order to determine priority of acceptance
 676					*acceptance = (EAcceptance)llmin((U32)item_acceptance, (U32)*acceptance);
 677				}
 678			}
 679			else
 680			{
 681				return;		
 682			}
 683		}
 684
 685		// all objects passed, go ahead and perform drop if necessary
 686		if (handled && drop && (U32)*acceptance >= ACCEPT_YES_COPY_SINGLE)
 687		{
 688			if ((U32)*acceptance < ACCEPT_YES_COPY_MULTI &&
 689			    mCargoIDs.size() > 1)
 690			{
 691				// tried to give multi-cargo to a single-acceptor - refuse and return.
 692				*acceptance = ACCEPT_NO;
 693				return;
 694			}
 695
 696			for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++)
 697			{
 698				LLInventoryObject* cargo = locateInventory(item, cat);
 699
 700				if (cargo)
 701				{
 702					S32 local_x, local_y;
 703
 704					EAcceptance item_acceptance;
 705					top_view->screenPointToLocal( x, y, &local_x, &local_y );
 706					handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, TRUE,
 707														mCargoTypes[mCurItemIndex],
 708														(void*)cargo,
 709														&item_acceptance,
 710														mToolTipMsg);
 711				}
 712			}
 713		}
 714		if (handled)
 715		{
 716			mLastAccept = (EAcceptance)*acceptance;
 717		}
 718	}
 719
 720	if (!handled)
 721	{
 722		handled = TRUE;
 723
 724		LLRootView* root_view = gViewerWindow->getRootView();
 725
 726		for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++)
 727		{
 728			LLInventoryObject* cargo = locateInventory(item, cat);
 729
 730			// fix for EXT-3191
 731			if (NULL == cargo) return;
 732
 733			EAcceptance item_acceptance = ACCEPT_NO;
 734			handled = handled && root_view->handleDragAndDrop(x, y, mask, FALSE,
 735												mCargoTypes[mCurItemIndex],
 736												(void*)cargo,
 737												&item_acceptance,
 738												mToolTipMsg);
 739			if (handled)
 740			{
 741				// use sort order to determine priority of acceptance
 742				*acceptance = (EAcceptance)llmin((U32)item_acceptance, (U32)*acceptance);
 743			}
 744		}
 745		// all objects passed, go ahead and perform drop if necessary
 746		if (handled && drop && (U32)*acceptance > ACCEPT_NO_LOCKED)
 747		{	
 748			if ((U32)*acceptance < ACCEPT_YES_COPY_MULTI &&
 749			    mCargoIDs.size() > 1)
 750			{
 751				// tried to give multi-cargo to a single-acceptor - refuse and return.
 752				*acceptance = ACCEPT_NO;
 753				return;
 754			}
 755
 756			for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++)
 757			{
 758				LLInventoryObject* cargo = locateInventory(item, cat);
 759
 760				if (cargo)
 761				{
 762					EAcceptance item_acceptance;
 763					handled = handled && root_view->handleDragAndDrop(x, y, mask, TRUE,
 764											  mCargoTypes[mCurItemIndex],
 765											  (void*)cargo,
 766											  &item_acceptance,
 767											  mToolTipMsg);
 768				}
 769			}
 770		}
 771
 772		if (handled)
 773		{
 774			mLastAccept = (EAcceptance)*acceptance;
 775		}
 776	}
 777
 778	if (!handled)
 779	{
 780		// Disallow drag and drop to 3D from the outbox
 781		const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false, false);
 782		if (outbox_id.notNull())
 783		{
 784			for (S32 item_index = 0; item_index < (S32)mCargoIDs.size(); item_index++)
 785			{
 786				if (gInventory.isObjectDescendentOf(mCargoIDs[item_index], outbox_id))
 787				{
 788					*acceptance = ACCEPT_NO;
 789					mToolTipMsg = LLTrans::getString("TooltipOutboxDragToWorld");
 790					return;
 791				}
 792			}
 793		}
 794		
 795		dragOrDrop3D( x, y, mask, drop, acceptance );
 796	}
 797}
 798
 799void LLToolDragAndDrop::dragOrDrop3D( S32 x, S32 y, MASK mask, BOOL drop, EAcceptance* acceptance )
 800{
 801	mDrop = drop;
 802	if (mDrop)
 803	{
 804		// don't allow drag and drop onto transparent objects
 805		pick(gViewerWindow->pickImmediate(x, y, FALSE));
 806	}
 807	else
 808	{
 809		// don't allow drag and drop onto transparent objects
 810		gViewerWindow->pickAsync(x, y, mask, pickCallback, FALSE);
 811	}
 812
 813	*acceptance = mLastAccept;
 814}
 815
 816void LLToolDragAndDrop::pickCallback(const LLPickInfo& pick_info)
 817{
 818	if (getInstance() != NULL)
 819	{
 820		getInstance()->pick(pick_info);
 821	}
 822}
 823
 824void LLToolDragAndDrop::pick(const LLPickInfo& pick_info)
 825{
 826	EDropTarget target = DT_NONE;
 827	S32	hit_face = -1;
 828
 829	LLViewerObject* hit_obj = pick_info.getObject();
 830	LLSelectMgr::getInstance()->unhighlightAll();
 831	bool highlight_object = false;
 832	// Treat attachments as part of the avatar they are attached to.
 833	if (hit_obj != NULL)
 834	{
 835		// don't allow drag and drop on grass, trees, etc.
 836		if (pick_info.mPickType == LLPickInfo::PICK_FLORA)
 837		{
 838			mCursor = UI_CURSOR_NO;
 839			gViewerWindow->getWindow()->setCursor( mCursor );
 840			return;
 841		}
 842
 843		if (hit_obj->isAttachment() && !hit_obj->isHUDAttachment())
 844		{
 845			LLVOAvatar* avatar = LLVOAvatar::findAvatarFromAttachment( hit_obj );
 846			if (!avatar)
 847			{
 848				mLastAccept = ACCEPT_NO;
 849				mCursor = UI_CURSOR_NO;
 850				gViewerWindow->getWindow()->setCursor( mCursor );
 851				return;
 852			}
 853			hit_obj = avatar;
 854		}
 855
 856		if (hit_obj->isAvatar())
 857		{
 858			if (((LLVOAvatar*) hit_obj)->isSelf())
 859			{
 860				target = DT_SELF;
 861				hit_face = -1;
 862			}
 863			else
 864			{
 865				target = DT_AVATAR;
 866				hit_face = -1;
 867			}
 868		}
 869		else
 870		{
 871			target = DT_OBJECT;
 872			hit_face = pick_info.mObjectFace;
 873			highlight_object = true;
 874		}
 875	}
 876	else if (pick_info.mPickType == LLPickInfo::PICK_LAND)
 877	{
 878		target = DT_LAND;
 879		hit_face = -1;
 880	}
 881
 882	mLastAccept = ACCEPT_YES_MULTI;
 883
 884	for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++)
 885	{
 886		const S32 item_index = mCurItemIndex;
 887		const EDragAndDropType dad_type = mCargoTypes[item_index];
 888		// Call the right implementation function
 889		mLastAccept = (EAcceptance)llmin(
 890			(U32)mLastAccept,
 891			(U32)callMemberFunction(*this, 
 892									LLDragAndDropDictionary::instance().get(dad_type, target))
 893				(hit_obj, hit_face, pick_info.mKeyMask, FALSE));
 894	}
 895
 896	if (mDrop && ((U32)mLastAccept >= ACCEPT_YES_COPY_SINGLE))
 897	{
 898		// if target allows multi-drop or there is only one item being dropped, go ahead
 899		if ((mLastAccept >= ACCEPT_YES_COPY_MULTI) || (mCargoIDs.size() == 1))
 900		{
 901			// Target accepts multi, or cargo is a single-drop
 902			for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++)
 903			{
 904				const S32 item_index = mCurItemIndex;
 905				const EDragAndDropType dad_type = mCargoTypes[item_index];
 906				// Call the right implementation function
 907				(U32)callMemberFunction(*this,
 908										LLDragAndDropDictionary::instance().get(dad_type, target))
 909					(hit_obj, hit_face, pick_info.mKeyMask, TRUE);
 910			}
 911		}
 912		else
 913		{
 914			// Target does not accept multi, but cargo is multi
 915			mLastAccept = ACCEPT_NO;
 916		}
 917	}
 918
 919	if (highlight_object && mLastAccept > ACCEPT_NO_LOCKED)
 920	{
 921		// if any item being dragged will be applied to the object under our cursor
 922		// highlight that object
 923		for (S32 i = 0; i < (S32)mCargoIDs.size(); i++)
 924		{
 925			if (mCargoTypes[i] != DAD_OBJECT || (pick_info.mKeyMask & MASK_CONTROL))
 926			{
 927				LLSelectMgr::getInstance()->highlightObjectAndFamily(hit_obj);
 928				break;
 929			}
 930		}
 931	}
 932	ECursorType cursor = acceptanceToCursor( mLastAccept );
 933	gViewerWindow->getWindow()->setCursor( cursor );
 934
 935	mLastHitPos = pick_info.mPosGlobal;
 936	mLastCameraPos = gAgentCamera.getCameraPositionGlobal();
 937}
 938
 939// static
 940BOOL LLToolDragAndDrop::handleDropTextureProtections(LLViewerObject* hit_obj,
 941													 LLInventoryItem* item,
 942													 LLToolDragAndDrop::ESource source,
 943													 const LLUUID& src_id)
 944{
 945	// Always succeed if....
 946	// texture is from the library 
 947	// or already in the contents of the object
 948	if (SOURCE_LIBRARY == source)
 949	{
 950		// dropping a texture from the library always just works.
 951		return TRUE;
 952	}
 953
 954	// In case the inventory has not been updated (e.g. due to some recent operation
 955	// causing a dirty inventory), stall the user while fetching the inventory.
 956	if (hit_obj->isInventoryDirty())
 957	{
 958		hit_obj->fetchInventoryFromServer();
 959		LLSD args;
 960		args["ERROR_MESSAGE"] = "Unable to add texture.\nPlease wait a few seconds and try again.";
 961		LLNotificationsUtil::add("ErrorMessage", args);
 962		return FALSE;
 963	}
 964	if (hit_obj->getInventoryItemByAsset(item->getAssetUUID()))
 965	{
 966		// if the asset is already in the object's inventory 
 967		// then it can always be added to a side.
 968		// This saves some work if the task's inventory is already loaded
 969		// and ensures that the texture item is only added once.
 970		return TRUE;
 971	}
 972
 973	if (!item) return FALSE;
 974	
 975	LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
 976	if (!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID()))
 977	{
 978		// Check that we can add the texture as inventory to the object
 979		if (willObjectAcceptInventory(hit_obj,item) < ACCEPT_YES_COPY_SINGLE )
 980		{
 981			return FALSE;
 982		}
 983		// make sure the object has the texture in it's inventory.
 984		if (SOURCE_AGENT == source)
 985		{
 986			// Remove the texture from local inventory. The server
 987			// will actually remove the item from agent inventory.
 988			gInventory.deleteObject(item->getUUID());
 989			gInventory.notifyObservers();
 990		}
 991		else if (SOURCE_WORLD == source)
 992		{
 993			// *FIX: if the objects are in different regions, and the
 994			// source region has crashed, you can bypass these
 995			// permissions.
 996			LLViewerObject* src_obj = gObjectList.findObject(src_id);
 997			if (src_obj)
 998			{
 999				src_obj->removeInventory(item->getUUID());
1000			}
1001			else
1002			{
1003				llwarns << "Unable to find source object." << llendl;
1004				return FALSE;
1005			}
1006		}
1007		// Add the texture item to the target object's inventory.
1008		hit_obj->updateInventory(new_item, TASK_INVENTORY_ITEM_KEY, true);
1009 		// TODO: Check to see if adding the item was successful; if not, then
1010		// we should return false here.
1011	}
1012	else if (!item->getPermissions().allowOperationBy(PERM_TRANSFER,
1013													 gAgent.getID()))
1014	{
1015		// Check that we can add the texture as inventory to the object
1016		if (willObjectAcceptInventory(hit_obj,item) < ACCEPT_YES_COPY_SINGLE )
1017		{
1018			return FALSE;
1019		}
1020		// *FIX: may want to make sure agent can paint hit_obj.
1021
1022		// Add the texture item to the target object's inventory.
1023		hit_obj->updateInventory(new_item, TASK_INVENTORY_ITEM_KEY, true);
1024		// Force the object to update its refetch its inventory so it has this texture.
1025		hit_obj->fetchInventoryFromServer();
1026 		// TODO: Check to see if adding the item was successful; if not, then
1027		// we should return false here.
1028	}
1029	return TRUE;
1030}
1031
1032void LLToolDragAndDrop::dropTextureAllFaces(LLViewerObject* hit_obj,
1033											LLInventoryItem* item,
1034											LLToolDragAndDrop::ESource source,
1035											const LLUUID& src_id)
1036{
1037	if (!item)
1038	{
1039		llwarns << "LLToolDragAndDrop::dropTextureAllFaces no texture item." << llendl;
1040		return;
1041	}
1042	LLUUID asset_id = item->getAssetUUID();
1043	BOOL success = handleDropTextureProtections(hit_obj, item, source, src_id);
1044	if (!success)
1045	{
1046		return;
1047	}
1048	LLViewerTexture* image = LLViewerTextureManager::getFetchedTexture(asset_id);
1049	LLViewerStats::getInstance()->incStat(LLViewerStats::ST_EDIT_TEXTURE_COUNT );
1050	S32 num_faces = hit_obj->getNumTEs();
1051	for( S32 face = 0; face < num_faces; face++ )
1052	{
1053
1054		// update viewer side image in anticipation of update from simulator
1055		hit_obj->setTEImage(face, image);
1056		dialog_refresh_all();
1057	}
1058	// send the update to the simulator
1059	hit_obj->sendTEUpdate();
1060}
1061
1062void LLToolDragAndDrop::dropMesh(LLViewerObject* hit_obj,
1063								 LLInventoryItem* item,
1064								 LLToolDragAndDrop::ESource source,
1065								 const LLUUID& src_id)
1066{
1067	if (!item)
1068	{
1069		llwarns << "no inventory item." << llendl;
1070		return;
1071	}
1072	LLUUID asset_id = item->getAssetUUID();
1073	BOOL success = handleDropTextureProtections(hit_obj, item, source, src_id);
1074	if(!success)
1075	{
1076		return;
1077	}
1078
1079	LLSculptParams sculpt_params;
1080	sculpt_params.setSculptTexture(asset_id);
1081	sculpt_params.setSculptType(LL_SCULPT_TYPE_MESH);
1082	hit_obj->setParameterEntry(LLNetworkData::PARAMS_SCULPT, sculpt_params, TRUE);
1083	
1084	dialog_refresh_all();
1085}
1086
1087/*
1088void LLToolDragAndDrop::dropTextureOneFaceAvatar(LLVOAvatar* avatar, S32 hit_face, LLInventoryItem* item)
1089{
1090	if (hit_face == -1) return;
1091	LLViewerTexture* image = LLViewerTextureManager::getFetchedTexture(item->getAssetUUID());
1092	
1093	avatar->userSetOptionalTE( hit_face, image);
1094}
1095*/
1096
1097void LLToolDragAndDrop::dropTextureOneFace(LLViewerObject* hit_obj,
1098										   S32 hit_face,
1099										   LLInventoryItem* item,
1100										   LLToolDragAndDrop::ESource source,
1101										   const LLUUID& src_id)
1102{
1103	if (hit_face == -1) return;
1104	if (!item)
1105	{
1106		llwarns << "LLToolDragAndDrop::dropTextureOneFace no texture item." << llendl;
1107		return;
1108	}
1109	LLUUID asset_id = item->getAssetUUID();
1110	BOOL success = handleDropTextureProtections(hit_obj, item, source, src_id);
1111	if (!success)
1112	{
1113		return;
1114	}
1115	// update viewer side image in anticipation of update from simulator
1116	LLViewerTexture* image = LLViewerTextureManager::getFetchedTexture(asset_id);
1117	LLViewerStats::getInstance()->incStat(LLViewerStats::ST_EDIT_TEXTURE_COUNT );
1118	hit_obj->setTEImage(hit_face, image);
1119	dialog_refresh_all();
1120
1121	// send the update to the simulator
1122	hit_obj->sendTEUpdate();
1123}
1124
1125
1126void LLToolDragAndDrop::dropScript(LLViewerObject* hit_obj,
1127								   LLInventoryItem* item,
1128								   BOOL active,
1129								   ESource source,
1130								   const LLUUID& src_id)
1131{
1132	// *HACK: In order to resolve SL-22177, we need to block drags
1133	// from notecards and objects onto other objects.
1134	if ((SOURCE_WORLD == LLToolDragAndDrop::getInstance()->mSource)
1135	   || (SOURCE_NOTECARD == LLToolDragAndDrop::getInstance()->mSource))
1136	{
1137		llwarns << "Call to LLToolDragAndDrop::dropScript() from world"
1138			<< " or notecard." << llendl;
1139		return;
1140	}
1141	if (hit_obj && item)
1142	{
1143		LLPointer<LLViewerInventoryItem> new_script = new LLViewerInventoryItem(item);
1144		if (!item->getPermissions().allowCopyBy(gAgent.getID()))
1145		{
1146			if (SOURCE_AGENT == source)
1147			{
1148				// Remove the script from local inventory. The server
1149				// will actually remove the item from agent inventory.
1150				gInventory.deleteObject(item->getUUID());
1151				gInventory.notifyObservers();
1152			}
1153			else if (SOURCE_WORLD == source)
1154			{
1155				// *FIX: if the objects are in different regions, and
1156				// the source region has crashed, you can bypass
1157				// these permissions.
1158				LLViewerObject* src_obj = gObjectList.findObject(src_id);
1159				if (src_obj)
1160				{
1161					src_obj->removeInventory(item->getUUID());
1162				}
1163				else
1164				{
1165					llwarns << "Unable to find source object." << llendl;
1166					return;
1167				}
1168			}
1169		}
1170		hit_obj->saveScript(new_script, active, true);
1171		gFloaterTools->dirty();
1172
1173		// VEFFECT: SetScript
1174		LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE);
1175		effectp->setSourceObject(gAgentAvatarp);
1176		effectp->setTargetObject(hit_obj);
1177		effectp->setDuration(LL_HUD_DUR_SHORT);
1178		effectp->setColor(LLColor4U(gAgent.getEffectColor()));
1179	}
1180}
1181
1182void LLToolDragAndDrop::dropObject(LLViewerObject* raycast_target,
1183				   BOOL bypass_sim_raycast,
1184				   BOOL from_task_inventory,
1185				   BOOL remove_from_inventory)
1186{
1187	LLViewerRegion* regionp = LLWorld::getInstance()->getRegionFromPosGlobal(mLastHitPos);
1188	if (!regionp)
1189	{
1190		llwarns << "Couldn't find region to rez object" << llendl;
1191		return;
1192	}
1193
1194	//llinfos << "Rezzing object" << llendl;
1195	make_ui_sound("UISndObjectRezIn");
1196	LLViewerInventoryItem* item;
1197	LLViewerInventoryCategory* cat;
1198	locateInventory(item, cat);
1199	if (!item || !item->isFinished()) return;
1200	
1201	//if (regionp
1202	//	&& (regionp->getRegionFlags() & REGION_FLAGS_SANDBOX))
1203	//{
1204	//	LLFirstUse::useSandbox();
1205	//}
1206	// check if it cannot be copied, and mark as remove if it is -
1207	// this will remove the object from inventory after rez. Only
1208	// bother with this check if we would not normally remove from
1209	// inventory.
1210	if (!remove_from_inventory
1211		&& !item->getPermissions().allowCopyBy(gAgent.getID()))
1212	{
1213		remove_from_inventory = TRUE;
1214	}
1215
1216	// Limit raycast to a single object.  
1217	// Speeds up server raycast + avoid problems with server ray
1218	// hitting objects that were clipped by the near plane or culled
1219	// on the viewer.
1220	LLUUID ray_target_id;
1221	if (raycast_target)
1222	{
1223		ray_target_id = raycast_target->getID();
1224	}
1225	else
1226	{
1227		ray_target_id.setNull();
1228	}
1229
1230	// Check if it's in the trash.
1231	bool is_in_trash = false;
1232	const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
1233	if (gInventory.isObjectDescendentOf(item->getUUID(), trash_id))
1234	{
1235		is_in_trash = true;
1236		remove_from_inventory = TRUE;
1237	}
1238
1239	LLUUID source_id = from_task_inventory ? mSourceID : LLUUID::null;
1240
1241	// Select the object only if we're editing.
1242	BOOL rez_selected = LLToolMgr::getInstance()->inEdit();
1243
1244
1245	LLVector3 ray_start = regionp->getPosRegionFromGlobal(mLastCameraPos);
1246	LLVector3 ray_end   = regionp->getPosRegionFromGlobal(mLastHitPos);
1247	// currently the ray's end point is an approximation,
1248	// and is sometimes too short (causing failure.)  so we
1249	// double the ray's length:
1250	if (bypass_sim_raycast == FALSE)
1251	{
1252		LLVector3 ray_direction = ray_start - ray_end;
1253		ray_end = ray_end - ray_direction;
1254	}
1255	
1256	
1257	// Message packing code should be it's own uninterrupted block
1258	LLMessageSystem* msg = gMessageSystem;
1259	if (mSource == SOURCE_NOTECARD)
1260	{
1261		msg->newMessageFast(_PREHASH_RezObjectFromNotecard);
1262	}
1263	else
1264	{
1265		msg->newMessageFast(_PREHASH_RezObject);
1266	}
1267	msg->nextBlockFast(_PREHASH_AgentData);
1268	msg->addUUIDFast(_PREHASH_AgentID,  gAgent.getID());
1269	msg->addUUIDFast(_PREHASH_SessionID,  gAgent.getSessionID());
1270	msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID());
1271
1272	msg->nextBlock("RezData");
1273	// if it's being rezzed from task inventory, we need to enable
1274	// saving it back into the task inventory.
1275	// *FIX: We can probably compress this to a single byte, since I
1276	// think folderid == mSourceID. This will be a later
1277	// optimization.
1278	msg->addUUIDFast(_PREHASH_FromTaskID, source_id);
1279	msg->addU8Fast(_PREHASH_BypassRaycast, (U8) bypass_sim_raycast);
1280	msg->addVector3Fast(_PREHASH_RayStart, ray_start);
1281	msg->addVector3Fast(_PREHASH_RayEnd, ray_end);
1282	msg->addUUIDFast(_PREHASH_RayTargetID, ray_target_id );
1283	msg->addBOOLFast(_PREHASH_RayEndIsIntersection, FALSE);
1284	msg->addBOOLFast(_PREHASH_RezSelected, rez_selected);
1285	msg->addBOOLFast(_PREHASH_RemoveItem, remove_from_inventory);
1286
1287	// deal with permissions slam logic
1288	pack_permissions_slam(msg, item->getFlags(), item->getPermissions());
1289
1290	LLUUID folder_id = item->getParentUUID();
1291	if ((SOURCE_LIBRARY == mSource) || (is_in_trash))
1292	{
1293		// since it's coming from the library or trash, we want to not
1294		// 'take' it back to the same place.
1295		item->setParent(LLUUID::null);
1296		// *TODO this code isn't working - the parent (FolderID) is still
1297		// set when the object is "taken".  so code on the "take" side is
1298		// checking for trash and library as well (llviewermenu.cpp)
1299	}
1300	if (mSource == SOURCE_NOTECARD)
1301	{
1302		msg->nextBlockFast(_PREHASH_NotecardData);
1303		msg->addUUIDFast(_PREHASH_NotecardItemID, mSourceID);
1304		msg->addUUIDFast(_PREHASH_ObjectID, mObjectID);
1305		msg->nextBlockFast(_PREHASH_InventoryData);
1306		msg->addUUIDFast(_PREHASH_ItemID, item->getUUID());
1307	}
1308	else
1309	{
1310		msg->nextBlockFast(_PREHASH_InventoryData);
1311		item->packMessage(msg);
1312	}
1313	msg->sendReliable(regionp->getHost());
1314	// back out the change. no actual internal changes take place.
1315	item->setParent(folder_id); 
1316
1317	// If we're going to select it, get ready for the incoming
1318	// selected object.
1319	if (rez_selected)
1320	{
1321		LLSelectMgr::getInstance()->deselectAll();
1322		gViewerWindow->getWindow()->incBusyCount();
1323	}
1324
1325	if (remove_from_inventory)
1326	{
1327		// Delete it from inventory immediately so that users cannot
1328		// easily bypass copy protection in laggy situations. If the
1329		// rez fails, we will put it back on the server.
1330		gInventory.deleteObject(item->getUUID());
1331		gInventory.notifyObservers();
1332	}
1333
1334	// VEFFECT: DropObject
1335	LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE);
1336	effectp->setSourceObject(gAgentAvatarp);
1337	effectp->setPositionGlobal(mLastHitPos);
1338	effectp->setDuration(LL_HUD_DUR_SHORT);
1339	effectp->setColor(LLColor4U(gAgent.getEffectColor()));
1340
1341	LLViewerStats::getInstance()->incStat(LLViewerStats::ST_REZ_COUNT);
1342}
1343
1344void LLToolDragAndDrop::dropInventory(LLViewerObject* hit_obj,
1345									  LLInventoryItem* item,
1346									  LLToolDragAndDrop::ESource source,
1347									  const LLUUID& src_id)
1348{
1349	// *HACK: In order to resolve SL-22177, we need to block drags
1350	// from notecards and objects onto other objects.
1351	if ((SOURCE_WORLD == LLToolDragAndDrop::getInstance()->mSource)
1352	   || (SOURCE_NOTECARD == LLToolDragAndDrop::getInstance()->mSource))
1353	{
1354		llwarns << "Call to LLToolDragAndDrop::dropInventory() from world"
1355			<< " or notecard." << llendl;
1356		return;
1357	}
1358
1359	LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
1360	time_t creation_date = time_corrected();
1361	new_item->setCreationDate(creation_date);
1362
1363	if (!item->getPermissions().allowCopyBy(gAgent.getID()))
1364	{
1365		if (SOURCE_AGENT == source)
1366		{
1367			// Remove the inventory item from local inventory. The
1368			// server will actually remove the item from agent
1369			// inventory.
1370			gInventory.deleteObject(item->getUUID());
1371			gInventory.notifyObservers();
1372		}
1373		else if (SOURCE_WORLD == source)
1374		{
1375			// *FIX: if the objects are in different regions, and the
1376			// source region has crashed, you can bypass these
1377			// permissions.
1378			LLViewerObject* src_obj = gObjectList.findObject(src_id);
1379			if (src_obj)
1380			{
1381				src_obj->removeInventory(item->getUUID());
1382			}
1383			else
1384			{
1385				llwarns << "Unable to find source object." << llendl;
1386				return;
1387			}
1388		}
1389	}
1390	hit_obj->updateInventory(new_item, TASK_INVENTORY_ITEM_KEY, true);
1391	if (LLFloaterReg::instanceVisible("build"))
1392	{
1393		// *FIX: only show this if panel not expanded?
1394		LLFloaterReg::showInstance("build", "Content");
1395	}
1396
1397	// VEFFECT: AddToInventory
1398	LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE);
1399	effectp->setSourceObject(gAgentAvatarp);
1400	effectp->setTargetObject(hit_obj);
1401	effectp->setDuration(LL_HUD_DUR_SHORT);
1402	effectp->setColor(LLColor4U(gAgent.getEffectColor()));
1403	gFloaterTools->dirty();
1404}
1405
1406// accessor that looks at permissions, copyability, and names of
1407// inventory items to determine if a drop would be ok.
1408EAcceptance LLToolDragAndDrop::willObjectAcceptInventory(LLViewerObject* obj, LLInventoryItem* item)
1409{
1410	// check the basics
1411	if (!item || !obj) return ACCEPT_NO;
1412	// HACK: downcast
1413	LLViewerInventoryItem* vitem = (LLViewerInventoryItem*)item;
1414	if (!vitem->isFinished()) return ACCEPT_NO;
1415	if (vitem->getIsLinkType()) return ACCEPT_NO; // No giving away links
1416
1417	// deny attempts to drop from an object onto itself. This is to
1418	// help make sure that drops that are from an object to an object
1419	// don't have to worry about order of evaluation. Think of this
1420	// like check for self in assignment.
1421	if(obj->getID() == item->getParentUUID())
1422	{
1423		return ACCEPT_NO;
1424	}
1425	
1426	//BOOL copy = (perm.allowCopyBy(gAgent.getID(),
1427	//							  gAgent.getGroupID())
1428	//			 && (obj->mPermModify || obj->mFlagAllowInventoryAdd));
1429	BOOL worn = FALSE;
1430	LLVOAvatarSelf* my_avatar = NULL;
1431	switch(item->getType())
1432	{
1433	case LLAssetType::AT_OBJECT:
1434		my_avatar = gAgentAvatarp;
1435		if(my_avatar && my_avatar->isWearingAttachment(item->getUUID()))
1436		{
1437				worn = TRUE;
1438		}
1439		break;
1440	case LLAssetType::AT_BODYPART:
1441	case LLAssetType::AT_CLOTHING:
1442		if(gAgentWearables.isWearingItem(item->getUUID()))
1443		{
1444			worn = TRUE;
1445		}
1446		break;
1447	case LLAssetType::AT_CALLINGCARD:
1448		// Calling Cards in object are disabled for now
1449		// because of incomplete LSL support. See STORM-1117.
1450		return ACCEPT_NO;
1451	default:
1452			break;
1453	}
1454	const LLPermissions& perm = item->getPermissions();
1455	BOOL modify = (obj->permModify() || obj->flagAllowInventoryAdd());
1456	BOOL transfer = FALSE;
1457	if((obj->permYouOwner() && (perm.getOwner() == gAgent.getID()))
1458	   || perm.allowOperationBy(PERM_TRANSFER, gAgent.getID()))
1459	{
1460		transfer = TRUE;
1461	}
1462	BOOL volume = (LL_PCODE_VOLUME == obj->getPCode());
1463	BOOL attached = obj->isAttachment();
1464	BOOL unrestricted = ((perm.getMaskBase() & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) ? TRUE : FALSE;
1465	if(attached && !unrestricted)
1466	{
1467		return ACCEPT_NO_LOCKED;
1468	}
1469	else if(modify && transfer && volume && !worn)
1470	{
1471		return ACCEPT_YES_MULTI;
1472	}
1473	else if(!modify)
1474	{
1475		return ACCEPT_NO_LOCKED;
1476	}
1477	return ACCEPT_NO;
1478}
1479
1480
1481static void give_inventory_cb(const LLSD& notification, const LLSD& response)
1482{
1483	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
1484	// if Cancel pressed
1485	if (option == 1)
1486	{
1487		return;
1488	}
1489
1490	LLSD payload = notification["payload"];
1491	const LLUUID& session_id = payload["session_id"];
1492	const LLUUID& agent_id = payload["agent_id"];
1493	LLViewerInventoryItem * inv_item =  gInventory.getItem(payload["item_id"]);
1494	if (NULL == inv_item)
1495	{
1496		llassert(NULL != inv_item);
1497		return;
1498	}
1499
1500	if (LLGiveInventory::doGiveInventoryItem(agent_id, inv_item, session_id))
1501	{
1502		if ("avatarpicker" == payload["d&d_dest"].asString())
1503		{
1504			LLFloaterReg::hideInstance("avatar_picker");
1505		}
1506		LLNotificationsUtil::add("ItemsShared");
1507	}
1508}
1509
1510static void show_item_sharing_confirmation(const std::string name,
1511					   LLViewerInventoryItem* inv_item,
1512					   const LLSD& dest,
1513					   const LLUUID& dest_agent,
1514					   const LLUUID& session_id = LLUUID::null)
1515{
1516	if (!inv_item)
1517	{
1518		llassert(NULL != inv_item);
1519		return;
1520	}
1521
1522	LLSD substitutions;
1523	substitutions["RESIDENTS"] = name;
1524	substitutions["ITEMS"] = inv_item->getName();
1525	LLSD payload;
1526	payload["agent_id"] = dest_agent;
1527	payload["item_id"] = inv_item->getUUID();
1528	payload["session_id"] = session_id;
1529	payload["d&d_dest"] = dest.asString();
1530	LLNotificationsUtil::add("ShareItemsConfirmation", substitutions, payload, &give_inventory_cb);
1531}
1532
1533static void get_name_cb(const LLUUID& id,
1534						const std::string& full_name,
1535						LLViewerInventoryItem* inv_item,
1536						const LLSD& dest,
1537						const LLUUID& dest_agent)
1538{
1539	show_item_sharing_confirmation(full_name,
1540								   inv_item,
1541								   dest,
1542								   id,
1543								   LLUUID::null);
1544}
1545
1546// function used as drag-and-drop handler for simple agent give inventory requests
1547//static
1548bool LLToolDragAndDrop::handleGiveDragAndDrop(LLUUID dest_agent, LLUUID session_id, BOOL drop,
1549											  EDragAndDropType cargo_type,
1550											  void* cargo_data,
1551											  EAcceptance* accept,
1552											  const LLSD& dest)
1553{
1554	// check the type
1555	switch(cargo_type)
1556	{
1557	case DAD_TEXTURE:
1558	case DAD_SOUND:
1559	case DAD_LANDMARK:
1560	case DAD_SCRIPT:
1561	case DAD_OBJECT:
1562	case DAD_NOTECARD:
1563	case DAD_CLOTHING:
1564	case DAD_BODYPART:
1565	case DAD_ANIMATION:
1566	case DAD_GESTURE:
1567	case DAD_CALLINGCARD:
1568	case DAD_MESH:
1569	{
1570		LLViewerInventoryItem* inv_item = (LLViewerInventoryItem*)cargo_data;
1571		if(gInventory.getItem(inv_item->getUUID())
1572			&& LLGiveInventory::isInventoryGiveAcceptable(inv_item))
1573		{
1574			// *TODO: get multiple object transfers working
1575			*accept = ACCEPT_YES_COPY_SINGLE;
1576			if(drop)
1577			{
1578				LLIMModel::LLIMSession * session = LLIMModel::instance().findIMSession(session_id);
1579
1580				// If no IM session found get the destination agent's name by id.
1581				if (NULL == session)
1582				{
1583					std::string fullname;
1584
1585					// If destination agent's name is found in cash proceed to showing the confirmation dialog.
1586					// Otherwise set up a callback to show the dialog when the name arrives.
1587					if (gCacheName->getFullName(dest_agent, fullname))
1588					{
1589						show_item_sharing_confirmation(fullname, inv_item, dest, dest_agent, LLUUID::null);
1590					}
1591					else
1592					{
1593						gCacheName->get(dest_agent, false, boost::bind(&get_name_cb, _1, _2, inv_item, dest, dest_agent));
1594					}
1595
1596					return true;
1597				}
1598
1599				// If an IM session with destination agent is found item offer will be logged in this session.
1600				show_item_sharing_confirmation(session->mName, inv_item, dest, dest_agent, session_id);
1601			}
1602		}
1603		else
1604		{
1605			// It's not in the user's inventory (it's probably
1606			// in an object's contents), so disallow dragging
1607			// it here.  You can't give something you don't
1608			// yet have.
1609			*accept = ACCEPT_NO;
1610		}
1611		break;
1612	}
1613	case DAD_CATEGORY:
1614	{
1615		LLViewerInventoryCategory* inv_cat = (LLViewerInventoryCategory*)cargo_data;
1616		if( gInventory.getCategory( inv_cat->getUUID() ) )
1617		{
1618			// *TODO: get multiple object transfers working
1619			*accept = ACCEPT_YES_COPY_SINGLE;
1620			if(drop)
1621			{
1622				LLGiveInventory::doGiveInventoryCategory(dest_agent, inv_cat, session_id);
1623			}
1624		}
1625		else
1626		{
1627			// It's not in the user's inventory (it's probably
1628			// in an object's contents), so disallow dragging
1629			// it here.  You can't give something you don't
1630			// yet have.
1631			*accept = ACCEPT_NO;
1632		}
1633		break;
1634	}
1635	default:
1636		*accept = ACCEPT_NO;
1637		break;
1638	}
1639
1640	return TRUE;
1641}
1642
1643
1644
1645///
1646/// Methods called in the drag & drop array
1647///
1648
1649EAcceptance LLToolDragAndDrop::dad3dNULL(
1650	LLViewerObject*, S32, MASK, BOOL)
1651{
1652	lldebugs << "LLToolDragAndDrop::dad3dNULL()" << llendl;
1653	return ACCEPT_NO;
1654}
1655
1656EAcceptance LLToolDragAndDrop::dad3dRezAttachmentFromInv(
1657	LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
1658{
1659	lldebugs << "LLToolDragAndDrop::dad3dRezAttachmentFromInv()" << llendl;
1660	// must be in the user's inventory
1661	if(mSource != SOURCE_AGENT && mSource != SOURCE_LIBRARY)
1662	{
1663		return ACCEPT_NO;
1664	}
1665
1666	LLViewerInventoryItem* item;
1667	LLViewerInventoryCategory* cat;
1668	locateInventory(item, cat);
1669	if (!item || !item->isFinished()) return ACCEPT_NO;
1670
1671	// must not be in the trash
1672	const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
1673	if( gInventory.isObjectDescendentOf( item->getUUID(), trash_id ) )
1674	{
1675		return ACCEPT_NO;
1676	}
1677
1678	// must not be already wearing it
1679	LLVOAvatarSelf* avatar = gAgentAvatarp;
1680	if( !avatar || avatar->isWearingAttachment(item->getUUID()) )
1681	{
1682		return ACCEPT_NO;
1683	}
1684
1685	const LLUUID &outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false);
1686	if(gInventory.isObjectDescendentOf(item->getUUID(), outbox_id))
1687	{
1688		return ACCEPT_NO;
1689	}
1690
1691
1692	if( drop )
1693	{
1694		if(mSource == SOURCE_LIBRARY)
1695		{
1696			LLPointer<LLInventoryCallback> cb = new RezAttachmentCallback(0);
1697			copy_inventory_item(
1698				gAgent.getID(),
1699				item->getPermissions().getOwner(),
1700				item->getUUID(),
1701				LLUUID::null,
1702				std::string(),
1703				cb);
1704		}
1705		else
1706		{
1707			rez_attachment(item, 0);
1708		}
1709	}
1710	return ACCEPT_YES_SINGLE;
1711}
1712
1713
1714EAcceptance LLToolDragAndDrop::dad3dRezObjectOnLand(
1715	LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
1716{
1717	if (mSource == SOURCE_WORLD)
1718	{
1719		return dad3dRezFromObjectOnLand(obj, face, mask, drop);
1720	}
1721
1722	lldebugs << "LLToolDragAndDrop::dad3dRezObjectOnLan…

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