3200 lines
106 KiB
C++
3200 lines
106 KiB
C++
/**
|
|
* @file lltooldraganddrop.cpp
|
|
* @brief LLToolDragAndDrop class implementation
|
|
*
|
|
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2010, Linden Research, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License only.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#include "llviewerprecompiledheaders.h"
|
|
#include "lltooldraganddrop.h"
|
|
|
|
// library headers
|
|
#include "llnotificationsutil.h"
|
|
// project headers
|
|
#include "llagent.h"
|
|
#include "llagentcamera.h"
|
|
#include "llagentwearables.h"
|
|
#include "llappearancemgr.h"
|
|
#include "llavatarnamecache.h"
|
|
#include "lldictionary.h"
|
|
#include "llfloaterreg.h"
|
|
#include "llfloatertools.h"
|
|
#include "llgesturemgr.h"
|
|
#include "llgiveinventory.h"
|
|
#include "llgltfmateriallist.h"
|
|
#include "llhudmanager.h"
|
|
#include "llhudeffecttrail.h"
|
|
#include "llimview.h"
|
|
#include "llinventorybridge.h"
|
|
#include "llinventorydefines.h"
|
|
#include "llinventoryfunctions.h"
|
|
#include "llinventorymodelbackgroundfetch.h"
|
|
#include "llpreviewnotecard.h"
|
|
#include "llrootview.h"
|
|
#include "llselectmgr.h"
|
|
#include "lltoolbarview.h"
|
|
#include "lltoolmgr.h"
|
|
#include "lltooltip.h"
|
|
#include "lltrans.h"
|
|
#include "llviewerobjectlist.h"
|
|
#include "llviewerregion.h"
|
|
#include "llviewerstats.h"
|
|
#include "llviewerwindow.h"
|
|
#include "llvoavatarself.h"
|
|
#include "llworld.h"
|
|
#include "llpanelface.h"
|
|
#include "lluiusage.h"
|
|
|
|
// syntactic sugar
|
|
#define callMemberFunction(object,ptrToMember) ((object).*(ptrToMember))
|
|
|
|
class LLNoPreferredType : public LLInventoryCollectFunctor
|
|
{
|
|
public:
|
|
LLNoPreferredType() {}
|
|
virtual ~LLNoPreferredType() {}
|
|
virtual bool operator()(LLInventoryCategory* cat,
|
|
LLInventoryItem* item)
|
|
{
|
|
if (cat && (cat->getPreferredType() == LLFolderType::FT_NONE))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
class LLNoPreferredTypeOrItem : public LLInventoryCollectFunctor
|
|
{
|
|
public:
|
|
LLNoPreferredTypeOrItem() {}
|
|
virtual ~LLNoPreferredTypeOrItem() {}
|
|
virtual bool operator()(LLInventoryCategory* cat,
|
|
LLInventoryItem* item)
|
|
{
|
|
if (item) return true;
|
|
if (cat && (cat->getPreferredType() == LLFolderType::FT_NONE))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
class LLDroppableItem : public LLInventoryCollectFunctor
|
|
{
|
|
public:
|
|
LLDroppableItem(bool is_transfer) :
|
|
mCountLosing(0), mIsTransfer(is_transfer) {}
|
|
virtual ~LLDroppableItem() {}
|
|
virtual bool operator()(LLInventoryCategory* cat,
|
|
LLInventoryItem* item);
|
|
S32 countNoCopy() const { return mCountLosing; }
|
|
|
|
protected:
|
|
S32 mCountLosing;
|
|
bool mIsTransfer;
|
|
};
|
|
|
|
bool LLDroppableItem::operator()(LLInventoryCategory* cat,
|
|
LLInventoryItem* item)
|
|
{
|
|
bool allowed = false;
|
|
if (item)
|
|
{
|
|
allowed = itemTransferCommonlyAllowed(item);
|
|
|
|
if (allowed
|
|
&& mIsTransfer
|
|
&& !item->getPermissions().allowOperationBy(PERM_TRANSFER,
|
|
gAgent.getID()))
|
|
{
|
|
allowed = false;
|
|
}
|
|
if (allowed && !item->getPermissions().allowCopyBy(gAgent.getID()))
|
|
{
|
|
++mCountLosing;
|
|
}
|
|
}
|
|
return allowed;
|
|
}
|
|
|
|
class LLDropCopyableItems : public LLInventoryCollectFunctor
|
|
{
|
|
public:
|
|
LLDropCopyableItems() {}
|
|
virtual ~LLDropCopyableItems() {}
|
|
virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
|
|
};
|
|
|
|
|
|
bool LLDropCopyableItems::operator()(
|
|
LLInventoryCategory* cat,
|
|
LLInventoryItem* item)
|
|
{
|
|
bool allowed = false;
|
|
if (item)
|
|
{
|
|
allowed = itemTransferCommonlyAllowed(item);
|
|
if (allowed &&
|
|
!item->getPermissions().allowCopyBy(gAgent.getID()))
|
|
{
|
|
// whoops, can't copy it - don't allow it.
|
|
allowed = false;
|
|
}
|
|
}
|
|
return allowed;
|
|
}
|
|
|
|
// Starts a fetch on folders and items. This is really not used
|
|
// as an observer in the traditional sense; we're just using it to
|
|
// request a fetch and we don't care about when/if the response arrives.
|
|
class LLCategoryFireAndForget : public LLInventoryFetchComboObserver
|
|
{
|
|
public:
|
|
LLCategoryFireAndForget(const uuid_vec_t& folder_ids,
|
|
const uuid_vec_t& item_ids) :
|
|
LLInventoryFetchComboObserver(folder_ids, item_ids)
|
|
{}
|
|
~LLCategoryFireAndForget() {}
|
|
virtual void done()
|
|
{
|
|
/* no-op: it's fire n forget right? */
|
|
LL_DEBUGS() << "LLCategoryFireAndForget::done()" << LL_ENDL;
|
|
}
|
|
};
|
|
|
|
class LLCategoryDropObserver : public LLInventoryFetchItemsObserver
|
|
{
|
|
public:
|
|
LLCategoryDropObserver(
|
|
const uuid_vec_t& ids,
|
|
const LLUUID& obj_id, LLToolDragAndDrop::ESource src) :
|
|
LLInventoryFetchItemsObserver(ids),
|
|
mObjectID(obj_id),
|
|
mSource(src)
|
|
{}
|
|
~LLCategoryDropObserver() {}
|
|
virtual void done();
|
|
|
|
protected:
|
|
LLUUID mObjectID;
|
|
LLToolDragAndDrop::ESource mSource;
|
|
};
|
|
|
|
void LLCategoryDropObserver::done()
|
|
{
|
|
gInventory.removeObserver(this);
|
|
LLViewerObject* dst_obj = gObjectList.findObject(mObjectID);
|
|
if (dst_obj)
|
|
{
|
|
// *FIX: coalesce these...
|
|
LLInventoryItem* item = NULL;
|
|
uuid_vec_t::iterator it = mComplete.begin();
|
|
uuid_vec_t::iterator end = mComplete.end();
|
|
for(; it < end; ++it)
|
|
{
|
|
item = gInventory.getItem(*it);
|
|
if (item)
|
|
{
|
|
LLToolDragAndDrop::dropInventory(
|
|
dst_obj,
|
|
item,
|
|
mSource,
|
|
LLUUID::null);
|
|
}
|
|
}
|
|
}
|
|
delete this;
|
|
}
|
|
|
|
S32 LLToolDragAndDrop::sOperationId = 0;
|
|
|
|
LLToolDragAndDrop::DragAndDropEntry::DragAndDropEntry(dragOrDrop3dImpl f_none,
|
|
dragOrDrop3dImpl f_self,
|
|
dragOrDrop3dImpl f_avatar,
|
|
dragOrDrop3dImpl f_object,
|
|
dragOrDrop3dImpl f_land) :
|
|
LLDictionaryEntry("")
|
|
{
|
|
mFunctions[DT_NONE] = f_none;
|
|
mFunctions[DT_SELF] = f_self;
|
|
mFunctions[DT_AVATAR] = f_avatar;
|
|
mFunctions[DT_OBJECT] = f_object;
|
|
mFunctions[DT_LAND] = f_land;
|
|
}
|
|
|
|
LLToolDragAndDrop::dragOrDrop3dImpl LLToolDragAndDrop::LLDragAndDropDictionary::get(EDragAndDropType dad_type, LLToolDragAndDrop::EDropTarget drop_target)
|
|
{
|
|
const DragAndDropEntry *entry = lookup(dad_type);
|
|
if (entry)
|
|
{
|
|
return (entry->mFunctions[(U8)drop_target]);
|
|
}
|
|
return &LLToolDragAndDrop::dad3dNULL;
|
|
}
|
|
|
|
LLToolDragAndDrop::LLDragAndDropDictionary::LLDragAndDropDictionary()
|
|
{
|
|
// DT_NONE DT_SELF DT_AVATAR DT_OBJECT DT_LAND
|
|
// |-------------------------------|----------------------------------------------|-----------------------------------------------|---------------------------------------------------|--------------------------------|
|
|
addEntry(DAD_NONE, new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL));
|
|
addEntry(DAD_TEXTURE, new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dGiveInventory, &LLToolDragAndDrop::dad3dTextureObject, &LLToolDragAndDrop::dad3dNULL));
|
|
addEntry(DAD_MATERIAL, new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dGiveInventory, &LLToolDragAndDrop::dad3dMaterialObject, &LLToolDragAndDrop::dad3dNULL));
|
|
addEntry(DAD_SOUND, new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dGiveInventory, &LLToolDragAndDrop::dad3dUpdateInventory, &LLToolDragAndDrop::dad3dNULL));
|
|
addEntry(DAD_CALLINGCARD, new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dGiveInventory, &LLToolDragAndDrop::dad3dUpdateInventory, &LLToolDragAndDrop::dad3dNULL));
|
|
addEntry(DAD_LANDMARK, new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dGiveInventory, &LLToolDragAndDrop::dad3dUpdateInventory, &LLToolDragAndDrop::dad3dNULL));
|
|
addEntry(DAD_SCRIPT, new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dGiveInventory, &LLToolDragAndDrop::dad3dRezScript, &LLToolDragAndDrop::dad3dNULL));
|
|
addEntry(DAD_CLOTHING, new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dWearItem, &LLToolDragAndDrop::dad3dGiveInventory, &LLToolDragAndDrop::dad3dUpdateInventory, &LLToolDragAndDrop::dad3dNULL));
|
|
addEntry(DAD_OBJECT, new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dRezAttachmentFromInv, &LLToolDragAndDrop::dad3dGiveInventoryObject, &LLToolDragAndDrop::dad3dRezObjectOnObject, &LLToolDragAndDrop::dad3dRezObjectOnLand));
|
|
addEntry(DAD_NOTECARD, new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dGiveInventory, &LLToolDragAndDrop::dad3dUpdateInventory, &LLToolDragAndDrop::dad3dNULL));
|
|
addEntry(DAD_CATEGORY, new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dWearCategory, &LLToolDragAndDrop::dad3dGiveInventoryCategory, &LLToolDragAndDrop::dad3dRezCategoryOnObject, &LLToolDragAndDrop::dad3dNULL));
|
|
addEntry(DAD_ROOT_CATEGORY, new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL));
|
|
addEntry(DAD_BODYPART, new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dWearItem, &LLToolDragAndDrop::dad3dGiveInventory, &LLToolDragAndDrop::dad3dUpdateInventory, &LLToolDragAndDrop::dad3dNULL));
|
|
addEntry(DAD_ANIMATION, new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dGiveInventory, &LLToolDragAndDrop::dad3dUpdateInventory, &LLToolDragAndDrop::dad3dNULL));
|
|
addEntry(DAD_GESTURE, new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dActivateGesture, &LLToolDragAndDrop::dad3dGiveInventory, &LLToolDragAndDrop::dad3dUpdateInventory, &LLToolDragAndDrop::dad3dNULL));
|
|
addEntry(DAD_LINK, new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL));
|
|
addEntry(DAD_MESH, new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dGiveInventory, &LLToolDragAndDrop::dad3dMeshObject, &LLToolDragAndDrop::dad3dNULL));
|
|
addEntry(DAD_SETTINGS, new DragAndDropEntry(&LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dNULL, &LLToolDragAndDrop::dad3dGiveInventory, &LLToolDragAndDrop::dad3dUpdateInventory, &LLToolDragAndDrop::dad3dNULL));
|
|
|
|
// TODO: animation on self could play it? edit it?
|
|
// TODO: gesture on self could play it? edit it?
|
|
};
|
|
|
|
LLToolDragAndDrop::LLToolDragAndDrop()
|
|
: LLTool(std::string("draganddrop"), NULL),
|
|
mCargoCount(0),
|
|
mDragStartX(0),
|
|
mDragStartY(0),
|
|
mSource(SOURCE_AGENT),
|
|
mCursor(UI_CURSOR_NO),
|
|
mLastAccept(ACCEPT_NO),
|
|
mDrop(false),
|
|
mCurItemIndex(0)
|
|
{
|
|
|
|
}
|
|
|
|
void LLToolDragAndDrop::setDragStart(S32 x, S32 y)
|
|
{
|
|
mDragStartX = x;
|
|
mDragStartY = y;
|
|
}
|
|
|
|
bool LLToolDragAndDrop::isOverThreshold(S32 x,S32 y)
|
|
{
|
|
S32 mouse_delta_x = x - mDragStartX;
|
|
S32 mouse_delta_y = y - mDragStartY;
|
|
|
|
return (mouse_delta_x * mouse_delta_x) + (mouse_delta_y * mouse_delta_y) > DRAG_N_DROP_DISTANCE_THRESHOLD * DRAG_N_DROP_DISTANCE_THRESHOLD;
|
|
}
|
|
|
|
void LLToolDragAndDrop::beginDrag(EDragAndDropType type,
|
|
const LLUUID& cargo_id,
|
|
ESource source,
|
|
const LLUUID& source_id,
|
|
const LLUUID& object_id)
|
|
{
|
|
if (type == DAD_NONE)
|
|
{
|
|
LL_WARNS() << "Attempted to start drag without a cargo type" << LL_ENDL;
|
|
return;
|
|
}
|
|
|
|
if (type != DAD_CATEGORY)
|
|
{
|
|
LLViewerInventoryItem* item = gInventory.getItem(cargo_id);
|
|
if (item && !item->isFinished())
|
|
{
|
|
LLInventoryModelBackgroundFetch::instance().start(item->getUUID(), false);
|
|
}
|
|
}
|
|
|
|
mCargoTypes.clear();
|
|
mCargoTypes.push_back(type);
|
|
mCargoIDs.clear();
|
|
mCargoIDs.push_back(cargo_id);
|
|
mSource = source;
|
|
mSourceID = source_id;
|
|
mObjectID = object_id;
|
|
|
|
setMouseCapture( true );
|
|
LLToolMgr::getInstance()->setTransientTool( this );
|
|
mCursor = UI_CURSOR_NO;
|
|
if ((mCargoTypes[0] == DAD_CATEGORY)
|
|
&& ((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY)))
|
|
{
|
|
LLInventoryCategory* cat = gInventory.getCategory(cargo_id);
|
|
// go ahead and fire & forget the descendents if we are not
|
|
// dragging a protected folder.
|
|
if (cat)
|
|
{
|
|
LLViewerInventoryCategory::cat_array_t cats;
|
|
LLViewerInventoryItem::item_array_t items;
|
|
LLNoPreferredTypeOrItem is_not_preferred;
|
|
uuid_vec_t folder_ids;
|
|
uuid_vec_t item_ids;
|
|
if (is_not_preferred(cat, NULL))
|
|
{
|
|
folder_ids.push_back(cargo_id);
|
|
}
|
|
gInventory.collectDescendentsIf(
|
|
cargo_id,
|
|
cats,
|
|
items,
|
|
LLInventoryModel::EXCLUDE_TRASH,
|
|
is_not_preferred);
|
|
for (auto& cat : cats)
|
|
{
|
|
folder_ids.emplace_back(cat->getUUID());
|
|
}
|
|
for (auto& item : items)
|
|
{
|
|
item_ids.emplace_back(item->getUUID());
|
|
}
|
|
if (!folder_ids.empty() || !item_ids.empty())
|
|
{
|
|
LLCategoryFireAndForget *fetcher = new LLCategoryFireAndForget(folder_ids, item_ids);
|
|
fetcher->startFetch();
|
|
delete fetcher;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLToolDragAndDrop::beginMultiDrag(
|
|
const std::vector<EDragAndDropType> types,
|
|
const uuid_vec_t& cargo_ids,
|
|
ESource source,
|
|
const LLUUID& source_id)
|
|
{
|
|
// assert on public api is evil
|
|
//llassert( type != DAD_NONE );
|
|
|
|
std::vector<EDragAndDropType>::const_iterator types_it;
|
|
for (types_it = types.begin(); types_it != types.end(); ++types_it)
|
|
{
|
|
if (DAD_NONE == *types_it)
|
|
{
|
|
LL_WARNS() << "Attempted to start drag without a cargo type" << LL_ENDL;
|
|
return;
|
|
}
|
|
}
|
|
mCargoTypes = types;
|
|
mCargoIDs = cargo_ids;
|
|
mSource = source;
|
|
mSourceID = source_id;
|
|
|
|
setMouseCapture( true );
|
|
LLToolMgr::getInstance()->setTransientTool( this );
|
|
mCursor = UI_CURSOR_NO;
|
|
if ((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY))
|
|
{
|
|
// find categories (i.e. inventory folders) in the cargo.
|
|
LLInventoryCategory* cat = NULL;
|
|
auto count = llmin(cargo_ids.size(), types.size());
|
|
uuid_set_t cat_ids;
|
|
for (size_t i = 0; i < count; ++i)
|
|
{
|
|
cat = gInventory.getCategory(cargo_ids[i]);
|
|
if (cat)
|
|
{
|
|
LLViewerInventoryCategory::cat_array_t cats;
|
|
LLViewerInventoryItem::item_array_t items;
|
|
LLNoPreferredType is_not_preferred;
|
|
if (is_not_preferred(cat, NULL))
|
|
{
|
|
cat_ids.insert(cat->getUUID());
|
|
}
|
|
gInventory.collectDescendentsIf(
|
|
cat->getUUID(),
|
|
cats,
|
|
items,
|
|
LLInventoryModel::EXCLUDE_TRASH,
|
|
is_not_preferred);
|
|
for (auto& cat : cats)
|
|
{
|
|
cat_ids.emplace(cat->getUUID());
|
|
}
|
|
}
|
|
}
|
|
if (!cat_ids.empty())
|
|
{
|
|
uuid_vec_t folder_ids;
|
|
uuid_vec_t item_ids;
|
|
std::back_insert_iterator<uuid_vec_t> copier(folder_ids);
|
|
std::copy(cat_ids.begin(), cat_ids.end(), copier);
|
|
LLCategoryFireAndForget fetcher(folder_ids, item_ids);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLToolDragAndDrop::endDrag()
|
|
{
|
|
mEndDragSignal();
|
|
LLSelectMgr::getInstance()->unhighlightAll();
|
|
setMouseCapture(false);
|
|
}
|
|
|
|
void LLToolDragAndDrop::onMouseCaptureLost()
|
|
{
|
|
// Called whenever the drag ends or if mouse capture is simply lost
|
|
LLToolMgr::getInstance()->clearTransientTool();
|
|
mCargoTypes.clear();
|
|
mCargoIDs.clear();
|
|
mSource = SOURCE_AGENT;
|
|
mSourceID.setNull();
|
|
mObjectID.setNull();
|
|
mCustomMsg.clear();
|
|
}
|
|
|
|
bool LLToolDragAndDrop::handleMouseUp( S32 x, S32 y, MASK mask )
|
|
{
|
|
if (hasMouseCapture())
|
|
{
|
|
EAcceptance acceptance = ACCEPT_NO;
|
|
dragOrDrop( x, y, mask, true, &acceptance );
|
|
endDrag();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
ECursorType LLToolDragAndDrop::acceptanceToCursor( EAcceptance acceptance )
|
|
{
|
|
switch (acceptance)
|
|
{
|
|
case ACCEPT_YES_MULTI:
|
|
if (mCargoIDs.size() > 1)
|
|
{
|
|
mCursor = UI_CURSOR_ARROWDRAGMULTI;
|
|
}
|
|
else
|
|
{
|
|
mCursor = UI_CURSOR_ARROWDRAG;
|
|
}
|
|
break;
|
|
case ACCEPT_YES_SINGLE:
|
|
if (mCargoIDs.size() > 1)
|
|
{
|
|
mToolTipMsg = LLTrans::getString("TooltipMustSingleDrop");
|
|
mCursor = UI_CURSOR_NO;
|
|
}
|
|
else
|
|
{
|
|
mCursor = UI_CURSOR_ARROWDRAG;
|
|
}
|
|
break;
|
|
|
|
case ACCEPT_NO_LOCKED:
|
|
mCursor = UI_CURSOR_NOLOCKED;
|
|
break;
|
|
|
|
case ACCEPT_NO_CUSTOM:
|
|
mToolTipMsg = mCustomMsg;
|
|
mCursor = UI_CURSOR_NO;
|
|
break;
|
|
|
|
|
|
case ACCEPT_NO:
|
|
mCursor = UI_CURSOR_NO;
|
|
break;
|
|
|
|
case ACCEPT_YES_COPY_MULTI:
|
|
if (mCargoIDs.size() > 1)
|
|
{
|
|
mCursor = UI_CURSOR_ARROWCOPYMULTI;
|
|
}
|
|
else
|
|
{
|
|
mCursor = UI_CURSOR_ARROWCOPY;
|
|
}
|
|
break;
|
|
case ACCEPT_YES_COPY_SINGLE:
|
|
if (mCargoIDs.size() > 1)
|
|
{
|
|
mToolTipMsg = LLTrans::getString("TooltipMustSingleDrop");
|
|
mCursor = UI_CURSOR_NO;
|
|
}
|
|
else
|
|
{
|
|
mCursor = UI_CURSOR_ARROWCOPY;
|
|
}
|
|
break;
|
|
case ACCEPT_POSTPONED:
|
|
break;
|
|
default:
|
|
llassert( false );
|
|
}
|
|
|
|
return mCursor;
|
|
}
|
|
|
|
bool LLToolDragAndDrop::handleHover( S32 x, S32 y, MASK mask )
|
|
{
|
|
EAcceptance acceptance = ACCEPT_NO;
|
|
dragOrDrop( x, y, mask, false, &acceptance );
|
|
|
|
ECursorType cursor = acceptanceToCursor(acceptance);
|
|
gViewerWindow->getWindow()->setCursor( cursor );
|
|
|
|
LL_DEBUGS("UserInput") << "hover handled by LLToolDragAndDrop" << LL_ENDL;
|
|
return true;
|
|
}
|
|
|
|
bool LLToolDragAndDrop::handleKey(KEY key, MASK mask)
|
|
{
|
|
if (key == KEY_ESCAPE)
|
|
{
|
|
// cancel drag and drop operation
|
|
endDrag();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool LLToolDragAndDrop::handleToolTip(S32 x, S32 y, MASK mask)
|
|
{
|
|
const F32 DRAG_N_DROP_TOOLTIP_DELAY = 0.1f;
|
|
if (!mToolTipMsg.empty())
|
|
{
|
|
LLToolTipMgr::instance().unblockToolTips();
|
|
LLToolTipMgr::instance().show(LLToolTip::Params()
|
|
.message(mToolTipMsg)
|
|
.delay_time(DRAG_N_DROP_TOOLTIP_DELAY));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void LLToolDragAndDrop::handleDeselect()
|
|
{
|
|
mToolTipMsg.clear();
|
|
mCustomMsg.clear();
|
|
|
|
LLToolTipMgr::instance().blockToolTips();
|
|
}
|
|
|
|
// protected
|
|
void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, bool drop,
|
|
EAcceptance* acceptance)
|
|
{
|
|
*acceptance = ACCEPT_YES_MULTI;
|
|
|
|
bool handled = false;
|
|
|
|
LLView* top_view = gFocusMgr.getTopCtrl();
|
|
LLViewerInventoryItem* item;
|
|
LLViewerInventoryCategory* cat;
|
|
|
|
mToolTipMsg.clear();
|
|
|
|
// Increment the operation id for every drop
|
|
if (drop)
|
|
{
|
|
sOperationId++;
|
|
}
|
|
|
|
// For people drag and drop we don't need an actual inventory object,
|
|
// instead we need the current cargo id, which should be a person id.
|
|
bool is_uuid_dragged = (mSource == SOURCE_PEOPLE);
|
|
|
|
if (top_view)
|
|
{
|
|
handled = true;
|
|
|
|
for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++)
|
|
{
|
|
S32 local_x, local_y;
|
|
top_view->screenPointToLocal( x, y, &local_x, &local_y );
|
|
EAcceptance item_acceptance = ACCEPT_NO;
|
|
|
|
LLInventoryObject* cargo = locateInventory(item, cat);
|
|
if (cargo)
|
|
{
|
|
handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, false,
|
|
mCargoTypes[mCurItemIndex],
|
|
(void*)cargo,
|
|
&item_acceptance,
|
|
mToolTipMsg);
|
|
}
|
|
else if (is_uuid_dragged)
|
|
{
|
|
handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, false,
|
|
mCargoTypes[mCurItemIndex],
|
|
(void*)&mCargoIDs[mCurItemIndex],
|
|
&item_acceptance,
|
|
mToolTipMsg);
|
|
}
|
|
if (handled)
|
|
{
|
|
// use sort order to determine priority of acceptance
|
|
*acceptance = (EAcceptance)llmin((U32)item_acceptance, (U32)*acceptance);
|
|
}
|
|
}
|
|
|
|
// all objects passed, go ahead and perform drop if necessary
|
|
if (handled && drop && (U32)*acceptance >= ACCEPT_YES_COPY_SINGLE)
|
|
{
|
|
if ((U32)*acceptance < ACCEPT_YES_COPY_MULTI &&
|
|
mCargoIDs.size() > 1)
|
|
{
|
|
// tried to give multi-cargo to a single-acceptor - refuse and return.
|
|
*acceptance = ACCEPT_NO;
|
|
return;
|
|
}
|
|
|
|
for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++)
|
|
{
|
|
S32 local_x, local_y;
|
|
EAcceptance item_acceptance;
|
|
top_view->screenPointToLocal( x, y, &local_x, &local_y );
|
|
|
|
LLInventoryObject* cargo = locateInventory(item, cat);
|
|
if (cargo)
|
|
{
|
|
handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, true,
|
|
mCargoTypes[mCurItemIndex],
|
|
(void*)cargo,
|
|
&item_acceptance,
|
|
mToolTipMsg);
|
|
}
|
|
else if (is_uuid_dragged)
|
|
{
|
|
handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, false,
|
|
mCargoTypes[mCurItemIndex],
|
|
(void*)&mCargoIDs[mCurItemIndex],
|
|
&item_acceptance,
|
|
mToolTipMsg);
|
|
}
|
|
}
|
|
}
|
|
if (handled)
|
|
{
|
|
mLastAccept = (EAcceptance)*acceptance;
|
|
}
|
|
}
|
|
|
|
if (!handled)
|
|
{
|
|
handled = true;
|
|
|
|
LLRootView* root_view = gViewerWindow->getRootView();
|
|
|
|
for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++)
|
|
{
|
|
EAcceptance item_acceptance = ACCEPT_NO;
|
|
|
|
LLInventoryObject* cargo = locateInventory(item, cat);
|
|
|
|
// fix for EXT-3191
|
|
if (cargo)
|
|
{
|
|
handled = handled && root_view->handleDragAndDrop(x, y, mask, false,
|
|
mCargoTypes[mCurItemIndex],
|
|
(void*)cargo,
|
|
&item_acceptance,
|
|
mToolTipMsg);
|
|
}
|
|
else if (is_uuid_dragged)
|
|
{
|
|
handled = handled && root_view->handleDragAndDrop(x, y, mask, false,
|
|
mCargoTypes[mCurItemIndex],
|
|
(void*)&mCargoIDs[mCurItemIndex],
|
|
&item_acceptance,
|
|
mToolTipMsg);
|
|
}
|
|
if (handled)
|
|
{
|
|
// use sort order to determine priority of acceptance
|
|
*acceptance = (EAcceptance)llmin((U32)item_acceptance, (U32)*acceptance);
|
|
}
|
|
}
|
|
// all objects passed, go ahead and perform drop if necessary
|
|
if (handled && drop && (U32)*acceptance > ACCEPT_NO_LOCKED)
|
|
{
|
|
if ((U32)*acceptance < ACCEPT_YES_COPY_MULTI &&
|
|
mCargoIDs.size() > 1)
|
|
{
|
|
// tried to give multi-cargo to a single-acceptor - refuse and return.
|
|
*acceptance = ACCEPT_NO;
|
|
return;
|
|
}
|
|
|
|
for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++)
|
|
{
|
|
EAcceptance item_acceptance;
|
|
|
|
LLInventoryObject* cargo = locateInventory(item, cat);
|
|
if (cargo)
|
|
{
|
|
handled = handled && root_view->handleDragAndDrop(x, y, mask, true,
|
|
mCargoTypes[mCurItemIndex],
|
|
(void*)cargo,
|
|
&item_acceptance,
|
|
mToolTipMsg);
|
|
}
|
|
else if (is_uuid_dragged)
|
|
{
|
|
handled = handled && root_view->handleDragAndDrop(x, y, mask, true,
|
|
mCargoTypes[mCurItemIndex],
|
|
(void*)&mCargoIDs[mCurItemIndex],
|
|
&item_acceptance,
|
|
mToolTipMsg);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (handled)
|
|
{
|
|
mLastAccept = (EAcceptance)*acceptance;
|
|
}
|
|
}
|
|
|
|
if (!handled)
|
|
{
|
|
// Disallow drag and drop to 3D from the marketplace
|
|
const LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
|
|
if (marketplacelistings_id.notNull())
|
|
{
|
|
for (S32 item_index = 0; item_index < (S32)mCargoIDs.size(); item_index++)
|
|
{
|
|
if (gInventory.isObjectDescendentOf(mCargoIDs[item_index], marketplacelistings_id))
|
|
{
|
|
*acceptance = ACCEPT_NO;
|
|
mToolTipMsg = LLTrans::getString("TooltipOutboxDragToWorld");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
dragOrDrop3D( x, y, mask, drop, acceptance );
|
|
}
|
|
}
|
|
|
|
void LLToolDragAndDrop::dragOrDrop3D( S32 x, S32 y, MASK mask, bool drop, EAcceptance* acceptance )
|
|
{
|
|
mDrop = drop;
|
|
if (mDrop)
|
|
{
|
|
// don't allow drag and drop onto rigged or transparent objects
|
|
pick(gViewerWindow->pickImmediate(x, y, false, false));
|
|
}
|
|
else
|
|
{
|
|
// don't allow drag and drop onto transparent objects
|
|
gViewerWindow->pickAsync(x, y, mask, pickCallback, false, false);
|
|
}
|
|
|
|
*acceptance = mLastAccept;
|
|
}
|
|
|
|
void LLToolDragAndDrop::pickCallback(const LLPickInfo& pick_info)
|
|
{
|
|
if (getInstance() != NULL)
|
|
{
|
|
getInstance()->pick(pick_info);
|
|
}
|
|
}
|
|
|
|
void LLToolDragAndDrop::pick(const LLPickInfo& pick_info)
|
|
{
|
|
EDropTarget target = DT_NONE;
|
|
S32 hit_face = -1;
|
|
|
|
LLViewerObject* hit_obj = pick_info.getObject();
|
|
LLSelectMgr::getInstance()->unhighlightAll();
|
|
bool highlight_object = false;
|
|
// Treat attachments as part of the avatar they are attached to.
|
|
if (hit_obj != NULL)
|
|
{
|
|
// don't allow drag and drop on grass, trees, etc.
|
|
if (pick_info.mPickType == LLPickInfo::PICK_FLORA)
|
|
{
|
|
mCursor = UI_CURSOR_NO;
|
|
gViewerWindow->getWindow()->setCursor( mCursor );
|
|
return;
|
|
}
|
|
|
|
if (hit_obj->isAttachment() && !hit_obj->isHUDAttachment())
|
|
{
|
|
LLVOAvatar* avatar = LLVOAvatar::findAvatarFromAttachment( hit_obj );
|
|
if (!avatar)
|
|
{
|
|
mLastAccept = ACCEPT_NO;
|
|
mCursor = UI_CURSOR_NO;
|
|
gViewerWindow->getWindow()->setCursor( mCursor );
|
|
return;
|
|
}
|
|
hit_obj = avatar;
|
|
}
|
|
|
|
if (hit_obj->isAvatar())
|
|
{
|
|
if (((LLVOAvatar*) hit_obj)->isSelf())
|
|
{
|
|
target = DT_SELF;
|
|
hit_face = -1;
|
|
}
|
|
else
|
|
{
|
|
target = DT_AVATAR;
|
|
hit_face = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
target = DT_OBJECT;
|
|
hit_face = pick_info.mObjectFace;
|
|
highlight_object = true;
|
|
}
|
|
}
|
|
else if (pick_info.mPickType == LLPickInfo::PICK_LAND)
|
|
{
|
|
target = DT_LAND;
|
|
hit_face = -1;
|
|
}
|
|
|
|
mLastAccept = ACCEPT_YES_MULTI;
|
|
|
|
for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++)
|
|
{
|
|
const S32 item_index = mCurItemIndex;
|
|
const EDragAndDropType dad_type = mCargoTypes[item_index];
|
|
// Call the right implementation function
|
|
mLastAccept = (EAcceptance)llmin(
|
|
(U32)mLastAccept,
|
|
(U32)callMemberFunction(*this,
|
|
LLDragAndDropDictionary::instance().get(dad_type, target))
|
|
(hit_obj, hit_face, pick_info.mKeyMask, false));
|
|
}
|
|
|
|
if (mDrop && ((U32)mLastAccept >= ACCEPT_YES_COPY_SINGLE))
|
|
{
|
|
// if target allows multi-drop or there is only one item being dropped, go ahead
|
|
if ((mLastAccept >= ACCEPT_YES_COPY_MULTI) || (mCargoIDs.size() == 1))
|
|
{
|
|
// Target accepts multi, or cargo is a single-drop
|
|
for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++)
|
|
{
|
|
const S32 item_index = mCurItemIndex;
|
|
const EDragAndDropType dad_type = mCargoTypes[item_index];
|
|
// Call the right implementation function
|
|
callMemberFunction(*this, LLDragAndDropDictionary::instance().get(dad_type, target))
|
|
(hit_obj, hit_face, pick_info.mKeyMask, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Target does not accept multi, but cargo is multi
|
|
mLastAccept = ACCEPT_NO;
|
|
}
|
|
}
|
|
|
|
if (highlight_object && mLastAccept > ACCEPT_NO_LOCKED)
|
|
{
|
|
// if any item being dragged will be applied to the object under our cursor
|
|
// highlight that object
|
|
for (S32 i = 0; i < (S32)mCargoIDs.size(); i++)
|
|
{
|
|
if (mCargoTypes[i] != DAD_OBJECT || (pick_info.mKeyMask & MASK_CONTROL))
|
|
{
|
|
LLSelectMgr::getInstance()->highlightObjectAndFamily(hit_obj);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
ECursorType cursor = acceptanceToCursor( mLastAccept );
|
|
gViewerWindow->getWindow()->setCursor( cursor );
|
|
|
|
mLastHitPos = pick_info.mPosGlobal;
|
|
mLastCameraPos = gAgentCamera.getCameraPositionGlobal();
|
|
}
|
|
|
|
// static
|
|
bool LLToolDragAndDrop::handleDropMaterialProtections(LLViewerObject* hit_obj,
|
|
LLInventoryItem* item,
|
|
LLToolDragAndDrop::ESource source,
|
|
const LLUUID& src_id)
|
|
{
|
|
if (!item) return false;
|
|
|
|
// Always succeed if....
|
|
// material is from the library
|
|
// or already in the contents of the object
|
|
if (SOURCE_LIBRARY == source)
|
|
{
|
|
// dropping a material from the library always just works.
|
|
return true;
|
|
}
|
|
|
|
// In case the inventory has not been loaded (e.g. due to some recent operation
|
|
// causing a dirty inventory) and we can do an update, stall the user
|
|
// while fetching the inventory.
|
|
//
|
|
// Fetch if inventory is dirty and listener is present (otherwise we will not receive update)
|
|
if (hit_obj->isInventoryDirty() && hit_obj->hasInventoryListeners())
|
|
{
|
|
hit_obj->requestInventory();
|
|
LLSD args;
|
|
if (LLAssetType::AT_MATERIAL == item->getType())
|
|
{
|
|
args["ERROR_MESSAGE"] = "Unable to add material.\nPlease wait a few seconds and try again.";
|
|
}
|
|
else
|
|
{
|
|
args["ERROR_MESSAGE"] = "Unable to add texture.\nPlease wait a few seconds and try again.";
|
|
}
|
|
LLNotificationsUtil::add("ErrorMessage", args);
|
|
return false;
|
|
}
|
|
// Make sure to verify both id and type since 'null'
|
|
// is a shared default for some asset types.
|
|
if (hit_obj->getInventoryItemByAsset(item->getAssetUUID(), item->getType()))
|
|
{
|
|
// if the asset is already in the object's inventory
|
|
// then it can always be added to a side.
|
|
// This saves some work if the task's inventory is already loaded
|
|
// and ensures that the asset item is only added once.
|
|
return true;
|
|
}
|
|
|
|
LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
|
|
if (!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID()))
|
|
{
|
|
// Check that we can add the material as inventory to the object
|
|
if (willObjectAcceptInventory(hit_obj,item) < ACCEPT_YES_COPY_SINGLE )
|
|
{
|
|
return false;
|
|
}
|
|
// make sure the object has the material in it's inventory.
|
|
if (SOURCE_AGENT == source)
|
|
{
|
|
// Remove the material from local inventory. The server
|
|
// will actually remove the item from agent inventory.
|
|
gInventory.deleteObject(item->getUUID());
|
|
gInventory.notifyObservers();
|
|
}
|
|
else if (SOURCE_WORLD == source)
|
|
{
|
|
// *FIX: if the objects are in different regions, and the
|
|
// source region has crashed, you can bypass these
|
|
// permissions.
|
|
LLViewerObject* src_obj = gObjectList.findObject(src_id);
|
|
if (src_obj)
|
|
{
|
|
src_obj->removeInventory(item->getUUID());
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS() << "Unable to find source object." << LL_ENDL;
|
|
return false;
|
|
}
|
|
}
|
|
// Add the asset item to the target object's inventory.
|
|
if (LLAssetType::AT_TEXTURE == new_item->getType()
|
|
|| LLAssetType::AT_MATERIAL == new_item->getType())
|
|
{
|
|
hit_obj->updateMaterialInventory(new_item, TASK_INVENTORY_ITEM_KEY, true);
|
|
}
|
|
else
|
|
{
|
|
hit_obj->updateInventory(new_item, TASK_INVENTORY_ITEM_KEY, true);
|
|
}
|
|
// Force the object to update and refetch its inventory so it has this asset.
|
|
hit_obj->dirtyInventory();
|
|
hit_obj->requestInventory();
|
|
// TODO: Check to see if adding the item was successful; if not, then
|
|
// we should return false here. This will requre a separate listener
|
|
// since without listener, we have no way to receive update
|
|
}
|
|
else if (!item->getPermissions().allowOperationBy(PERM_TRANSFER,
|
|
gAgent.getID()))
|
|
{
|
|
// Check that we can add the asset as inventory to the object
|
|
if (willObjectAcceptInventory(hit_obj,item) < ACCEPT_YES_COPY_SINGLE )
|
|
{
|
|
return false;
|
|
}
|
|
// *FIX: may want to make sure agent can paint hit_obj.
|
|
|
|
// Add the asset item to the target object's inventory.
|
|
if (LLAssetType::AT_TEXTURE == new_item->getType()
|
|
|| LLAssetType::AT_MATERIAL == new_item->getType())
|
|
{
|
|
hit_obj->updateMaterialInventory(new_item, TASK_INVENTORY_ITEM_KEY, true);
|
|
}
|
|
else
|
|
{
|
|
hit_obj->updateInventory(new_item, TASK_INVENTORY_ITEM_KEY, true);
|
|
}
|
|
// Force the object to update and refetch its inventory so it has this asset.
|
|
hit_obj->dirtyInventory();
|
|
hit_obj->requestInventory();
|
|
// TODO: Check to see if adding the item was successful; if not, then
|
|
// we should return false here. This will requre a separate listener
|
|
// since without listener, we have no way to receive update
|
|
}
|
|
else if (LLAssetType::AT_MATERIAL == new_item->getType() &&
|
|
!item->getPermissions().allowOperationBy(PERM_MODIFY, gAgent.getID()))
|
|
{
|
|
// Check that we can add the material as inventory to the object
|
|
if (willObjectAcceptInventory(hit_obj,item) < ACCEPT_YES_COPY_SINGLE )
|
|
{
|
|
return false;
|
|
}
|
|
// *FIX: may want to make sure agent can paint hit_obj.
|
|
|
|
// Add the material item to the target object's inventory.
|
|
hit_obj->updateMaterialInventory(new_item, TASK_INVENTORY_ITEM_KEY, true);
|
|
|
|
// Force the object to update and refetch its inventory so it has this material.
|
|
hit_obj->dirtyInventory();
|
|
hit_obj->requestInventory();
|
|
// TODO: Check to see if adding the item was successful; if not, then
|
|
// we should return false here. This will requre a separate listener
|
|
// since without listener, we have no way to receive update
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void set_texture_to_material(LLViewerObject* hit_obj,
|
|
S32 hit_face,
|
|
const LLUUID& asset_id,
|
|
LLGLTFMaterial::TextureInfo drop_channel)
|
|
{
|
|
LLTextureEntry* te = hit_obj->getTE(hit_face);
|
|
if (!te)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const LLUUID base_mat_id = hit_obj->getRenderMaterialID(hit_face);
|
|
if (base_mat_id.isNull())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (hit_obj->isInventoryDirty() && hit_obj->hasInventoryListeners())
|
|
{
|
|
hit_obj->requestInventory();
|
|
return;
|
|
}
|
|
|
|
LLViewerInventoryItem* mat_item = hit_obj->getInventoryItemByAsset(base_mat_id);
|
|
if (mat_item && !mat_item->getPermissions().allowModifyBy(gAgentID))
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLPointer<LLGLTFMaterial> material = te->getGLTFMaterialOverride();
|
|
|
|
// make a copy to not invalidate existing
|
|
// material for multiple objects
|
|
if (material.isNull())
|
|
{
|
|
// Start with a material override which does not make any changes
|
|
material = new LLGLTFMaterial();
|
|
}
|
|
else
|
|
{
|
|
material = new LLGLTFMaterial(*material);
|
|
}
|
|
|
|
switch (drop_channel)
|
|
{
|
|
case LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR:
|
|
default:
|
|
{
|
|
material->setBaseColorId(asset_id);
|
|
}
|
|
break;
|
|
|
|
case LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS:
|
|
{
|
|
material->setOcclusionRoughnessMetallicId(asset_id);
|
|
}
|
|
break;
|
|
|
|
case LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE:
|
|
{
|
|
material->setEmissiveId(asset_id);
|
|
}
|
|
break;
|
|
|
|
case LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL:
|
|
{
|
|
material->setNormalId(asset_id);
|
|
}
|
|
break;
|
|
}
|
|
LLGLTFMaterialList::queueModify(hit_obj, hit_face, material);
|
|
}
|
|
|
|
void LLToolDragAndDrop::dropTextureAllFaces(LLViewerObject* hit_obj,
|
|
LLInventoryItem* item,
|
|
LLToolDragAndDrop::ESource source,
|
|
const LLUUID& src_id,
|
|
bool remove_pbr)
|
|
{
|
|
if (!item)
|
|
{
|
|
LL_WARNS() << "LLToolDragAndDrop::dropTextureAllFaces no texture item." << LL_ENDL;
|
|
return;
|
|
}
|
|
S32 num_faces = hit_obj->getNumTEs();
|
|
bool has_non_pbr_faces = false;
|
|
for (S32 face = 0; face < num_faces; face++)
|
|
{
|
|
if (hit_obj->getRenderMaterialID(face).isNull())
|
|
{
|
|
has_non_pbr_faces = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (has_non_pbr_faces || remove_pbr)
|
|
{
|
|
bool res = handleDropMaterialProtections(hit_obj, item, source, src_id);
|
|
if (!res)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
LLUUID asset_id = item->getAssetUUID();
|
|
|
|
// Overrides require textures to be copy and transfer free
|
|
LLPermissions item_permissions = item->getPermissions();
|
|
bool allow_adding_to_override = item_permissions.allowOperationBy(PERM_COPY, gAgent.getID());
|
|
allow_adding_to_override &= item_permissions.allowOperationBy(PERM_TRANSFER, gAgent.getID());
|
|
|
|
LLViewerTexture* image = LLViewerTextureManager::getFetchedTexture(asset_id);
|
|
add(LLStatViewer::EDIT_TEXTURE, 1);
|
|
for( S32 face = 0; face < num_faces; face++ )
|
|
{
|
|
if (remove_pbr)
|
|
{
|
|
hit_obj->setRenderMaterialID(face, LLUUID::null);
|
|
hit_obj->setTEImage(face, image);
|
|
dialog_refresh_all();
|
|
}
|
|
else if (hit_obj->getRenderMaterialID(face).isNull())
|
|
{
|
|
// update viewer side
|
|
hit_obj->setTEImage(face, image);
|
|
dialog_refresh_all();
|
|
}
|
|
else if (allow_adding_to_override)
|
|
{
|
|
set_texture_to_material(hit_obj, face, asset_id, LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR);
|
|
}
|
|
}
|
|
|
|
// send the update to the simulator
|
|
LLGLTFMaterialList::flushUpdates(nullptr);
|
|
hit_obj->sendTEUpdate();
|
|
}
|
|
|
|
void LLToolDragAndDrop::dropMaterial(LLViewerObject* hit_obj,
|
|
S32 hit_face,
|
|
LLInventoryItem* item,
|
|
LLToolDragAndDrop::ESource source,
|
|
const LLUUID& src_id,
|
|
bool all_faces)
|
|
{
|
|
LLSelectNode* nodep = nullptr;
|
|
if (hit_obj->isSelected())
|
|
{
|
|
// update object's saved materials
|
|
nodep = LLSelectMgr::getInstance()->getSelection()->findNode(hit_obj);
|
|
}
|
|
|
|
// If user dropped a material onto face it implies
|
|
// applying texture now without cancel, save to selection
|
|
if (all_faces)
|
|
{
|
|
dropMaterialAllFaces(hit_obj, item, source, src_id);
|
|
|
|
if (nodep)
|
|
{
|
|
uuid_vec_t material_ids;
|
|
gltf_materials_vec_t override_materials;
|
|
S32 num_faces = hit_obj->getNumTEs();
|
|
for (S32 face = 0; face < num_faces; face++)
|
|
{
|
|
material_ids.push_back(hit_obj->getRenderMaterialID(face));
|
|
override_materials.push_back(nullptr);
|
|
}
|
|
nodep->saveGLTFMaterials(material_ids, override_materials);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dropMaterialOneFace(hit_obj, hit_face, item, source, src_id);
|
|
|
|
// If user dropped a material onto face it implies
|
|
// applying texture now without cancel, save to selection
|
|
if (nodep
|
|
&& gFloaterTools
|
|
&& gFloaterTools->getVisible()
|
|
&& nodep->mSavedGLTFMaterialIds.size() > hit_face)
|
|
{
|
|
nodep->mSavedGLTFMaterialIds[hit_face] = hit_obj->getRenderMaterialID(hit_face);
|
|
nodep->mSavedGLTFOverrideMaterials[hit_face] = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLToolDragAndDrop::dropMaterialOneFace(LLViewerObject* hit_obj,
|
|
S32 hit_face,
|
|
LLInventoryItem* item,
|
|
LLToolDragAndDrop::ESource source,
|
|
const LLUUID& src_id)
|
|
{
|
|
if (hit_face == -1) return;
|
|
if (!item || item->getInventoryType() != LLInventoryType::IT_MATERIAL)
|
|
{
|
|
LL_WARNS() << "LLToolDragAndDrop::dropTextureOneFace no material item." << LL_ENDL;
|
|
return;
|
|
}
|
|
|
|
// SL-20013 must save asset_id before handleDropMaterialProtections since our item instance
|
|
// may be deleted if it is moved into task inventory
|
|
LLUUID asset_id = item->getAssetUUID();
|
|
bool success = handleDropMaterialProtections(hit_obj, item, source, src_id);
|
|
if (!success)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (asset_id.isNull())
|
|
{
|
|
// use blank material
|
|
asset_id = BLANK_MATERIAL_ASSET_ID;
|
|
}
|
|
|
|
hit_obj->setRenderMaterialID(hit_face, asset_id);
|
|
|
|
dialog_refresh_all();
|
|
|
|
// send the update to the simulator
|
|
hit_obj->sendTEUpdate();
|
|
}
|
|
|
|
|
|
void LLToolDragAndDrop::dropMaterialAllFaces(LLViewerObject* hit_obj,
|
|
LLInventoryItem* item,
|
|
LLToolDragAndDrop::ESource source,
|
|
const LLUUID& src_id)
|
|
{
|
|
if (!item || item->getInventoryType() != LLInventoryType::IT_MATERIAL)
|
|
{
|
|
LL_WARNS() << "LLToolDragAndDrop::dropTextureAllFaces no material item." << LL_ENDL;
|
|
return;
|
|
}
|
|
|
|
// SL-20013 must save asset_id before handleDropMaterialProtections since our item instance
|
|
// may be deleted if it is moved into task inventory
|
|
LLUUID asset_id = item->getAssetUUID();
|
|
bool success = handleDropMaterialProtections(hit_obj, item, source, src_id);
|
|
|
|
if (!success)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (asset_id.isNull())
|
|
{
|
|
// use blank material
|
|
asset_id = BLANK_MATERIAL_ASSET_ID;
|
|
}
|
|
|
|
hit_obj->setRenderMaterialIDs(asset_id);
|
|
dialog_refresh_all();
|
|
// send the update to the simulator
|
|
hit_obj->sendTEUpdate();
|
|
}
|
|
|
|
|
|
void LLToolDragAndDrop::dropMesh(LLViewerObject* hit_obj,
|
|
LLInventoryItem* item,
|
|
LLToolDragAndDrop::ESource source,
|
|
const LLUUID& src_id)
|
|
{
|
|
if (!item)
|
|
{
|
|
LL_WARNS() << "no inventory item." << LL_ENDL;
|
|
return;
|
|
}
|
|
LLUUID asset_id = item->getAssetUUID();
|
|
bool success = handleDropMaterialProtections(hit_obj, item, source, src_id);
|
|
if(!success)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLSculptParams sculpt_params;
|
|
sculpt_params.setSculptTexture(asset_id, LL_SCULPT_TYPE_MESH);
|
|
hit_obj->setParameterEntry(LLNetworkData::PARAMS_SCULPT, sculpt_params, true);
|
|
|
|
dialog_refresh_all();
|
|
}
|
|
|
|
void LLToolDragAndDrop::dropTexture(LLViewerObject* hit_obj,
|
|
S32 hit_face,
|
|
LLInventoryItem* item,
|
|
ESource source,
|
|
const LLUUID& src_id,
|
|
bool all_faces,
|
|
bool remove_pbr,
|
|
S32 tex_channel)
|
|
{
|
|
LLSelectNode* nodep = nullptr;
|
|
if (hit_obj->isSelected())
|
|
{
|
|
// update object's saved textures
|
|
nodep = LLSelectMgr::getInstance()->getSelection()->findNode(hit_obj);
|
|
}
|
|
|
|
if (all_faces)
|
|
{
|
|
dropTextureAllFaces(hit_obj, item, source, src_id, remove_pbr);
|
|
|
|
// If user dropped a texture onto face it implies
|
|
// applying texture now without cancel, save to selection
|
|
if (nodep)
|
|
{
|
|
uuid_vec_t texture_ids;
|
|
uuid_vec_t material_ids;
|
|
gltf_materials_vec_t override_materials;
|
|
S32 num_faces = hit_obj->getNumTEs();
|
|
for (S32 face = 0; face < num_faces; face++)
|
|
{
|
|
LLViewerTexture* tex = hit_obj->getTEImage(face);
|
|
if (tex != nullptr)
|
|
{
|
|
texture_ids.push_back(tex->getID());
|
|
}
|
|
else
|
|
{
|
|
texture_ids.push_back(LLUUID::null);
|
|
}
|
|
|
|
// either removed or modified materials
|
|
if (remove_pbr)
|
|
{
|
|
material_ids.push_back(LLUUID::null);
|
|
}
|
|
else
|
|
{
|
|
material_ids.push_back(hit_obj->getRenderMaterialID(face));
|
|
}
|
|
|
|
LLTextureEntry* te = hit_obj->getTE(hit_face);
|
|
if (te && !remove_pbr)
|
|
{
|
|
// saveGLTFMaterials will make a copy
|
|
override_materials.push_back(te->getGLTFMaterialOverride());
|
|
}
|
|
else
|
|
{
|
|
override_materials.push_back(nullptr);
|
|
}
|
|
}
|
|
|
|
nodep->saveTextures(texture_ids);
|
|
nodep->saveGLTFMaterials(material_ids, override_materials);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dropTextureOneFace(hit_obj, hit_face, item, source, src_id, remove_pbr, tex_channel);
|
|
|
|
// If user dropped a texture onto face it implies
|
|
// applying texture now without cancel, save to selection
|
|
LLPanelFace* panel_face = gFloaterTools ? gFloaterTools->getPanelFace() : nullptr;
|
|
if (nodep
|
|
&& panel_face
|
|
&& gFloaterTools->getVisible()
|
|
&& panel_face->getTextureDropChannel() == 0 /*texture*/
|
|
&& nodep->mSavedTextures.size() > hit_face)
|
|
{
|
|
LLViewerTexture* tex = hit_obj->getTEImage(hit_face);
|
|
if (tex != nullptr)
|
|
{
|
|
nodep->mSavedTextures[hit_face] = tex->getID();
|
|
}
|
|
else
|
|
{
|
|
nodep->mSavedTextures[hit_face] = LLUUID::null;
|
|
}
|
|
|
|
LLTextureEntry* te = hit_obj->getTE(hit_face);
|
|
LLGLTFMaterial * override_mat = nullptr;
|
|
if (te && !remove_pbr && (override_mat = te->getGLTFMaterialOverride()))
|
|
{
|
|
LLGLTFMaterial* copy = new LLGLTFMaterial(*override_mat);
|
|
nodep->mSavedGLTFOverrideMaterials[hit_face] = copy;
|
|
}
|
|
else
|
|
{
|
|
nodep->mSavedGLTFOverrideMaterials[hit_face] = nullptr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLToolDragAndDrop::dropTextureOneFace(LLViewerObject* hit_obj,
|
|
S32 hit_face,
|
|
LLInventoryItem* item,
|
|
LLToolDragAndDrop::ESource source,
|
|
const LLUUID& src_id,
|
|
bool remove_pbr,
|
|
S32 tex_channel)
|
|
{
|
|
if (hit_face == -1) return;
|
|
if (!item)
|
|
{
|
|
LL_WARNS() << "LLToolDragAndDrop::dropTextureOneFace no texture item." << LL_ENDL;
|
|
return;
|
|
}
|
|
|
|
LLUUID asset_id = item->getAssetUUID();
|
|
|
|
if (hit_obj->getRenderMaterialID(hit_face).notNull() && !remove_pbr)
|
|
{
|
|
// Overrides require textures to be copy and transfer free
|
|
LLPermissions item_permissions = item->getPermissions();
|
|
bool allow_adding_to_override = item_permissions.allowOperationBy(PERM_COPY, gAgent.getID());
|
|
allow_adding_to_override &= item_permissions.allowOperationBy(PERM_TRANSFER, gAgent.getID());
|
|
|
|
if (allow_adding_to_override)
|
|
{
|
|
LLGLTFMaterial::TextureInfo drop_channel = LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR;
|
|
LLPanelFace* panel_face = gFloaterTools ? gFloaterTools->getPanelFace() : nullptr;
|
|
if (panel_face && gFloaterTools->getVisible())
|
|
{
|
|
drop_channel = panel_face->getPBRDropChannel();
|
|
}
|
|
set_texture_to_material(hit_obj, hit_face, asset_id, drop_channel);
|
|
LLGLTFMaterialList::flushUpdates(nullptr);
|
|
}
|
|
return;
|
|
}
|
|
bool success = handleDropMaterialProtections(hit_obj, item, source, src_id);
|
|
if (!success)
|
|
{
|
|
return;
|
|
}
|
|
if (remove_pbr)
|
|
{
|
|
hit_obj->setRenderMaterialID(hit_face, LLUUID::null);
|
|
}
|
|
|
|
// update viewer side image in anticipation of update from simulator
|
|
LLViewerTexture* image = LLViewerTextureManager::getFetchedTexture(asset_id);
|
|
add(LLStatViewer::EDIT_TEXTURE, 1);
|
|
|
|
LLTextureEntry* tep = hit_obj->getTE(hit_face);
|
|
|
|
LLPanelFace* panel_face = gFloaterTools ? gFloaterTools->getPanelFace() : nullptr;
|
|
|
|
if (panel_face && gFloaterTools->getVisible())
|
|
{
|
|
tex_channel = (tex_channel > -1) ? tex_channel : panel_face->getTextureDropChannel();
|
|
switch (tex_channel)
|
|
{
|
|
|
|
case 0:
|
|
default:
|
|
{
|
|
hit_obj->setTEImage(hit_face, image);
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
if (tep)
|
|
{
|
|
LLMaterialPtr old_mat = tep->getMaterialParams();
|
|
LLMaterialPtr new_mat = panel_face->createDefaultMaterial(old_mat);
|
|
new_mat->setNormalID(asset_id);
|
|
tep->setMaterialParams(new_mat);
|
|
hit_obj->setTENormalMap(hit_face, asset_id);
|
|
LLMaterialMgr::getInstance()->put(hit_obj->getID(), hit_face, *new_mat);
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
if (tep)
|
|
{
|
|
LLMaterialPtr old_mat = tep->getMaterialParams();
|
|
LLMaterialPtr new_mat = panel_face->createDefaultMaterial(old_mat);
|
|
new_mat->setSpecularID(asset_id);
|
|
tep->setMaterialParams(new_mat);
|
|
hit_obj->setTESpecularMap(hit_face, asset_id);
|
|
LLMaterialMgr::getInstance()->put(hit_obj->getID(), hit_face, *new_mat);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hit_obj->setTEImage(hit_face, image);
|
|
}
|
|
|
|
dialog_refresh_all();
|
|
|
|
// send the update to the simulator
|
|
hit_obj->sendTEUpdate();
|
|
}
|
|
|
|
|
|
void LLToolDragAndDrop::dropScript(LLViewerObject* hit_obj,
|
|
LLInventoryItem* item,
|
|
bool active,
|
|
ESource source,
|
|
const LLUUID& src_id)
|
|
{
|
|
// *HACK: In order to resolve SL-22177, we need to block drags
|
|
// from notecards and objects onto other objects.
|
|
if ((SOURCE_WORLD == LLToolDragAndDrop::getInstance()->mSource)
|
|
|| (SOURCE_NOTECARD == LLToolDragAndDrop::getInstance()->mSource))
|
|
{
|
|
LL_WARNS() << "Call to LLToolDragAndDrop::dropScript() from world"
|
|
<< " or notecard." << LL_ENDL;
|
|
return;
|
|
}
|
|
if (hit_obj && item)
|
|
{
|
|
LLPointer<LLViewerInventoryItem> new_script = new LLViewerInventoryItem(item);
|
|
if (!item->getPermissions().allowCopyBy(gAgent.getID()))
|
|
{
|
|
if (SOURCE_AGENT == source)
|
|
{
|
|
// Remove the script from local inventory. The server
|
|
// will actually remove the item from agent inventory.
|
|
gInventory.deleteObject(item->getUUID());
|
|
gInventory.notifyObservers();
|
|
}
|
|
else if (SOURCE_WORLD == source)
|
|
{
|
|
// *FIX: if the objects are in different regions, and
|
|
// the source region has crashed, you can bypass
|
|
// these permissions.
|
|
LLViewerObject* src_obj = gObjectList.findObject(src_id);
|
|
if (src_obj)
|
|
{
|
|
src_obj->removeInventory(item->getUUID());
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS() << "Unable to find source object." << LL_ENDL;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
hit_obj->saveScript(new_script, active, true);
|
|
if (gFloaterTools)
|
|
{
|
|
gFloaterTools->dirty();
|
|
}
|
|
|
|
// VEFFECT: SetScript
|
|
LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, true);
|
|
effectp->setSourceObject(gAgentAvatarp);
|
|
effectp->setTargetObject(hit_obj);
|
|
effectp->setDuration(LL_HUD_DUR_SHORT);
|
|
effectp->setColor(LLColor4U(gAgent.getEffectColor()));
|
|
}
|
|
}
|
|
|
|
void LLToolDragAndDrop::dropObject(LLViewerObject* raycast_target,
|
|
bool bypass_sim_raycast,
|
|
bool from_task_inventory,
|
|
bool remove_from_inventory)
|
|
{
|
|
LLViewerRegion* regionp = LLWorld::getInstance()->getRegionFromPosGlobal(mLastHitPos);
|
|
if (!regionp)
|
|
{
|
|
LL_WARNS() << "Couldn't find region to rez object" << LL_ENDL;
|
|
return;
|
|
}
|
|
|
|
//LL_INFOS() << "Rezzing object" << LL_ENDL;
|
|
make_ui_sound("UISndObjectRezIn");
|
|
LLViewerInventoryItem* item;
|
|
LLViewerInventoryCategory* cat;
|
|
locateInventory(item, cat);
|
|
if (!item || !item->isFinished()) return;
|
|
|
|
//if (regionp
|
|
// && (regionp->getRegionFlag(REGION_FLAGS_SANDBOX)))
|
|
//{
|
|
// LLFirstUse::useSandbox();
|
|
//}
|
|
// check if it cannot be copied, and mark as remove if it is -
|
|
// this will remove the object from inventory after rez. Only
|
|
// bother with this check if we would not normally remove from
|
|
// inventory.
|
|
if (!remove_from_inventory
|
|
&& !item->getPermissions().allowCopyBy(gAgent.getID()))
|
|
{
|
|
remove_from_inventory = true;
|
|
}
|
|
|
|
// Limit raycast to a single object.
|
|
// Speeds up server raycast + avoid problems with server ray
|
|
// hitting objects that were clipped by the near plane or culled
|
|
// on the viewer.
|
|
LLUUID ray_target_id;
|
|
if (raycast_target)
|
|
{
|
|
ray_target_id = raycast_target->getID();
|
|
}
|
|
else
|
|
{
|
|
ray_target_id.setNull();
|
|
}
|
|
|
|
// Check if it's in the trash.
|
|
bool is_in_trash = false;
|
|
const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
|
|
if (gInventory.isObjectDescendentOf(item->getUUID(), trash_id))
|
|
{
|
|
is_in_trash = true;
|
|
}
|
|
|
|
LLUUID source_id = from_task_inventory ? mSourceID : LLUUID::null;
|
|
|
|
// Select the object only if we're editing.
|
|
bool rez_selected = LLToolMgr::getInstance()->inEdit();
|
|
|
|
|
|
LLVector3 ray_start = regionp->getPosRegionFromGlobal(mLastCameraPos);
|
|
LLVector3 ray_end = regionp->getPosRegionFromGlobal(mLastHitPos);
|
|
// currently the ray's end point is an approximation,
|
|
// and is sometimes too short (causing failure.) so we
|
|
// double the ray's length:
|
|
if (!bypass_sim_raycast)
|
|
{
|
|
LLVector3 ray_direction = ray_start - ray_end;
|
|
ray_end = ray_end - ray_direction;
|
|
}
|
|
|
|
|
|
// Message packing code should be it's own uninterrupted block
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
if (mSource == SOURCE_NOTECARD)
|
|
{
|
|
LLUIUsage::instance().logCommand("Object.RezObjectFromNotecard");
|
|
msg->newMessageFast(_PREHASH_RezObjectFromNotecard);
|
|
}
|
|
else
|
|
{
|
|
LLUIUsage::instance().logCommand("Object.RezObject");
|
|
msg->newMessageFast(_PREHASH_RezObject);
|
|
}
|
|
msg->nextBlockFast(_PREHASH_AgentData);
|
|
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
|
|
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
|
|
msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID());
|
|
|
|
msg->nextBlock("RezData");
|
|
// if it's being rezzed from task inventory, we need to enable
|
|
// saving it back into the task inventory.
|
|
// *FIX: We can probably compress this to a single byte, since I
|
|
// think folderid == mSourceID. This will be a later
|
|
// optimization.
|
|
msg->addUUIDFast(_PREHASH_FromTaskID, source_id);
|
|
msg->addU8Fast(_PREHASH_BypassRaycast, (U8) bypass_sim_raycast);
|
|
msg->addVector3Fast(_PREHASH_RayStart, ray_start);
|
|
msg->addVector3Fast(_PREHASH_RayEnd, ray_end);
|
|
msg->addUUIDFast(_PREHASH_RayTargetID, ray_target_id );
|
|
msg->addBOOLFast(_PREHASH_RayEndIsIntersection, false);
|
|
msg->addBOOLFast(_PREHASH_RezSelected, rez_selected);
|
|
msg->addBOOLFast(_PREHASH_RemoveItem, remove_from_inventory);
|
|
|
|
// deal with permissions slam logic
|
|
pack_permissions_slam(msg, item->getFlags(), item->getPermissions());
|
|
|
|
LLUUID folder_id = item->getParentUUID();
|
|
if ((SOURCE_LIBRARY == mSource) || (is_in_trash))
|
|
{
|
|
// since it's coming from the library or trash, we want to not
|
|
// 'take' it back to the same place.
|
|
item->setParent(LLUUID::null);
|
|
// *TODO this code isn't working - the parent (FolderID) is still
|
|
// set when the object is "taken". so code on the "take" side is
|
|
// checking for trash and library as well (llviewermenu.cpp)
|
|
}
|
|
if (mSource == SOURCE_NOTECARD)
|
|
{
|
|
msg->nextBlockFast(_PREHASH_NotecardData);
|
|
msg->addUUIDFast(_PREHASH_NotecardItemID, mSourceID);
|
|
msg->addUUIDFast(_PREHASH_ObjectID, mObjectID);
|
|
msg->nextBlockFast(_PREHASH_InventoryData);
|
|
msg->addUUIDFast(_PREHASH_ItemID, item->getUUID());
|
|
}
|
|
else
|
|
{
|
|
msg->nextBlockFast(_PREHASH_InventoryData);
|
|
item->packMessage(msg);
|
|
}
|
|
msg->sendReliable(regionp->getHost());
|
|
// back out the change. no actual internal changes take place.
|
|
item->setParent(folder_id);
|
|
|
|
// If we're going to select it, get ready for the incoming
|
|
// selected object.
|
|
if (rez_selected)
|
|
{
|
|
LLSelectMgr::getInstance()->deselectAll();
|
|
gViewerWindow->getWindow()->incBusyCount();
|
|
}
|
|
|
|
if (remove_from_inventory)
|
|
{
|
|
// Delete it from inventory immediately so that users cannot
|
|
// easily bypass copy protection in laggy situations. If the
|
|
// rez fails, we will put it back on the server.
|
|
gInventory.deleteObject(item->getUUID());
|
|
gInventory.notifyObservers();
|
|
}
|
|
|
|
// VEFFECT: DropObject
|
|
LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, true);
|
|
effectp->setSourceObject(gAgentAvatarp);
|
|
effectp->setPositionGlobal(mLastHitPos);
|
|
effectp->setDuration(LL_HUD_DUR_SHORT);
|
|
effectp->setColor(LLColor4U(gAgent.getEffectColor()));
|
|
|
|
add(LLStatViewer::OBJECT_REZ, 1);
|
|
}
|
|
|
|
void LLToolDragAndDrop::dropInventory(LLViewerObject* hit_obj,
|
|
LLInventoryItem* item,
|
|
LLToolDragAndDrop::ESource source,
|
|
const LLUUID& src_id)
|
|
{
|
|
// *HACK: In order to resolve SL-22177, we need to block drags
|
|
// from notecards and objects onto other objects.
|
|
if ((SOURCE_WORLD == LLToolDragAndDrop::getInstance()->mSource)
|
|
|| (SOURCE_NOTECARD == LLToolDragAndDrop::getInstance()->mSource))
|
|
{
|
|
LL_WARNS() << "Call to LLToolDragAndDrop::dropInventory() from world"
|
|
<< " or notecard." << LL_ENDL;
|
|
return;
|
|
}
|
|
|
|
LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
|
|
time_t creation_date = time_corrected();
|
|
new_item->setCreationDate(creation_date);
|
|
|
|
if (!item->getPermissions().allowCopyBy(gAgent.getID()))
|
|
{
|
|
if (SOURCE_AGENT == source)
|
|
{
|
|
// Remove the inventory item from local inventory. The
|
|
// server will actually remove the item from agent
|
|
// inventory.
|
|
gInventory.deleteObject(item->getUUID());
|
|
gInventory.notifyObservers();
|
|
}
|
|
else if (SOURCE_WORLD == source)
|
|
{
|
|
// *FIX: if the objects are in different regions, and the
|
|
// source region has crashed, you can bypass these
|
|
// permissions.
|
|
LLViewerObject* src_obj = gObjectList.findObject(src_id);
|
|
if (src_obj)
|
|
{
|
|
src_obj->removeInventory(item->getUUID());
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS() << "Unable to find source object." << LL_ENDL;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
hit_obj->updateInventory(new_item, TASK_INVENTORY_ITEM_KEY, true);
|
|
if (LLFloaterReg::instanceVisible("build"))
|
|
{
|
|
// *FIX: only show this if panel not expanded?
|
|
LLFloaterReg::showInstance("build", "Content");
|
|
}
|
|
|
|
// VEFFECT: AddToInventory
|
|
LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, true);
|
|
effectp->setSourceObject(gAgentAvatarp);
|
|
effectp->setTargetObject(hit_obj);
|
|
effectp->setDuration(LL_HUD_DUR_SHORT);
|
|
effectp->setColor(LLColor4U(gAgent.getEffectColor()));
|
|
if (gFloaterTools)
|
|
{
|
|
gFloaterTools->dirty();
|
|
}
|
|
}
|
|
|
|
// accessor that looks at permissions, copyability, and names of
|
|
// inventory items to determine if a drop would be ok.
|
|
EAcceptance LLToolDragAndDrop::willObjectAcceptInventory(LLViewerObject* obj, LLInventoryItem* item, EDragAndDropType type)
|
|
{
|
|
// check the basics
|
|
if (!item || !obj)
|
|
return ACCEPT_NO;
|
|
|
|
// HACK: downcast
|
|
LLViewerInventoryItem* vitem = (LLViewerInventoryItem*)item;
|
|
if (!vitem->isFinished() && (type != DAD_CATEGORY))
|
|
{
|
|
// Note: for DAD_CATEGORY we assume that folder version check passed and folder
|
|
// is complete, meaning that items inside are up to date.
|
|
// (isFinished() == false) at the moment shows that item was loaded from cache.
|
|
// Library or agent inventory only.
|
|
return ACCEPT_NO;
|
|
}
|
|
if (vitem->getIsLinkType())
|
|
return ACCEPT_NO; // No giving away links
|
|
|
|
// deny attempts to drop from an object onto itself. This is to
|
|
// help make sure that drops that are from an object to an object
|
|
// don't have to worry about order of evaluation. Think of this
|
|
// like check for self in assignment.
|
|
if (obj->getID() == item->getParentUUID())
|
|
{
|
|
return ACCEPT_NO;
|
|
}
|
|
|
|
//bool copy = (perm.allowCopyBy(gAgent.getID(),
|
|
// gAgent.getGroupID())
|
|
// && (obj->mPermModify || obj->mFlagAllowInventoryAdd));
|
|
bool worn = false;
|
|
LLVOAvatarSelf* my_avatar = NULL;
|
|
switch (item->getType())
|
|
{
|
|
case LLAssetType::AT_OBJECT:
|
|
my_avatar = gAgentAvatarp;
|
|
if(my_avatar && my_avatar->isWearingAttachment(item->getUUID()))
|
|
{
|
|
worn = true;
|
|
}
|
|
break;
|
|
case LLAssetType::AT_BODYPART:
|
|
case LLAssetType::AT_CLOTHING:
|
|
if (gAgentWearables.isWearingItem(item->getUUID()))
|
|
{
|
|
worn = true;
|
|
}
|
|
break;
|
|
case LLAssetType::AT_CALLINGCARD:
|
|
// Calling Cards in object are disabled for now
|
|
// because of incomplete LSL support. See STORM-1117.
|
|
return ACCEPT_NO;
|
|
default:
|
|
break;
|
|
}
|
|
const LLPermissions& perm = item->getPermissions();
|
|
bool modify = (obj->permModify() || obj->flagAllowInventoryAdd());
|
|
bool transfer = false;
|
|
if ((obj->permYouOwner() && (perm.getOwner() == gAgent.getID()))
|
|
|| perm.allowOperationBy(PERM_TRANSFER, gAgent.getID()))
|
|
{
|
|
transfer = true;
|
|
}
|
|
bool volume = (LL_PCODE_VOLUME == obj->getPCode());
|
|
bool attached = obj->isAttachment();
|
|
bool unrestricted = (perm.getMaskBase() & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED;
|
|
|
|
if (attached && !unrestricted)
|
|
{
|
|
// Attachments are in world and in inventory simultaneously,
|
|
// at the moment server doesn't support such a situation.
|
|
return ACCEPT_NO_LOCKED;
|
|
}
|
|
|
|
if (modify && transfer && volume && !worn)
|
|
{
|
|
return ACCEPT_YES_MULTI;
|
|
}
|
|
|
|
if (!modify)
|
|
{
|
|
return ACCEPT_NO_LOCKED;
|
|
}
|
|
|
|
return ACCEPT_NO;
|
|
}
|
|
|
|
|
|
static void give_inventory_cb(const LLSD& notification, const LLSD& response)
|
|
{
|
|
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
|
|
// if Cancel pressed
|
|
if (option == 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLSD payload = notification["payload"];
|
|
const LLUUID& session_id = payload["session_id"];
|
|
const LLUUID& agent_id = payload["agent_id"];
|
|
LLViewerInventoryItem * inv_item = gInventory.getItem(payload["item_id"]);
|
|
LLViewerInventoryCategory * inv_cat = gInventory.getCategory(payload["item_id"]);
|
|
if (NULL == inv_item && NULL == inv_cat)
|
|
{
|
|
llassert( false );
|
|
return;
|
|
}
|
|
bool successfully_shared;
|
|
if (inv_item)
|
|
{
|
|
successfully_shared = LLGiveInventory::doGiveInventoryItem(agent_id, inv_item, session_id);
|
|
}
|
|
else
|
|
{
|
|
successfully_shared = LLGiveInventory::doGiveInventoryCategory(agent_id, inv_cat, session_id);
|
|
}
|
|
if (successfully_shared)
|
|
{
|
|
if ("avatarpicker" == payload["d&d_dest"].asString())
|
|
{
|
|
LLFloaterReg::hideInstance("avatar_picker");
|
|
}
|
|
LLNotificationsUtil::add("ItemsShared");
|
|
}
|
|
}
|
|
|
|
static void show_object_sharing_confirmation(const std::string name,
|
|
LLInventoryObject* inv_item,
|
|
const LLSD& dest,
|
|
const LLUUID& dest_agent,
|
|
const LLUUID& session_id = LLUUID::null)
|
|
{
|
|
if (!inv_item)
|
|
{
|
|
llassert(NULL != inv_item);
|
|
return;
|
|
}
|
|
LLSD substitutions;
|
|
substitutions["RESIDENTS"] = name;
|
|
substitutions["ITEMS"] = inv_item->getName();
|
|
LLSD payload;
|
|
payload["agent_id"] = dest_agent;
|
|
payload["item_id"] = inv_item->getUUID();
|
|
payload["session_id"] = session_id;
|
|
payload["d&d_dest"] = dest.asString();
|
|
LLNotificationsUtil::add("ShareItemsConfirmation", substitutions, payload, &give_inventory_cb);
|
|
}
|
|
|
|
static void get_name_cb(const LLUUID& id,
|
|
const LLAvatarName& av_name,
|
|
LLInventoryObject* inv_obj,
|
|
const LLSD& dest,
|
|
const LLUUID& dest_agent)
|
|
{
|
|
show_object_sharing_confirmation(av_name.getUserName(),
|
|
inv_obj,
|
|
dest,
|
|
id,
|
|
LLUUID::null);
|
|
}
|
|
|
|
// function used as drag-and-drop handler for simple agent give inventory requests
|
|
//static
|
|
bool LLToolDragAndDrop::handleGiveDragAndDrop(LLUUID dest_agent, LLUUID session_id, bool drop,
|
|
EDragAndDropType cargo_type,
|
|
void* cargo_data,
|
|
EAcceptance* accept,
|
|
const LLSD& dest)
|
|
{
|
|
// check the type
|
|
switch(cargo_type)
|
|
{
|
|
case DAD_TEXTURE:
|
|
case DAD_SOUND:
|
|
case DAD_LANDMARK:
|
|
case DAD_SCRIPT:
|
|
case DAD_OBJECT:
|
|
case DAD_NOTECARD:
|
|
case DAD_CLOTHING:
|
|
case DAD_BODYPART:
|
|
case DAD_ANIMATION:
|
|
case DAD_GESTURE:
|
|
case DAD_CALLINGCARD:
|
|
case DAD_MESH:
|
|
case DAD_CATEGORY:
|
|
case DAD_SETTINGS:
|
|
case DAD_MATERIAL:
|
|
{
|
|
LLInventoryObject* inv_obj = (LLInventoryObject*)cargo_data;
|
|
if(gInventory.getCategory(inv_obj->getUUID()) || (gInventory.getItem(inv_obj->getUUID())
|
|
&& LLGiveInventory::isInventoryGiveAcceptable(dynamic_cast<LLInventoryItem*>(inv_obj))))
|
|
{
|
|
// *TODO: get multiple object transfers working
|
|
*accept = ACCEPT_YES_COPY_SINGLE;
|
|
if(drop)
|
|
{
|
|
LLIMModel::LLIMSession * session = LLIMModel::instance().findIMSession(session_id);
|
|
|
|
// If no IM session found get the destination agent's name by id.
|
|
if (NULL == session)
|
|
{
|
|
LLAvatarName av_name;
|
|
|
|
// If destination agent's name is found in cash proceed to showing the confirmation dialog.
|
|
// Otherwise set up a callback to show the dialog when the name arrives.
|
|
if (LLAvatarNameCache::get(dest_agent, &av_name))
|
|
{
|
|
show_object_sharing_confirmation(av_name.getUserName(), inv_obj, dest, dest_agent, LLUUID::null);
|
|
}
|
|
else
|
|
{
|
|
LLAvatarNameCache::get(dest_agent, boost::bind(&get_name_cb, _1, _2, inv_obj, dest, dest_agent));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
std::string dest_name = session->mName;
|
|
LLAvatarName av_name;
|
|
if(LLAvatarNameCache::get(dest_agent, &av_name))
|
|
{
|
|
dest_name = av_name.getCompleteName();
|
|
}
|
|
// If an IM session with destination agent is found item offer will be logged in this session.
|
|
show_object_sharing_confirmation(dest_name, inv_obj, dest, dest_agent, session_id);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// It's not in the user's inventory (it's probably
|
|
// in an object's contents), so disallow dragging
|
|
// it here. You can't give something you don't
|
|
// yet have.
|
|
*accept = ACCEPT_NO;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
*accept = ACCEPT_NO;
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
///
|
|
/// Methods called in the drag & drop array
|
|
///
|
|
|
|
EAcceptance LLToolDragAndDrop::dad3dNULL(
|
|
LLViewerObject*, S32, MASK, bool)
|
|
{
|
|
LL_DEBUGS() << "LLToolDragAndDrop::dad3dNULL()" << LL_ENDL;
|
|
return ACCEPT_NO;
|
|
}
|
|
|
|
EAcceptance LLToolDragAndDrop::dad3dRezAttachmentFromInv(
|
|
LLViewerObject* obj, S32 face, MASK mask, bool drop)
|
|
{
|
|
LL_DEBUGS() << "LLToolDragAndDrop::dad3dRezAttachmentFromInv()" << LL_ENDL;
|
|
// must be in the user's inventory
|
|
if(mSource != SOURCE_AGENT && mSource != SOURCE_LIBRARY)
|
|
{
|
|
return ACCEPT_NO;
|
|
}
|
|
|
|
LLViewerInventoryItem* item;
|
|
LLViewerInventoryCategory* cat;
|
|
locateInventory(item, cat);
|
|
if (!item || !item->isFinished()) return ACCEPT_NO;
|
|
|
|
// must not be in the trash
|
|
const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
|
|
if( gInventory.isObjectDescendentOf( item->getUUID(), trash_id ) )
|
|
{
|
|
return ACCEPT_NO;
|
|
}
|
|
|
|
// must not be already wearing it
|
|
LLVOAvatarSelf* avatar = gAgentAvatarp;
|
|
if( !avatar || avatar->isWearingAttachment(item->getUUID()) )
|
|
{
|
|
return ACCEPT_NO;
|
|
}
|
|
|
|
const LLUUID &outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX);
|
|
if(outbox_id.notNull() && gInventory.isObjectDescendentOf(item->getUUID(), outbox_id))
|
|
{
|
|
// Legacy
|
|
return ACCEPT_NO;
|
|
}
|
|
|
|
|
|
if( drop )
|
|
{
|
|
if(mSource == SOURCE_LIBRARY)
|
|
{
|
|
LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(rez_attachment_cb, _1, (LLViewerJointAttachment*)0));
|
|
copy_inventory_item(
|
|
gAgent.getID(),
|
|
item->getPermissions().getOwner(),
|
|
item->getUUID(),
|
|
LLUUID::null,
|
|
std::string(),
|
|
cb);
|
|
}
|
|
else
|
|
{
|
|
rez_attachment(item, 0);
|
|
}
|
|
}
|
|
return ACCEPT_YES_SINGLE;
|
|
}
|
|
|
|
|
|
EAcceptance LLToolDragAndDrop::dad3dRezObjectOnLand(
|
|
LLViewerObject* obj, S32 face, MASK mask, bool drop)
|
|
{
|
|
if (mSource == SOURCE_WORLD)
|
|
{
|
|
return dad3dRezFromObjectOnLand(obj, face, mask, drop);
|
|
}
|
|
|
|
LL_DEBUGS() << "LLToolDragAndDrop::dad3dRezObjectOnLand()" << LL_ENDL;
|
|
LLViewerInventoryItem* item;
|
|
LLViewerInventoryCategory* cat;
|
|
locateInventory(item, cat);
|
|
if (!item || !item->isFinished()) return ACCEPT_NO;
|
|
|
|
LLVOAvatarSelf* my_avatar = gAgentAvatarp;
|
|
if( !my_avatar || my_avatar->isWearingAttachment( item->getUUID() ) )
|
|
{
|
|
return ACCEPT_NO;
|
|
}
|
|
|
|
EAcceptance accept;
|
|
bool remove_inventory;
|
|
|
|
// Get initial settings based on shift key
|
|
if (mask & MASK_SHIFT)
|
|
{
|
|
// For now, always make copy
|
|
//accept = ACCEPT_YES_SINGLE;
|
|
//remove_inventory = true;
|
|
accept = ACCEPT_YES_COPY_SINGLE;
|
|
remove_inventory = false;
|
|
}
|
|
else
|
|
{
|
|
accept = ACCEPT_YES_COPY_SINGLE;
|
|
remove_inventory = false;
|
|
}
|
|
|
|
// check if the item can be copied. If not, send that to the sim
|
|
// which will remove the inventory item.
|
|
if(!item->getPermissions().allowCopyBy(gAgent.getID()))
|
|
{
|
|
accept = ACCEPT_YES_SINGLE;
|
|
remove_inventory = true;
|
|
}
|
|
|
|
// Check if it's in the trash.
|
|
const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
|
|
if(gInventory.isObjectDescendentOf(item->getUUID(), trash_id))
|
|
{
|
|
accept = ACCEPT_YES_SINGLE;
|
|
}
|
|
|
|
if(drop)
|
|
{
|
|
dropObject(obj, true, false, remove_inventory);
|
|
}
|
|
|
|
return accept;
|
|
}
|
|
|
|
EAcceptance LLToolDragAndDrop::dad3dRezObjectOnObject(
|
|
LLViewerObject* obj, S32 face, MASK mask, bool drop)
|
|
{
|
|
// handle objects coming from object inventory
|
|
if (mSource == SOURCE_WORLD)
|
|
{
|
|
return dad3dRezFromObjectOnObject(obj, face, mask, drop);
|
|
}
|
|
|
|
LL_DEBUGS() << "LLToolDragAndDrop::dad3dRezObjectOnObject()" << LL_ENDL;
|
|
LLViewerInventoryItem* item;
|
|
LLViewerInventoryCategory* cat;
|
|
locateInventory(item, cat);
|
|
if (!item || !item->isFinished()) return ACCEPT_NO;
|
|
LLVOAvatarSelf* my_avatar = gAgentAvatarp;
|
|
if( !my_avatar || my_avatar->isWearingAttachment( item->getUUID() ) )
|
|
{
|
|
return ACCEPT_NO;
|
|
}
|
|
|
|
if((mask & MASK_CONTROL))
|
|
{
|
|
// *HACK: In order to resolve SL-22177, we need to block drags
|
|
// from notecards and objects onto other objects.
|
|
if(mSource == SOURCE_NOTECARD)
|
|
{
|
|
return ACCEPT_NO;
|
|
}
|
|
|
|
EAcceptance rv = willObjectAcceptInventory(obj, item);
|
|
if(drop && (ACCEPT_YES_SINGLE <= rv))
|
|
{
|
|
dropInventory(obj, item, mSource, mSourceID);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
EAcceptance accept;
|
|
bool remove_inventory;
|
|
|
|
if (mask & MASK_SHIFT)
|
|
{
|
|
// For now, always make copy
|
|
//accept = ACCEPT_YES_SINGLE;
|
|
//remove_inventory = true;
|
|
accept = ACCEPT_YES_COPY_SINGLE;
|
|
remove_inventory = false;
|
|
}
|
|
else
|
|
{
|
|
accept = ACCEPT_YES_COPY_SINGLE;
|
|
remove_inventory = false;
|
|
}
|
|
|
|
// check if the item can be copied. If not, send that to the sim
|
|
// which will remove the inventory item.
|
|
if(!item->getPermissions().allowCopyBy(gAgent.getID()))
|
|
{
|
|
accept = ACCEPT_YES_SINGLE;
|
|
remove_inventory = true;
|
|
}
|
|
|
|
// Check if it's in the trash.
|
|
const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
|
|
if(gInventory.isObjectDescendentOf(item->getUUID(), trash_id))
|
|
{
|
|
accept = ACCEPT_YES_SINGLE;
|
|
remove_inventory = true;
|
|
}
|
|
|
|
if(drop)
|
|
{
|
|
dropObject(obj, false, false, remove_inventory);
|
|
}
|
|
|
|
return accept;
|
|
}
|
|
|
|
EAcceptance LLToolDragAndDrop::dad3dRezScript(
|
|
LLViewerObject* obj, S32 face, MASK mask, bool drop)
|
|
{
|
|
LL_DEBUGS() << "LLToolDragAndDrop::dad3dRezScript()" << LL_ENDL;
|
|
|
|
// *HACK: In order to resolve SL-22177, we need to block drags
|
|
// from notecards and objects onto other objects.
|
|
if((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource))
|
|
{
|
|
return ACCEPT_NO;
|
|
}
|
|
|
|
LLViewerInventoryItem* item;
|
|
LLViewerInventoryCategory* cat;
|
|
locateInventory(item, cat);
|
|
if (!item || !item->isFinished()) return ACCEPT_NO;
|
|
EAcceptance rv = willObjectAcceptInventory(obj, item);
|
|
if(drop && (ACCEPT_YES_SINGLE <= rv))
|
|
{
|
|
// rez in the script active by default, rez in inactive if the
|
|
// control key is being held down.
|
|
bool active = ((mask & MASK_CONTROL) == 0);
|
|
|
|
LLViewerObject* root_object = obj;
|
|
if (obj && obj->getParent())
|
|
{
|
|
LLViewerObject* parent_obj = (LLViewerObject*)obj->getParent();
|
|
if (!parent_obj->isAvatar())
|
|
{
|
|
root_object = parent_obj;
|
|
}
|
|
}
|
|
|
|
dropScript(root_object, item, active, mSource, mSourceID);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
EAcceptance LLToolDragAndDrop::dad3dApplyToObject(
|
|
LLViewerObject* obj, S32 face, MASK mask, bool drop, EDragAndDropType cargo_type)
|
|
{
|
|
LL_DEBUGS() << "LLToolDragAndDrop::dad3dApplyToObject()" << LL_ENDL;
|
|
|
|
// *HACK: In order to resolve SL-22177, we need to block drags
|
|
// from notecards and objects onto other objects.
|
|
if((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource))
|
|
{
|
|
return ACCEPT_NO;
|
|
}
|
|
|
|
LLViewerInventoryItem* item;
|
|
LLViewerInventoryCategory* cat;
|
|
locateInventory(item, cat);
|
|
if (!item || !item->isFinished()) return ACCEPT_NO;
|
|
LLPermissions item_permissions = item->getPermissions();
|
|
EAcceptance rv = willObjectAcceptInventory(obj, item);
|
|
if((mask & MASK_CONTROL))
|
|
{
|
|
if((ACCEPT_YES_SINGLE <= rv) && drop)
|
|
{
|
|
dropInventory(obj, item, mSource, mSourceID);
|
|
}
|
|
return rv;
|
|
}
|
|
if(!obj->permModify())
|
|
{
|
|
return ACCEPT_NO_LOCKED;
|
|
}
|
|
|
|
if (cargo_type == DAD_TEXTURE && (mask & MASK_ALT) == 0)
|
|
{
|
|
bool has_non_pbr_faces = false;
|
|
if ((mask & MASK_SHIFT))
|
|
{
|
|
S32 num_faces = obj->getNumTEs();
|
|
for (S32 face = 0; face < num_faces; face++)
|
|
{
|
|
if (obj->getRenderMaterialID(face).isNull())
|
|
{
|
|
has_non_pbr_faces = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
has_non_pbr_faces = obj->getRenderMaterialID(face).isNull();
|
|
}
|
|
|
|
if (!has_non_pbr_faces)
|
|
{
|
|
// Only pbr faces selected, texture will be added to an override
|
|
// Overrides require textures to be copy and transfer free
|
|
bool allow_adding_to_override = item_permissions.allowOperationBy(PERM_COPY, gAgent.getID());
|
|
allow_adding_to_override &= item_permissions.allowOperationBy(PERM_TRANSFER, gAgent.getID());
|
|
if (!allow_adding_to_override) return ACCEPT_NO;
|
|
}
|
|
}
|
|
|
|
if(drop && (ACCEPT_YES_SINGLE <= rv))
|
|
{
|
|
if (cargo_type == DAD_TEXTURE)
|
|
{
|
|
bool all_faces = mask & MASK_SHIFT;
|
|
bool remove_pbr = mask & MASK_ALT;
|
|
if (item_permissions.allowOperationBy(PERM_COPY, gAgent.getID()))
|
|
{
|
|
dropTexture(obj, face, item, mSource, mSourceID, all_faces, remove_pbr);
|
|
}
|
|
else
|
|
{
|
|
ESource source = mSource;
|
|
LLUUID source_id = mSourceID;
|
|
LLNotificationsUtil::add("ApplyInventoryToObject", LLSD(), LLSD(), [obj, face, item, source, source_id, all_faces, remove_pbr](const LLSD& notification, const LLSD& response)
|
|
{
|
|
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
|
|
// if Cancel pressed
|
|
if (option == 1)
|
|
{
|
|
return;
|
|
}
|
|
dropTexture(obj, face, item, source, source_id, all_faces, remove_pbr);
|
|
});
|
|
}
|
|
}
|
|
else if (cargo_type == DAD_MATERIAL)
|
|
{
|
|
bool all_faces = mask & MASK_SHIFT;
|
|
if (item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID()))
|
|
{
|
|
dropMaterial(obj, face, item, mSource, mSourceID, all_faces);
|
|
}
|
|
else
|
|
{
|
|
ESource source = mSource;
|
|
LLUUID source_id = mSourceID;
|
|
LLNotificationsUtil::add("ApplyInventoryToObject", LLSD(), LLSD(), [obj, face, item, source, source_id, all_faces](const LLSD& notification, const LLSD& response)
|
|
{
|
|
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
|
|
// if Cancel pressed
|
|
if (option == 1)
|
|
{
|
|
return;
|
|
}
|
|
dropMaterial(obj, face, item, source, source_id, all_faces);
|
|
});
|
|
}
|
|
}
|
|
else if (cargo_type == DAD_MESH)
|
|
{
|
|
dropMesh(obj, item, mSource, mSourceID);
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS() << "unsupported asset type" << LL_ENDL;
|
|
}
|
|
|
|
// VEFFECT: SetTexture
|
|
LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, true);
|
|
effectp->setSourceObject(gAgentAvatarp);
|
|
effectp->setTargetObject(obj);
|
|
effectp->setDuration(LL_HUD_DUR_SHORT);
|
|
effectp->setColor(LLColor4U(gAgent.getEffectColor()));
|
|
}
|
|
|
|
// enable multi-drop, although last texture will win
|
|
return ACCEPT_YES_MULTI;
|
|
}
|
|
|
|
|
|
EAcceptance LLToolDragAndDrop::dad3dTextureObject(
|
|
LLViewerObject* obj, S32 face, MASK mask, bool drop)
|
|
{
|
|
return dad3dApplyToObject(obj, face, mask, drop, DAD_TEXTURE);
|
|
}
|
|
|
|
EAcceptance LLToolDragAndDrop::dad3dMaterialObject(
|
|
LLViewerObject* obj, S32 face, MASK mask, bool drop)
|
|
{
|
|
return dad3dApplyToObject(obj, face, mask, drop, DAD_MATERIAL);
|
|
}
|
|
|
|
EAcceptance LLToolDragAndDrop::dad3dMeshObject(
|
|
LLViewerObject* obj, S32 face, MASK mask, bool drop)
|
|
{
|
|
return dad3dApplyToObject(obj, face, mask, drop, DAD_MESH);
|
|
}
|
|
|
|
|
|
/*
|
|
EAcceptance LLToolDragAndDrop::dad3dTextureSelf(
|
|
LLViewerObject* obj, S32 face, MASK mask, bool drop)
|
|
{
|
|
LL_DEBUGS() << "LLToolDragAndDrop::dad3dTextureAvatar()" << LL_ENDL;
|
|
if(drop)
|
|
{
|
|
if( !(mask & MASK_SHIFT) )
|
|
{
|
|
dropTextureOneFaceAvatar( (LLVOAvatar*)obj, face, (LLInventoryItem*)mCargoData);
|
|
}
|
|
}
|
|
return (mask & MASK_SHIFT) ? ACCEPT_NO : ACCEPT_YES_SINGLE;
|
|
}
|
|
*/
|
|
|
|
EAcceptance LLToolDragAndDrop::dad3dWearItem(
|
|
LLViewerObject* obj, S32 face, MASK mask, bool drop)
|
|
{
|
|
LL_DEBUGS() << "LLToolDragAndDrop::dad3dWearItem()" << LL_ENDL;
|
|
LLViewerInventoryItem* item;
|
|
LLViewerInventoryCategory* cat;
|
|
locateInventory(item, cat);
|
|
if (!item || !item->isFinished()) return ACCEPT_NO;
|
|
|
|
if(mSource == SOURCE_AGENT || mSource == SOURCE_LIBRARY)
|
|
{
|
|
// it's in the agent inventory
|
|
const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
|
|
if( gInventory.isObjectDescendentOf( item->getUUID(), trash_id ) )
|
|
{
|
|
return ACCEPT_NO;
|
|
}
|
|
|
|
if( drop )
|
|
{
|
|
// TODO: investigate wearables may not be loaded at this point EXT-8231
|
|
|
|
LLAppearanceMgr::instance().wearItemOnAvatar(item->getUUID(),true, !(mask & MASK_CONTROL));
|
|
}
|
|
return ACCEPT_YES_MULTI;
|
|
}
|
|
else
|
|
{
|
|
// TODO: copy/move item to avatar's inventory and then wear it.
|
|
return ACCEPT_NO;
|
|
}
|
|
}
|
|
|
|
EAcceptance LLToolDragAndDrop::dad3dActivateGesture(
|
|
LLViewerObject* obj, S32 face, MASK mask, bool drop)
|
|
{
|
|
LL_DEBUGS() << "LLToolDragAndDrop::dad3dActivateGesture()" << LL_ENDL;
|
|
LLViewerInventoryItem* item;
|
|
LLViewerInventoryCategory* cat;
|
|
locateInventory(item, cat);
|
|
if (!item || !item->isFinished()) return ACCEPT_NO;
|
|
|
|
if(mSource == SOURCE_AGENT || mSource == SOURCE_LIBRARY)
|
|
{
|
|
// it's in the agent inventory
|
|
const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
|
|
if( gInventory.isObjectDescendentOf( item->getUUID(), trash_id ) )
|
|
{
|
|
return ACCEPT_NO;
|
|
}
|
|
|
|
if( drop )
|
|
{
|
|
LLUUID item_id;
|
|
if(mSource == SOURCE_LIBRARY)
|
|
{
|
|
// create item based on that one, and put it on if that
|
|
// was a success.
|
|
LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(activate_gesture_cb);
|
|
copy_inventory_item(
|
|
gAgent.getID(),
|
|
item->getPermissions().getOwner(),
|
|
item->getUUID(),
|
|
LLUUID::null,
|
|
std::string(),
|
|
cb);
|
|
}
|
|
else
|
|
{
|
|
LLGestureMgr::instance().activateGesture(item->getUUID());
|
|
gInventory.updateItem(item);
|
|
gInventory.notifyObservers();
|
|
}
|
|
}
|
|
return ACCEPT_YES_MULTI;
|
|
}
|
|
else
|
|
{
|
|
return ACCEPT_NO;
|
|
}
|
|
}
|
|
|
|
EAcceptance LLToolDragAndDrop::dad3dWearCategory(
|
|
LLViewerObject* obj, S32 face, MASK mask, bool drop)
|
|
{
|
|
LL_DEBUGS() << "LLToolDragAndDrop::dad3dWearCategory()" << LL_ENDL;
|
|
LLViewerInventoryItem* item;
|
|
LLViewerInventoryCategory* category;
|
|
locateInventory(item, category);
|
|
if(!category) return ACCEPT_NO;
|
|
|
|
if (drop)
|
|
{
|
|
// TODO: investigate wearables may not be loaded at this point EXT-8231
|
|
}
|
|
|
|
U32 max_items = gSavedSettings.getU32("WearFolderLimit");
|
|
LLInventoryModel::cat_array_t cats;
|
|
LLInventoryModel::item_array_t items;
|
|
LLFindWearablesEx not_worn(/*is_worn=*/ false, /*include_body_parts=*/ false);
|
|
gInventory.collectDescendentsIf(category->getUUID(),
|
|
cats,
|
|
items,
|
|
LLInventoryModel::EXCLUDE_TRASH,
|
|
not_worn);
|
|
if (items.size() > max_items)
|
|
{
|
|
LLStringUtil::format_map_t args;
|
|
args["AMOUNT"] = llformat("%d", max_items);
|
|
mCustomMsg = LLTrans::getString("TooltipTooManyWearables",args);
|
|
return ACCEPT_NO_CUSTOM;
|
|
}
|
|
|
|
if (mSource == SOURCE_AGENT)
|
|
{
|
|
const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
|
|
if (gInventory.isObjectDescendentOf(category->getUUID(), trash_id))
|
|
{
|
|
return ACCEPT_NO;
|
|
}
|
|
|
|
const LLUUID &outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX);
|
|
if (outbox_id.notNull() && gInventory.isObjectDescendentOf(category->getUUID(), outbox_id))
|
|
{
|
|
// Legacy
|
|
return ACCEPT_NO;
|
|
}
|
|
|
|
if (drop)
|
|
{
|
|
LLAppearanceMgr::instance().wearInventoryCategory(category, false, mask & MASK_SHIFT);
|
|
}
|
|
|
|
return ACCEPT_YES_MULTI;
|
|
}
|
|
|
|
if (mSource == SOURCE_LIBRARY)
|
|
{
|
|
if (drop)
|
|
{
|
|
LLAppearanceMgr::instance().wearInventoryCategory(category, true, false);
|
|
}
|
|
|
|
return ACCEPT_YES_MULTI;
|
|
}
|
|
|
|
// TODO: copy/move category to avatar's inventory and then wear it.
|
|
return ACCEPT_NO;
|
|
}
|
|
|
|
|
|
EAcceptance LLToolDragAndDrop::dad3dUpdateInventory(
|
|
LLViewerObject* obj, S32 face, MASK mask, bool drop)
|
|
{
|
|
LL_DEBUGS() << "LLToolDragAndDrop::dadUpdateInventory()" << LL_ENDL;
|
|
|
|
// *HACK: In order to resolve SL-22177, we need to block drags
|
|
// from notecards and objects onto other objects.
|
|
if((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource))
|
|
{
|
|
return ACCEPT_NO;
|
|
}
|
|
|
|
LLViewerInventoryItem* item;
|
|
LLViewerInventoryCategory* cat;
|
|
locateInventory(item, cat);
|
|
if (!item || !item->isFinished())
|
|
return ACCEPT_NO;
|
|
|
|
LLViewerObject* root_object = obj;
|
|
if (obj && obj->getParent())
|
|
{
|
|
LLViewerObject* parent_obj = (LLViewerObject*)obj->getParent();
|
|
if (!parent_obj->isAvatar())
|
|
{
|
|
root_object = parent_obj;
|
|
}
|
|
}
|
|
|
|
EAcceptance rv = willObjectAcceptInventory(root_object, item);
|
|
if (root_object && drop && (ACCEPT_YES_COPY_SINGLE <= rv))
|
|
{
|
|
dropInventory(root_object, item, mSource, mSourceID);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
bool LLToolDragAndDrop::dadUpdateInventory(LLViewerObject* obj, bool drop)
|
|
{
|
|
EAcceptance rv = dad3dUpdateInventory(obj, -1, MASK_NONE, drop);
|
|
return rv >= ACCEPT_YES_COPY_SINGLE;
|
|
}
|
|
|
|
EAcceptance LLToolDragAndDrop::dad3dUpdateInventoryCategory(
|
|
LLViewerObject* obj, S32 face, MASK mask, bool drop)
|
|
{
|
|
LL_DEBUGS() << "LLToolDragAndDrop::dad3dUpdateInventoryCategory()" << LL_ENDL;
|
|
if (obj == NULL)
|
|
{
|
|
LL_WARNS() << "obj is NULL; aborting func with ACCEPT_NO" << LL_ENDL;
|
|
return ACCEPT_NO;
|
|
}
|
|
|
|
if ((mSource != SOURCE_AGENT) && (mSource != SOURCE_LIBRARY))
|
|
{
|
|
return ACCEPT_NO;
|
|
}
|
|
if (obj->isAttachment())
|
|
{
|
|
return ACCEPT_NO_LOCKED;
|
|
}
|
|
|
|
LLViewerInventoryItem* item = NULL;
|
|
LLViewerInventoryCategory* cat = NULL;
|
|
locateInventory(item, cat);
|
|
if (!cat)
|
|
{
|
|
return ACCEPT_NO;
|
|
}
|
|
|
|
// Find all the items in the category
|
|
LLDroppableItem droppable(!obj->permYouOwner());
|
|
LLInventoryModel::cat_array_t cats;
|
|
LLInventoryModel::item_array_t items;
|
|
gInventory.collectDescendentsIf(cat->getUUID(),
|
|
cats,
|
|
items,
|
|
LLInventoryModel::EXCLUDE_TRASH,
|
|
droppable);
|
|
cats.push_back(cat);
|
|
if (droppable.countNoCopy() > 0)
|
|
{
|
|
LL_WARNS() << "*** Need to confirm this step" << LL_ENDL;
|
|
}
|
|
LLViewerObject* root_object = obj;
|
|
if (obj->getParent())
|
|
{
|
|
LLViewerObject* parent_obj = (LLViewerObject*)obj->getParent();
|
|
if (!parent_obj->isAvatar())
|
|
{
|
|
root_object = parent_obj;
|
|
}
|
|
}
|
|
|
|
EAcceptance rv = ACCEPT_NO;
|
|
|
|
// Check for accept
|
|
for (LLInventoryModel::cat_array_t::const_iterator cat_iter = cats.begin();
|
|
cat_iter != cats.end();
|
|
++cat_iter)
|
|
{
|
|
const LLViewerInventoryCategory *cat = (*cat_iter);
|
|
rv = gInventory.isCategoryComplete(cat->getUUID()) ? ACCEPT_YES_MULTI : ACCEPT_NO;
|
|
if(rv < ACCEPT_YES_SINGLE)
|
|
{
|
|
LL_DEBUGS() << "Category " << cat->getUUID() << "is not complete." << LL_ENDL;
|
|
break;
|
|
}
|
|
}
|
|
if (ACCEPT_YES_COPY_SINGLE <= rv)
|
|
{
|
|
for (LLInventoryModel::item_array_t::const_iterator item_iter = items.begin();
|
|
item_iter != items.end();
|
|
++item_iter)
|
|
{
|
|
LLViewerInventoryItem *item = (*item_iter);
|
|
/*
|
|
// Pass the base objects, not the links.
|
|
if (item && item->getIsLinkType())
|
|
{
|
|
item = item->getLinkedItem();
|
|
(*item_iter) = item;
|
|
}
|
|
*/
|
|
rv = willObjectAcceptInventory(root_object, item, DAD_CATEGORY);
|
|
if (rv < ACCEPT_YES_COPY_SINGLE)
|
|
{
|
|
LL_DEBUGS() << "Object will not accept " << item->getUUID() << LL_ENDL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If every item is accepted, send it on
|
|
if (drop && (ACCEPT_YES_COPY_SINGLE <= rv))
|
|
{
|
|
uuid_vec_t ids;
|
|
for (LLInventoryModel::item_array_t::const_iterator item_iter = items.begin();
|
|
item_iter != items.end();
|
|
++item_iter)
|
|
{
|
|
const LLViewerInventoryItem *item = (*item_iter);
|
|
ids.push_back(item->getUUID());
|
|
}
|
|
LLCategoryDropObserver* dropper = new LLCategoryDropObserver(ids, obj->getID(), mSource);
|
|
dropper->startFetch();
|
|
if (dropper->isFinished())
|
|
{
|
|
dropper->done();
|
|
}
|
|
else
|
|
{
|
|
gInventory.addObserver(dropper);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
EAcceptance LLToolDragAndDrop::dad3dRezCategoryOnObject(
|
|
LLViewerObject* obj, S32 face, MASK mask, bool drop)
|
|
{
|
|
if ((mask & MASK_CONTROL))
|
|
{
|
|
return dad3dUpdateInventoryCategory(obj, face, mask, drop);
|
|
}
|
|
else
|
|
{
|
|
return ACCEPT_NO;
|
|
}
|
|
}
|
|
|
|
|
|
bool LLToolDragAndDrop::dadUpdateInventoryCategory(LLViewerObject* obj,
|
|
bool drop)
|
|
{
|
|
EAcceptance rv = dad3dUpdateInventoryCategory(obj, -1, MASK_NONE, drop);
|
|
return (rv >= ACCEPT_YES_COPY_SINGLE);
|
|
}
|
|
|
|
EAcceptance LLToolDragAndDrop::dad3dGiveInventoryObject(
|
|
LLViewerObject* obj, S32 face, MASK mask, bool drop)
|
|
{
|
|
LL_DEBUGS() << "LLToolDragAndDrop::dad3dGiveInventoryObject()" << LL_ENDL;
|
|
|
|
// item has to be in agent inventory.
|
|
if(mSource != SOURCE_AGENT) return ACCEPT_NO;
|
|
|
|
// find the item now.
|
|
LLViewerInventoryItem* item;
|
|
LLViewerInventoryCategory* cat;
|
|
locateInventory(item, cat);
|
|
if (!item || !item->isFinished()) return ACCEPT_NO;
|
|
if(!item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()))
|
|
{
|
|
// cannot give away no-transfer objects
|
|
return ACCEPT_NO;
|
|
}
|
|
LLVOAvatarSelf* avatar = gAgentAvatarp;
|
|
if(avatar && avatar->isWearingAttachment( item->getUUID() ) )
|
|
{
|
|
// You can't give objects that are attached to you
|
|
return ACCEPT_NO;
|
|
}
|
|
if( obj && avatar )
|
|
{
|
|
if(drop)
|
|
{
|
|
LLGiveInventory::doGiveInventoryItem(obj->getID(), item );
|
|
}
|
|
// *TODO: deal with all the issues surrounding multi-object
|
|
// inventory transfers.
|
|
return ACCEPT_YES_SINGLE;
|
|
}
|
|
return ACCEPT_NO;
|
|
}
|
|
|
|
|
|
EAcceptance LLToolDragAndDrop::dad3dGiveInventory(
|
|
LLViewerObject* obj, S32 face, MASK mask, bool drop)
|
|
{
|
|
LL_DEBUGS() << "LLToolDragAndDrop::dad3dGiveInventory()" << LL_ENDL;
|
|
// item has to be in agent inventory.
|
|
if(mSource != SOURCE_AGENT) return ACCEPT_NO;
|
|
LLViewerInventoryItem* item;
|
|
LLViewerInventoryCategory* cat;
|
|
locateInventory(item, cat);
|
|
if (!item || !item->isFinished()) return ACCEPT_NO;
|
|
if (!LLGiveInventory::isInventoryGiveAcceptable(item))
|
|
{
|
|
return ACCEPT_NO;
|
|
}
|
|
if (drop && obj)
|
|
{
|
|
LLGiveInventory::doGiveInventoryItem(obj->getID(), item);
|
|
}
|
|
// *TODO: deal with all the issues surrounding multi-object
|
|
// inventory transfers.
|
|
return ACCEPT_YES_SINGLE;
|
|
}
|
|
|
|
EAcceptance LLToolDragAndDrop::dad3dGiveInventoryCategory(
|
|
LLViewerObject* obj, S32 face, MASK mask, bool drop)
|
|
{
|
|
LL_DEBUGS() << "LLToolDragAndDrop::dad3dGiveInventoryCategory()" << LL_ENDL;
|
|
if(drop && obj)
|
|
{
|
|
LLViewerInventoryItem* item;
|
|
LLViewerInventoryCategory* cat;
|
|
locateInventory(item, cat);
|
|
if(!cat) return ACCEPT_NO;
|
|
LLGiveInventory::doGiveInventoryCategory(obj->getID(), cat);
|
|
}
|
|
// *TODO: deal with all the issues surrounding multi-object
|
|
// inventory transfers.
|
|
return ACCEPT_YES_SINGLE;
|
|
}
|
|
|
|
|
|
EAcceptance LLToolDragAndDrop::dad3dRezFromObjectOnLand(
|
|
LLViewerObject* obj, S32 face, MASK mask, bool drop)
|
|
{
|
|
LL_DEBUGS() << "LLToolDragAndDrop::dad3dRezFromObjectOnLand()" << LL_ENDL;
|
|
LLViewerInventoryItem* item = NULL;
|
|
LLViewerInventoryCategory* cat = NULL;
|
|
locateInventory(item, cat);
|
|
if (!item || !item->isFinished()) return ACCEPT_NO;
|
|
|
|
if(!gAgent.allowOperation(PERM_COPY, item->getPermissions())
|
|
|| !item->getPermissions().allowTransferTo(LLUUID::null))
|
|
{
|
|
return ACCEPT_NO_LOCKED;
|
|
}
|
|
if(drop)
|
|
{
|
|
dropObject(obj, true, true, false);
|
|
}
|
|
return ACCEPT_YES_SINGLE;
|
|
}
|
|
|
|
EAcceptance LLToolDragAndDrop::dad3dRezFromObjectOnObject(
|
|
LLViewerObject* obj, S32 face, MASK mask, bool drop)
|
|
{
|
|
LL_DEBUGS() << "LLToolDragAndDrop::dad3dRezFromObjectOnObject()" << LL_ENDL;
|
|
LLViewerInventoryItem* item;
|
|
LLViewerInventoryCategory* cat;
|
|
locateInventory(item, cat);
|
|
if (!item || !item->isFinished()) return ACCEPT_NO;
|
|
if((mask & MASK_CONTROL))
|
|
{
|
|
// *HACK: In order to resolve SL-22177, we need to block drags
|
|
// from notecards and objects onto other objects.
|
|
return ACCEPT_NO;
|
|
|
|
// *HACK: uncomment this when appropriate
|
|
//EAcceptance rv = willObjectAcceptInventory(obj, item);
|
|
//if(drop && (ACCEPT_YES_SINGLE <= rv))
|
|
//{
|
|
// dropInventory(obj, item, mSource, mSourceID);
|
|
//}
|
|
//return rv;
|
|
}
|
|
if(!item->getPermissions().allowCopyBy(gAgent.getID(),
|
|
gAgent.getGroupID())
|
|
|| !item->getPermissions().allowTransferTo(LLUUID::null))
|
|
{
|
|
return ACCEPT_NO_LOCKED;
|
|
}
|
|
if(drop)
|
|
{
|
|
dropObject(obj, false, true, false);
|
|
}
|
|
return ACCEPT_YES_SINGLE;
|
|
}
|
|
|
|
EAcceptance LLToolDragAndDrop::dad3dCategoryOnLand(
|
|
LLViewerObject *obj, S32 face, MASK mask, bool drop)
|
|
{
|
|
return ACCEPT_NO;
|
|
/*
|
|
LL_DEBUGS() << "LLToolDragAndDrop::dad3dCategoryOnLand()" << LL_ENDL;
|
|
LLInventoryItem* item;
|
|
LLInventoryCategory* cat;
|
|
locateInventory(item, cat);
|
|
if(!cat) return ACCEPT_NO;
|
|
EAcceptance rv = ACCEPT_NO;
|
|
|
|
// find all the items in the category
|
|
LLViewerInventoryCategory::cat_array_t cats;
|
|
LLViewerInventoryItem::item_array_t items;
|
|
LLDropCopyableItems droppable;
|
|
gInventory.collectDescendentsIf(cat->getUUID(),
|
|
cats,
|
|
items,
|
|
LLInventoryModel::EXCLUDE_TRASH,
|
|
droppable);
|
|
if(items.size() > 0)
|
|
{
|
|
rv = ACCEPT_YES_SINGLE;
|
|
}
|
|
if((rv >= ACCEPT_YES_COPY_SINGLE) && drop)
|
|
{
|
|
createContainer(items, cat->getName());
|
|
return ACCEPT_NO;
|
|
}
|
|
return rv;
|
|
*/
|
|
}
|
|
|
|
|
|
// This is based on ALOT of copied, special-cased code
|
|
// This shortcuts alot of steps to make a basic object
|
|
// w/ an inventory and a special permissions set
|
|
EAcceptance LLToolDragAndDrop::dad3dAssetOnLand(
|
|
LLViewerObject *obj, S32 face, MASK mask, bool drop)
|
|
{
|
|
return ACCEPT_NO;
|
|
/*
|
|
LL_DEBUGS() << "LLToolDragAndDrop::dad3dAssetOnLand()" << LL_ENDL;
|
|
LLViewerInventoryCategory::cat_array_t cats;
|
|
LLViewerInventoryItem::item_array_t items;
|
|
LLViewerInventoryItem::item_array_t copyable_items;
|
|
locateMultipleInventory(items, cats);
|
|
if(!items.size()) return ACCEPT_NO;
|
|
EAcceptance rv = ACCEPT_NO;
|
|
for (S32 i = 0; i < items.size(); i++)
|
|
{
|
|
LLInventoryItem* item = items[i];
|
|
if(item->getPermissions().allowCopyBy(gAgent.getID()))
|
|
{
|
|
copyable_items.push_back(item);
|
|
rv = ACCEPT_YES_SINGLE;
|
|
}
|
|
}
|
|
|
|
if((rv >= ACCEPT_YES_COPY_SINGLE) && drop)
|
|
{
|
|
createContainer(copyable_items, NULL);
|
|
}
|
|
|
|
return rv;
|
|
*/
|
|
}
|
|
|
|
LLInventoryObject* LLToolDragAndDrop::locateInventory(
|
|
LLViewerInventoryItem*& item,
|
|
LLViewerInventoryCategory*& cat)
|
|
{
|
|
item = NULL;
|
|
cat = NULL;
|
|
|
|
if (mCargoIDs.empty()
|
|
|| (mSource == SOURCE_PEOPLE)) ///< There is no inventory item for people drag and drop.
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY))
|
|
{
|
|
// The object should be in user inventory.
|
|
item = (LLViewerInventoryItem*)gInventory.getItem(mCargoIDs[mCurItemIndex]);
|
|
cat = (LLViewerInventoryCategory*)gInventory.getCategory(mCargoIDs[mCurItemIndex]);
|
|
}
|
|
else if(mSource == SOURCE_WORLD)
|
|
{
|
|
// This object is in some task inventory somewhere.
|
|
LLViewerObject* obj = gObjectList.findObject(mSourceID);
|
|
if(obj)
|
|
{
|
|
if((mCargoTypes[mCurItemIndex] == DAD_CATEGORY)
|
|
|| (mCargoTypes[mCurItemIndex] == DAD_ROOT_CATEGORY))
|
|
{
|
|
cat = (LLViewerInventoryCategory*)obj->getInventoryObject(mCargoIDs[mCurItemIndex]);
|
|
}
|
|
else
|
|
{
|
|
item = (LLViewerInventoryItem*)obj->getInventoryObject(mCargoIDs[mCurItemIndex]);
|
|
}
|
|
}
|
|
}
|
|
else if(mSource == SOURCE_NOTECARD)
|
|
{
|
|
LLPreviewNotecard* preview = LLFloaterReg::findTypedInstance<LLPreviewNotecard>("preview_notecard", mSourceID);
|
|
if(preview)
|
|
{
|
|
item = (LLViewerInventoryItem*)preview->getDragItem();
|
|
}
|
|
}
|
|
else if(mSource == SOURCE_VIEWER)
|
|
{
|
|
item = (LLViewerInventoryItem*)gToolBarView->getDragItem();
|
|
}
|
|
|
|
if(item) return item;
|
|
if(cat) return cat;
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
LLInventoryObject* LLToolDragAndDrop::locateMultipleInventory(LLViewerInventoryCategory::cat_array_t& cats,
|
|
LLViewerInventoryItem::item_array_t& items)
|
|
{
|
|
if(mCargoIDs.size() == 0) return NULL;
|
|
if((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY))
|
|
{
|
|
// The object should be in user inventory.
|
|
for (S32 i = 0; i < mCargoIDs.size(); i++)
|
|
{
|
|
LLInventoryItem* item = gInventory.getItem(mCargoIDs[i]);
|
|
if (item)
|
|
{
|
|
items.push_back(item);
|
|
}
|
|
LLInventoryCategory* category = gInventory.getCategory(mCargoIDs[i]);
|
|
if (category)
|
|
{
|
|
cats.push_back(category);
|
|
}
|
|
}
|
|
}
|
|
else if(mSource == SOURCE_WORLD)
|
|
{
|
|
// This object is in some task inventory somewhere.
|
|
LLViewerObject* obj = gObjectList.findObject(mSourceID);
|
|
if(obj)
|
|
{
|
|
if((mCargoType == DAD_CATEGORY)
|
|
|| (mCargoType == DAD_ROOT_CATEGORY))
|
|
{
|
|
// The object should be in user inventory.
|
|
for (S32 i = 0; i < mCargoIDs.size(); i++)
|
|
{
|
|
LLInventoryCategory* category = (LLInventoryCategory*)obj->getInventoryObject(mCargoIDs[i]);
|
|
if (category)
|
|
{
|
|
cats.push_back(category);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (S32 i = 0; i < mCargoIDs.size(); i++)
|
|
{
|
|
LLInventoryItem* item = (LLInventoryItem*)obj->getInventoryObject(mCargoIDs[i]);
|
|
if (item)
|
|
{
|
|
items.push_back(item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(mSource == SOURCE_NOTECARD)
|
|
{
|
|
LLPreviewNotecard* card;
|
|
card = (LLPreviewNotecard*)LLPreview::find(mSourceID);
|
|
if(card)
|
|
{
|
|
items.push_back((LLInventoryItem*)card->getDragItem());
|
|
}
|
|
}
|
|
if(items.size()) return items[0];
|
|
if(cats.size()) return cats[0];
|
|
return NULL;
|
|
}
|
|
*/
|
|
|
|
// void LLToolDragAndDrop::createContainer(LLViewerInventoryItem::item_array_t &items, const char* preferred_name )
|
|
// {
|
|
// LL_WARNS() << "LLToolDragAndDrop::createContainer()" << LL_ENDL;
|
|
// return;
|
|
// }
|
|
|
|
|
|
// utility functions
|
|
|
|
void pack_permissions_slam(LLMessageSystem* msg, U32 flags, const LLPermissions& perms)
|
|
{
|
|
// CRUFT -- the server no longer pays attention to this data
|
|
U32 group_mask = perms.getMaskGroup();
|
|
U32 everyone_mask = perms.getMaskEveryone();
|
|
U32 next_owner_mask = perms.getMaskNextOwner();
|
|
|
|
msg->addU32Fast(_PREHASH_ItemFlags, flags);
|
|
msg->addU32Fast(_PREHASH_GroupMask, group_mask);
|
|
msg->addU32Fast(_PREHASH_EveryoneMask, everyone_mask);
|
|
msg->addU32Fast(_PREHASH_NextOwnerMask, next_owner_mask);
|
|
}
|