diff options
author | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
---|---|---|
committer | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
commit | 420b91db29485df39fd6e724e782c449158811cb (patch) | |
tree | b471a94563af914d3ed3edd3e856d21cb1b69945 /indra/newview/llinventorybridge.cpp |
Print done when done.
Diffstat (limited to 'indra/newview/llinventorybridge.cpp')
-rwxr-xr-x | indra/newview/llinventorybridge.cpp | 4405 |
1 files changed, 4405 insertions, 0 deletions
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp new file mode 100755 index 0000000000..ed9209935e --- /dev/null +++ b/indra/newview/llinventorybridge.cpp @@ -0,0 +1,4405 @@ +/** + * @file llinventorybridge.cpp + * @brief Implementation of the Inventory-Folder-View-Bridge classes. + * + * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "llviewerprecompiledheaders.h" + +#include <utility> // for std::pair<> + +#include "llinventoryview.h" +#include "llinventorybridge.h" + +#include "message.h" + +#include "llagent.h" +#include "llcallingcard.h" +#include "llcheckboxctrl.h" // for radio buttons +#include "llradiogroup.h" +#include "llspinctrl.h" +#include "lltextbox.h" +#include "llui.h" + +#include "llviewercontrol.h" +#include "llfirstuse.h" +#include "llfloateravatarinfo.h" +#include "llfloaterchat.h" +#include "llfloatercustomize.h" +#include "llfloaterproperties.h" +#include "llfloaterworldmap.h" +#include "llfocusmgr.h" +#include "llfolderview.h" +#include "llgesturemgr.h" +#include "lliconctrl.h" +#include "llinventorymodel.h" +#include "llinventoryclipboard.h" +#include "lllineeditor.h" +#include "llmenugl.h" +#include "llpreviewanim.h" +#include "llpreviewgesture.h" +#include "llpreviewlandmark.h" +#include "llpreviewnotecard.h" +#include "llpreviewscript.h" +#include "llpreviewsound.h" +#include "llpreviewtexture.h" +#include "llresmgr.h" +#include "llscrollcontainer.h" +#include "llimview.h" +#include "lltooldraganddrop.h" +#include "llviewerimagelist.h" +#include "llviewerinventory.h" +#include "llviewerobjectlist.h" +#include "llviewerwindow.h" +#include "llwearable.h" +#include "llwearablelist.h" +#include "viewer.h" +#include "llviewermessage.h" +#include "llviewerregion.h" +#include "lltabcontainer.h" +#include "llvieweruictrlfactory.h" +#include "llselectmgr.h" +#include "llfloateropenobject.h" + +// Helpers +// bug in busy count inc/dec right now, logic is complex... do we really need it? +void inc_busy_count() +{ +// gViewerWindow->getWindow()->incBusyCount(); +} +void dec_busy_count() +{ +// gViewerWindow->getWindow()->decBusyCount(); +} + +// Function declarations +struct LLWearableHoldingPattern; +void wear_inventory_category_on_avatar(LLInventoryCategory* category, BOOL append); +void wear_inventory_category_on_avatar_step2( BOOL proceed, void* userdata); +void wear_inventory_category_on_avatar_loop(LLWearable* wearable, void*); +void wear_inventory_category_on_avatar_step3(LLWearableHoldingPattern* holder, BOOL append); +void remove_inventory_category_from_avatar(LLInventoryCategory* category); +void remove_inventory_category_from_avatar_step2( BOOL proceed, void* userdata); +void move_task_inventory_callback(S32 option, void* user_data); +void confirm_replace_attachment_rez(S32 option, void* user_data); + +// TomY XUI: translate +const char* FIND_HINT = "Start typing to select an item by name"; +const char* NAME_SEARCH_DESC = "Find items whose name contains (leave blank for all):"; +const char* NEW_LSL_NAME = "New Script"; +const char* NEW_NOTECARD_NAME = "New Note"; +const char* NEW_GESTURE_NAME = "New Gesture"; + +const char* ICON_NAME[ICON_NAME_COUNT] = +{ + "inv_item_texture.tga", + "inv_item_sound.tga", + "inv_item_callingcard_online.tga", + "inv_item_callingcard_offline.tga", + "inv_item_landmark.tga", + "inv_item_landmark_visited.tga", + "inv_item_script.tga", + "inv_item_clothing.tga", + "inv_item_object.tga", + "inv_item_notecard.tga", + "inv_item_bodypart.tga", + "inv_item_snapshot.tga", + + "inv_item_shape.tga", + "inv_item_bodypart.tga", + "inv_item_hair.tga", + "inv_item_eyes.tga", + "inv_item_shirt.tga", + "inv_item_pants.tga", + "inv_item_shoes.tga", + "inv_item_socks.tga", + "inv_item_jacket.tga", + "inv_item_gloves.tga", + "inv_item_undershirt.tga", + "inv_item_underpants.tga", + "inv_item_skirt.tga", + + "inv_item_animation.tga", + "inv_item_gesture.tga", +}; + +struct LLWearInfo +{ + LLUUID mCategoryID; + BOOL mAppend; +}; + +BOOL gAddToOutfit = FALSE; + +// +=================================================+ +// | LLInvFVBridge | +// +=================================================+ + +const LLString& LLInvFVBridge::getName() const +{ + LLInventoryObject* obj = getInventoryObject(); + if(obj) + { + return obj->getName(); + } + return LLString::null; +} + +const LLString& LLInvFVBridge::getDisplayName() const +{ + return getName(); +} + +// Folders have full perms +PermissionMask LLInvFVBridge::getPermissionMask() const +{ + + return PERM_ALL; +} + +// Folders don't have creation dates. +U32 LLInvFVBridge::getCreationDate() const +{ + return 0; +} + +// Can be destoryed (or moved to trash) +BOOL LLInvFVBridge::isItemRemovable() +{ + LLInventoryModel* model = mInventoryPanel->getModel(); + if(!model) return FALSE; + if(model->isObjectDescendentOf(mUUID, gAgent.getInventoryRootID())) + { + return TRUE; + } + return FALSE; +} + +// Can be moved to another folder +BOOL LLInvFVBridge::isItemMovable() +{ + return TRUE; +} + +//FIXME: make sure this does the right thing +void LLInvFVBridge::showProperties() +{ + LLShowProps::showProperties(mUUID); +} + +void LLInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch) +{ + removeBatchNoCheck(batch); +} + +void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener*>& batch) +{ + // this method moves a bunch of items and folders to the trash. As + // per design guidelines for the inventory model, the message is + // built and the accounting is performed first. After all of that, + // we call LLInventoryModel::moveObject() to move everything + // around. + LLInvFVBridge* bridge; + LLInventoryModel* model = mInventoryPanel->getModel(); + if(!model) return; + LLMessageSystem* msg = gMessageSystem; + LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH); + LLViewerInventoryItem* item = NULL; + LLViewerInventoryCategory* cat = NULL; + std::vector<LLUUID> move_ids; + LLInventoryModel::update_map_t update; + bool start_new_message = true; + S32 count = batch.count(); + S32 i; + for(i = 0; i < count; ++i) + { + bridge = (LLInvFVBridge*)(batch.get(i)); + if(!bridge || !bridge->isItemRemovable()) continue; + item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); + if(item) + { + if(item->getParentUUID() == trash_id) continue; + move_ids.push_back(item->getUUID()); + LLPreview::hide(item->getUUID()); + --update[item->getParentUUID()]; + ++update[trash_id]; + if(start_new_message) + { + start_new_message = false; + msg->newMessageFast(_PREHASH_MoveInventoryItem); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->addBOOLFast(_PREHASH_Stamp, TRUE); + } + msg->nextBlockFast(_PREHASH_InventoryData); + msg->addUUIDFast(_PREHASH_ItemID, item->getUUID()); + msg->addUUIDFast(_PREHASH_FolderID, trash_id); + msg->addString("NewName", NULL); + if(msg->isSendFullFast(_PREHASH_InventoryData)) + { + start_new_message = true; + gAgent.sendReliableMessage(); + gInventory.accountForUpdate(update); + update.clear(); + } + } + } + if(!start_new_message) + { + start_new_message = true; + gAgent.sendReliableMessage(); + gInventory.accountForUpdate(update); + update.clear(); + } + for(i = 0; i < count; ++i) + { + bridge = (LLInvFVBridge*)(batch.get(i)); + if(!bridge || !bridge->isItemRemovable()) continue; + cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID()); + if(cat) + { + if(cat->getParentUUID() == trash_id) continue; + move_ids.push_back(cat->getUUID()); + --update[cat->getParentUUID()]; + ++update[trash_id]; + if(start_new_message) + { + start_new_message = false; + msg->newMessageFast(_PREHASH_MoveInventoryFolder); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->addBOOL("Stamp", TRUE); + } + msg->nextBlockFast(_PREHASH_InventoryData); + msg->addUUIDFast(_PREHASH_FolderID, cat->getUUID()); + msg->addUUIDFast(_PREHASH_ParentID, trash_id); + if(msg->isSendFullFast(_PREHASH_InventoryData)) + { + start_new_message = true; + gAgent.sendReliableMessage(); + gInventory.accountForUpdate(update); + update.clear(); + } + } + } + if(!start_new_message) + { + gAgent.sendReliableMessage(); + gInventory.accountForUpdate(update); + } + + // move everything. + std::vector<LLUUID>::iterator it = move_ids.begin(); + std::vector<LLUUID>::iterator end = move_ids.end(); + for(; it != end; ++it) + { + gInventory.moveObject((*it), trash_id); + } + + // notify inventory observers. + model->notifyObservers(); +} + +BOOL LLInvFVBridge::isClipboardPasteable() const +{ + LLInventoryModel* model = mInventoryPanel->getModel(); + if(!model) return FALSE; + BOOL is_agent_inventory = model->isObjectDescendentOf(mUUID, gAgent.getInventoryRootID()); + + if(LLInventoryClipboard::instance().hasContents() && is_agent_inventory) + { + return TRUE; + } + return FALSE; +} + +void hideContextEntries(LLMenuGL& menu, + const std::vector<LLString> &entries_to_show, + const std::vector<LLString> &disabled_entries) +{ + const LLView::child_list_t *list = menu.getChildList(); + + LLView::child_list_t::const_iterator itor; + for (itor = list->begin(); itor != list->end(); ++itor) + { + LLString name = (*itor)->getName(); + bool found = false; + std::vector<LLString>::const_iterator itor2; + for (itor2 = entries_to_show.begin(); itor2 != entries_to_show.end(); ++itor2) + { + if (*itor2 == name) + { + found = true; + } + } + if (!found) + { + (*itor)->setVisible(FALSE); + } + else + { + for (itor2 = disabled_entries.begin(); itor2 != disabled_entries.end(); ++itor2) + { + if (*itor2 == name) + { + (*itor)->setEnabled(FALSE); + } + } + } + } +} + +// Helper for commonly-used entries +void LLInvFVBridge::getClipboardEntries(bool show_asset_id, std::vector<LLString> &items, + std::vector<LLString> &disabled_items, U32 flags) +{ + items.push_back("Rename"); + if (!isItemRenameable() || (flags & FIRST_SELECTED_ITEM) == 0) + { + disabled_items.push_back("Rename"); + } + + if (show_asset_id) + { + items.push_back("Copy Asset UUID"); + if ( (! ( isItemPermissive() || gAgent.isGodlike() ) ) + || (flags & FIRST_SELECTED_ITEM) == 0) + { + disabled_items.push_back("Copy Asset UUID"); + } + } + + items.push_back("Copy Separator"); + + items.push_back("Copy"); + if (!isItemCopyable()) + { + disabled_items.push_back("Copy"); + } + + items.push_back("Paste"); + if (!isClipboardPasteable() || (flags & FIRST_SELECTED_ITEM) == 0) + { + disabled_items.push_back("Paste"); + } + + items.push_back("Paste Separator"); + + items.push_back("Delete"); + if (!isItemRemovable()) + { + disabled_items.push_back("Delete"); + } +} + +void LLInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + lldebugs << "LLInvFVBridge::buildContextMenu()" << llendl; + std::vector<LLString> items; + std::vector<LLString> disabled_items; + if(isInTrash()) + { + items.push_back("Purge Item"); + if (!isItemRemovable()) + { + disabled_items.push_back("Purge Item"); + } + items.push_back("Restore Item"); + } + else + { + items.push_back("Open"); + items.push_back("Properties"); + + getClipboardEntries(true, items, disabled_items, flags); + } + hideContextEntries(menu, items, disabled_items); +} + +//FIXME: remove this +BOOL LLInvFVBridge::startDrag(EDragAndDropType* type, LLUUID* id) +{ + BOOL rv = FALSE; + + LLInventoryObject* obj = getInventoryObject(); + + if(obj) + { + *type = LLAssetType::lookupDragAndDropType(obj->getType()); + if(*type == DAD_NONE) + { + return FALSE; + } + + *id = obj->getUUID(); + //object_ids.put(obj->getUUID()); + + if (*type == DAD_CATEGORY) + { + gInventory.startBackgroundFetch(obj->getUUID()); + } + + rv = TRUE; + } + + return rv; +} + +LLInventoryObject* LLInvFVBridge::getInventoryObject() const +{ + LLInventoryObject* obj = NULL; + LLInventoryModel* model = mInventoryPanel->getModel(); + if(model) + { + obj = (LLInventoryObject*)model->getObject(mUUID); + } + return obj; +} + +BOOL LLInvFVBridge::isInTrash() const +{ + LLInventoryModel* model = mInventoryPanel->getModel(); + if(!model) return FALSE; + LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH); + return model->isObjectDescendentOf(mUUID, trash_id); +} + +BOOL LLInvFVBridge::isAgentInventory() const +{ + LLInventoryModel* model = mInventoryPanel->getModel(); + if(!model) return FALSE; + if(gAgent.getInventoryRootID() == mUUID) return TRUE; + return model->isObjectDescendentOf(mUUID, gAgent.getInventoryRootID()); +} + +BOOL LLInvFVBridge::isItemPermissive() const +{ + return FALSE; +} + +// static +void LLInvFVBridge::changeItemParent(LLInventoryModel* model, + LLViewerInventoryItem* item, + const LLUUID& new_parent, + BOOL restamp) +{ + if(item->getParentUUID() != new_parent) + { + LLInventoryModel::update_list_t update; + LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1); + update.push_back(old_folder); + LLInventoryModel::LLCategoryUpdate new_folder(new_parent, 1); + update.push_back(new_folder); + gInventory.accountForUpdate(update); + + LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); + new_item->setParent(new_parent); + new_item->updateParentOnServer(restamp); + model->updateItem(new_item); + model->notifyObservers(); + } +} + +// static +void LLInvFVBridge::changeCategoryParent(LLInventoryModel* model, + LLViewerInventoryCategory* cat, + const LLUUID& new_parent, + BOOL restamp) +{ + if(cat->getParentUUID() != new_parent) + { + LLInventoryModel::update_list_t update; + LLInventoryModel::LLCategoryUpdate old_folder(cat->getParentUUID(), -1); + update.push_back(old_folder); + LLInventoryModel::LLCategoryUpdate new_folder(new_parent, 1); + update.push_back(new_folder); + gInventory.accountForUpdate(update); + + LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat); + new_cat->setParent(new_parent); + new_cat->updateParentOnServer(restamp); + model->updateCategory(new_cat); + model->notifyObservers(); + } +} + + +const char* safe_inv_type_lookup(LLInventoryType::EType inv_type) +{ + const char* rv = LLInventoryType::lookup(inv_type); + if(!rv) + { + const char* INVALID_TYPE = "<invalid>"; + rv = INVALID_TYPE; + } + return rv; +} + +LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, + LLInventoryType::EType inv_type, + LLInventoryPanel* inventory, + const LLUUID& uuid, + U32 flags) +{ + LLInvFVBridge* new_listener = NULL; + switch(asset_type) + { + case LLAssetType::AT_TEXTURE: + if(!(inv_type == LLInventoryType::IT_TEXTURE || inv_type == LLInventoryType::IT_SNAPSHOT)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLTextureBridge(inventory, uuid, inv_type); + break; + + case LLAssetType::AT_SOUND: + if(!(inv_type == LLInventoryType::IT_SOUND)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLSoundBridge(inventory, uuid); + break; + + case LLAssetType::AT_LANDMARK: + if(!(inv_type == LLInventoryType::IT_LANDMARK)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLLandmarkBridge(inventory, uuid, flags); + break; + + case LLAssetType::AT_CALLINGCARD: + if(!(inv_type == LLInventoryType::IT_CALLINGCARD)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLCallingCardBridge(inventory, uuid); + break; + + case LLAssetType::AT_SCRIPT: + if(!(inv_type == LLInventoryType::IT_LSL)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLScriptBridge(inventory, uuid); + break; + + case LLAssetType::AT_OBJECT: + if(!(inv_type == LLInventoryType::IT_OBJECT || inv_type == LLInventoryType::IT_ATTACHMENT)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLObjectBridge(inventory, uuid, inv_type, flags); + break; + + case LLAssetType::AT_NOTECARD: + if(!(inv_type == LLInventoryType::IT_NOTECARD)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLNotecardBridge(inventory, uuid); + break; + + case LLAssetType::AT_ANIMATION: + if(!(inv_type == LLInventoryType::IT_ANIMATION)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLAnimationBridge(inventory, uuid); + break; + + case LLAssetType::AT_GESTURE: + if(!(inv_type == LLInventoryType::IT_GESTURE)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLGestureBridge(inventory, uuid); + break; + + case LLAssetType::AT_LSL_TEXT: + if(!(inv_type == LLInventoryType::IT_LSL)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLLSLTextBridge(inventory, uuid); + break; + + case LLAssetType::AT_CLOTHING: + case LLAssetType::AT_BODYPART: + if(!(inv_type == LLInventoryType::IT_WEARABLE)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLWearableBridge(inventory, uuid, asset_type, inv_type, (EWearableType)flags); + break; + + case LLAssetType::AT_CATEGORY: + case LLAssetType::AT_ROOT_CATEGORY: + new_listener = new LLFolderBridge(inventory, uuid); + break; + + default: + llinfos << "Unhandled asset type (llassetstorage.h): " + << (S32)asset_type << llendl; + break; + } + + new_listener->mInvType = inv_type; + return new_listener; +} + +// +=================================================+ +// | LLItemBridge | +// +=================================================+ + +void LLItemBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action) +{ + if ("open" == action) + { + openItem(); + } + else if ("properties" == action) + { + showProperties(); + } + else if ("purge" == action) + { + LLInventoryCategory* cat = model->getCategory(mUUID); + if(cat) + { + model->purgeDescendentsOf(mUUID); + } + LLInventoryObject* obj = model->getObject(mUUID); + if(!obj) return; + obj->removeFromServer(); + model->deleteObject(mUUID); + model->notifyObservers(); + } + else if ("restore" == action) + { + restoreItem(); + } + else if ("copy_uuid" == action) + { + // Single item only + LLInventoryItem* item = model->getItem(mUUID); + if(!item) return; + LLUUID asset_id = item->getAssetUUID(); + char buffer[UUID_STR_LENGTH]; + asset_id.toString(buffer); + + gViewerWindow->mWindow->copyTextToClipboard(utf8str_to_wstring(buffer)); + return; + } + else if ("copy" == action) + { + copyToClipboard(); + return; + } + else if ("paste" == action) + { + // Single item only + LLInventoryItem* itemp = model->getItem(mUUID); + if (!itemp) return; + + LLFolderViewItem* folder_view_itemp = folder->getItemByID(itemp->getParentUUID()); + if (!folder_view_itemp) return; + + folder_view_itemp->getListener()->pasteFromClipboard(); + return; + } +} + +void LLItemBridge::selectItem() +{ + LLViewerInventoryItem* item = (LLViewerInventoryItem*)getItem(); + if(item && !item->isComplete()) + { + item->fetchFromServer(); + } +} + +void LLItemBridge::restoreItem() +{ + LLViewerInventoryItem* item = (LLViewerInventoryItem*)getItem(); + if(item) + { + LLInventoryModel* model = mInventoryPanel->getModel(); + LLUUID new_parent = model->findCategoryUUIDForType(item->getType()); + // do not restamp on restore. + LLInvFVBridge::changeItemParent(model, item, new_parent, FALSE); + } +} + +LLViewerImage* LLItemBridge::getIcon() const +{ + LLString uuid_string = gViewerArt.getString(ICON_NAME[OBJECT_ICON_NAME]); + return gImageList.getImage(LLUUID(uuid_string), MIPMAP_FALSE, TRUE); +} + +PermissionMask LLItemBridge::getPermissionMask() const +{ + LLViewerInventoryItem* item = getItem(); + PermissionMask perm_mask = 0; + if(item) + { + BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID()); + BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID()); + BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER, + gAgent.getID()); + + if (copy) perm_mask |= PERM_COPY; + if (mod) perm_mask |= PERM_MODIFY; + if (xfer) perm_mask |= PERM_TRANSFER; + + } + return perm_mask; +} + +const LLString& LLItemBridge::getDisplayName() const +{ + if(mDisplayName.empty()) + { + buildDisplayName(getItem(), mDisplayName); + } + return mDisplayName; +} + +void LLItemBridge::buildDisplayName(LLInventoryItem* item, LLString& name) +{ + if(item) + { + name.assign(item->getName()); + } + else + { + name.assign(LLString::null); + } +} + +LLString LLItemBridge::getLabelSuffix() const +{ + LLString suffix; + LLInventoryItem* item = getItem(); + if(item) + { + // it's a bit confusing to put nocopy/nomod/etc on calling cards. + if(LLAssetType::AT_CALLINGCARD != item->getType() + && item->getPermissions().getOwner() == gAgent.getID()) + { + BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID()); + BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID()); + BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER, + gAgent.getID()); + const char* EMPTY = ""; + const char* NO_COPY = " (no copy)"; + const char* NO_MOD = " (no modify)"; + const char* NO_XFER = " (no transfer)"; + const char* scopy; + if(copy) scopy = EMPTY; + else scopy = NO_COPY; + const char* smod; + if(mod) smod = EMPTY; + else smod = NO_MOD; + const char* sxfer; + if(xfer) sxfer = EMPTY; + else sxfer = NO_XFER; + char buffer[MAX_STRING]; + snprintf( + buffer, + MAX_STRING, + "%s%s%s", + scopy, + smod, + sxfer); + suffix.assign(buffer); + } + } + return suffix; +} + +U32 LLItemBridge::getCreationDate() const +{ + LLViewerInventoryItem* item = getItem(); + if (item) + { + return item->getCreationDate(); + } + return 0; +} + + +BOOL LLItemBridge::isItemRenameable() const +{ + LLViewerInventoryItem* item = getItem(); + if(item) + { + return (item->getPermissions().allowModifyBy(gAgent.getID())); + } + return FALSE; +} + +BOOL LLItemBridge::renameItem(const LLString& new_name) +{ + if(!isItemRenameable()) return FALSE; + LLPreview::rename(mUUID, getPrefix() + new_name); + LLInventoryModel* model = mInventoryPanel->getModel(); + if(!model) return FALSE; + LLViewerInventoryItem* item = getItem(); + if(item && (item->getName() != new_name)) + { + LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); + new_item->rename(new_name); + buildDisplayName(new_item, mDisplayName); + new_item->updateServer(FALSE); + model->updateItem(new_item); + model->notifyObservers(); + } + // return FALSE because we either notified observers (& therefore + // rebuilt) or we didn't update. + return FALSE; +} + + +BOOL LLItemBridge::removeItem() +{ + if(!isItemRemovable()) + { + return FALSE; + } + // move it to the trash + LLPreview::hide(mUUID); + LLInventoryModel* model = mInventoryPanel->getModel(); + if(!model) return FALSE; + LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH); + LLViewerInventoryItem* item = getItem(); + if(item) + { + // restamp on move to trash. + LLInvFVBridge::changeItemParent(model, item, trash_id, TRUE); + } + + // return false anyway, so that if it's called from the folder + // view, it doesn't remove the view - it's just being moved to the + // trash. + return FALSE; +} + +BOOL LLItemBridge::isItemCopyable() const +{ + LLViewerInventoryItem* item = getItem(); + if (item) + { + return (item->getPermissions().allowCopyBy(gAgent.getID())); + } + return FALSE; +} +BOOL LLItemBridge::copyToClipboard() const +{ + if(isItemCopyable()) + { + LLInventoryClipboard::instance().add(mUUID); + return TRUE; + } + return FALSE; +} + +LLViewerInventoryItem* LLItemBridge::getItem() const +{ + LLViewerInventoryItem* item = NULL; + LLInventoryModel* model = mInventoryPanel->getModel(); + if(model) + { + item = (LLViewerInventoryItem*)model->getItem(mUUID); + } + return item; +} + +BOOL LLItemBridge::isItemPermissive() const +{ + LLViewerInventoryItem* item = getItem(); + if(item) + { + U32 mask = item->getPermissions().getMaskBase(); + if((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) + { + return TRUE; + } + } + return FALSE; +} + +// +=================================================+ +// | LLFolderBridge | +// +=================================================+ + +LLFolderBridge* LLFolderBridge::sSelf=NULL; + +// Can be moved to another folder +BOOL LLFolderBridge::isItemMovable() +{ + LLInventoryObject* obj = getInventoryObject(); + if(obj) + { + return (LLAssetType::AT_NONE == ((LLInventoryCategory*)obj)->getPreferredType()); + } + return FALSE; +} + +void LLFolderBridge::selectItem() +{ +} + + +// Can be destroyed (or moved to trash) +BOOL LLFolderBridge::isItemRemovable() +{ + LLInventoryModel* model = mInventoryPanel->getModel(); + if(!model) + { + return FALSE; + } + + if(!model->isObjectDescendentOf(mUUID, gAgent.getInventoryRootID())) + { + return FALSE; + } + + LLVOAvatar* avatar = gAgent.getAvatarObject(); + if( !avatar ) + { + return FALSE; + } + + LLInventoryCategory* category = model->getCategory(mUUID); + if( !category ) + { + return FALSE; + } + + if( LLAssetType::AT_NONE != category->getPreferredType() ) + { + return FALSE; + } + + LLInventoryModel::cat_array_t descendent_categories; + LLInventoryModel::item_array_t descendent_items; + gInventory.collectDescendents( mUUID, descendent_categories, descendent_items, FALSE ); + + S32 i; + for( i = 0; i < descendent_categories.count(); i++ ) + { + LLInventoryCategory* category = descendent_categories[i]; + if( LLAssetType::AT_NONE != category->getPreferredType() ) + { + return FALSE; + } + } + + for( i = 0; i < descendent_items.count(); i++ ) + { + LLInventoryItem* item = descendent_items[i]; + if( (item->getType() == LLAssetType::AT_CLOTHING) || + (item->getType() == LLAssetType::AT_BODYPART) ) + { + if( gAgent.isWearingItem( item->getUUID() ) ) + { + return FALSE; + } + } + else + if( item->getType() == LLAssetType::AT_OBJECT ) + { + if( avatar->isWearingAttachment( item->getUUID() ) ) + { + return FALSE; + } + } + } + + return TRUE; +} + +BOOL LLFolderBridge::isUpToDate() const +{ + LLInventoryModel* model = mInventoryPanel->getModel(); + if(!model) return FALSE; + LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID); + if( !category ) + { + return FALSE; + } + + return category->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN; +} + +BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, + BOOL drop) +{ + // This should never happen, but if an inventory item is incorrectly parented, + // the UI will get confused and pass in a NULL. + if(!inv_cat) return FALSE; + + LLInventoryModel* model = mInventoryPanel->getModel(); + if(!model) return FALSE; + + LLVOAvatar* avatar = gAgent.getAvatarObject(); + if(!avatar) return FALSE; + + // cannot drag into library + if(!isAgentInventory()) + { + return FALSE; + } + + // check to make sure source is agent inventory, and is represented there. + LLToolDragAndDrop::ESource source = gToolDragAndDrop->getSource(); + BOOL is_agent_inventory = (model->getCategory(inv_cat->getUUID()) != NULL) + && (LLToolDragAndDrop::SOURCE_AGENT == source); + + BOOL accept = FALSE; + S32 i; + LLInventoryModel::cat_array_t descendent_categories; + LLInventoryModel::item_array_t descendent_items; + if(is_agent_inventory) + { + const LLUUID& cat_id = inv_cat->getUUID(); + + // Is the destination the trash? + LLUUID trash_id; + trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH); + BOOL move_is_into_trash = (mUUID == trash_id) + || model->isObjectDescendentOf(mUUID, trash_id); + BOOL is_movable = (LLAssetType::AT_NONE == inv_cat->getPreferredType()); + if( is_movable ) + { + gInventory.collectDescendents( cat_id, descendent_categories, descendent_items, FALSE ); + + for( i = 0; i < descendent_categories.count(); i++ ) + { + LLInventoryCategory* category = descendent_categories[i]; + if( LLAssetType::AT_NONE != category->getPreferredType() ) + { + // ...can't move "special folders" like Textures + is_movable = FALSE; + break; + } + } + + if( is_movable ) + { + if( move_is_into_trash ) + { + for( i = 0; i < descendent_items.count(); i++ ) + { + LLInventoryItem* item = descendent_items[i]; + if( (item->getType() == LLAssetType::AT_CLOTHING) || + (item->getType() == LLAssetType::AT_BODYPART) ) + { + if( gAgent.isWearingItem( item->getUUID() ) ) + { + is_movable = FALSE; // It's generally movable, but not into the trash! + break; + } + } + else + if( item->getType() == LLAssetType::AT_OBJECT ) + { + if( avatar->isWearingAttachment( item->getUUID() ) ) + { + is_movable = FALSE; // It's generally movable, but not into the trash! + break; + } + } + } + } + } + } + + + accept = is_movable + && (mUUID != cat_id) // Can't move a folder into itself + && (mUUID != inv_cat->getParentUUID()) // Avoid moves that would change nothing + && !(model->isObjectDescendentOf(mUUID, cat_id)); // Avoid circularity + if(accept && drop) + { + // Look for any gestures and deactivate them + if (move_is_into_trash) + { + for (i = 0; i < descendent_items.count(); i++) + { + LLInventoryItem* item = descendent_items[i]; + if (item->getType() == LLAssetType::AT_GESTURE + && gGestureManager.isGestureActive(item->getUUID())) + { + gGestureManager.deactivateGesture(item->getUUID()); + } + } + } + + // Reparent the folder and restamp children if it's moving + // into trash. + LLInvFVBridge::changeCategoryParent( + model, + (LLViewerInventoryCategory*)inv_cat, + mUUID, + move_is_into_trash); + } + } + else if(LLToolDragAndDrop::SOURCE_WORLD == source) + { + // content category has same ID as object itself + LLUUID object_id = inv_cat->getUUID(); + LLUUID category_id = mUUID; + accept = move_inv_category_world_to_agent(object_id, category_id, drop); + } + return accept; +} + +void warn_move_inventory(LLViewerObject* object, LLMoveInv* move_inv) +{ + const char* dialog = NULL; + if (object->flagScripted()) + { + dialog = "MoveInventoryFromScriptedObject"; + } + else + { + dialog = "MoveInventoryFromObject"; + } + gViewerWindow->alertXml(dialog, move_task_inventory_callback, move_inv); +} + +// Move/copy all inventory items from the Contents folder of an in-world +// object to the agent's inventory, inside a given category. +BOOL move_inv_category_world_to_agent(const LLUUID& object_id, + const LLUUID& category_id, + BOOL drop, + void (*callback)(S32, void*), + void* user_data) +{ + // Make sure the object exists. If we allowed dragging from + // anonymous objects, it would be possible to bypass + // permissions. + // content category has same ID as object itself + LLViewerObject* object = gObjectList.findObject(object_id); + if(!object) + { + llinfos << "Object not found for drop." << llendl; + return FALSE; + } + + // this folder is coming from an object, as there is only one folder in an object, the root, + // we need to collect the entire contents and handle them as a group + InventoryObjectList inventory_objects; + object->getInventoryContents(inventory_objects); + + if (inventory_objects.empty()) + { + llinfos << "Object contents not found for drop." << llendl; + return FALSE; + } + + BOOL accept = TRUE; + BOOL is_move = FALSE; + + // coming from a task. Need to figure out if the person can + // move/copy this item. + InventoryObjectList::iterator it = inventory_objects.begin(); + InventoryObjectList::iterator end = inventory_objects.end(); + for ( ; it != end; ++it) + { + // coming from a task. Need to figure out if the person can + // move/copy this item. + LLPermissions perm(((LLInventoryItem*)((LLInventoryObject*)(*it)))->getPermissions()); + if((perm.allowCopyBy(gAgent.getID(), gAgent.getGroupID()) + && perm.allowTransferTo(gAgent.getID()))) +// || gAgent.isGodlike()) + { + accept = TRUE; + } + else if(object->permYouOwner()) + { + // If the object cannot be copied, but the object the + // inventory is owned by the agent, then the item can be + // moved from the task to agent inventory. + is_move = TRUE; + accept = TRUE; + } + else + { + accept = FALSE; + break; + } + } + + if(drop && accept) + { + it = inventory_objects.begin(); + InventoryObjectList::iterator first_it = inventory_objects.begin(); + LLMoveInv* move_inv = new LLMoveInv; + move_inv->mObjectID = object_id; + move_inv->mCategoryID = category_id; + move_inv->mCallback = callback; + move_inv->mUserData = user_data; + + for ( ; it != end; ++it) + { + two_uuids_t two(category_id, (*it)->getUUID()); + move_inv->mMoveList.push_back(two); + } + + if(is_move) + { + // Callback called from within here. + warn_move_inventory(object, move_inv); + } + else + { + move_task_inventory_callback(0, (void*)(move_inv)); + } + } + return accept; +} + +class LLFindWearables : public LLInventoryCollectFunctor +{ +public: + LLFindWearables() {} + virtual ~LLFindWearables() {} + virtual bool operator()(LLInventoryCategory* cat, + LLInventoryItem* item); +}; + +bool LLFindWearables::operator()(LLInventoryCategory* cat, + LLInventoryItem* item) +{ + if(item) + { + if((item->getType() == LLAssetType::AT_CLOTHING) + || (item->getType() == LLAssetType::AT_BODYPART)) + { + return TRUE; + } + } + return FALSE; +} + +//Used by LLFolderBridge as callback for directory recursion. +class LLRightClickInventoryFetchObserver : public LLInventoryFetchObserver +{ +public: + LLRightClickInventoryFetchObserver() {}; + LLRightClickInventoryFetchObserver(const LLUUID& cat_id, bool copy_items) : + mCatID(cat_id), + mCopyItems(copy_items) + { }; + virtual void done() + { + // we've downloaded all the items, so repaint the dialog + LLFolderBridge::staticFolderOptionsMenu(); + + gInventory.removeObserver(this); + delete this; + } + + +protected: + LLUUID mCatID; + bool mCopyItems; + +}; + +//Used by LLFolderBridge as callback for directory recursion. +class LLRightClickInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver +{ +public: + LLRightClickInventoryFetchDescendentsObserver(bool copy_items) : mCopyItems(copy_items) {} + ~LLRightClickInventoryFetchDescendentsObserver() {} + virtual void done(); +protected: + bool mCopyItems; +}; + +void LLRightClickInventoryFetchDescendentsObserver::done() +{ + // What we do here is get the complete information on the items in + // the library, and set up an observer that will wait for that to + // happen. + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + gInventory.collectDescendents(mCompleteFolders.front(), + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH); + S32 count = item_array.count(); +#if 0 + // This early causes a giant menu to get produced, and doesn't seem to be needed. + if(!count) + { + llwarns << "Nothing fetched in category " << mCompleteFolders.front() + << llendl; + dec_busy_count(); + gInventory.removeObserver(this); + delete this; + return; + } +#endif + + LLRightClickInventoryFetchObserver* outfit; + outfit = new LLRightClickInventoryFetchObserver(mCompleteFolders.front(), mCopyItems); + LLInventoryFetchObserver::item_ref_t ids; + for(S32 i = 0; i < count; ++i) + { + ids.push_back(item_array.get(i)->getUUID()); + } + + // clean up, and remove this as an observer since the call to the + // outfit could notify observers and throw us into an infinite + // loop. + dec_busy_count(); + gInventory.removeObserver(this); + delete this; + + // increment busy count and either tell the inventory to check & + // call done, or add this object to the inventory for observation. + inc_busy_count(); + + // do the fetch + outfit->fetchItems(ids); + outfit->done(); //Not interested in waiting and this will be right 99% of the time. +//Uncomment the following code for laggy Inventory UI. +/* if(outfit->isEverythingComplete()) + { + // everything is already here - call done. + outfit->done(); + } + else + { + // it's all on it's way - add an observer, and the inventory + // will call done for us when everything is here. + gInventory.addObserver(outfit); + }*/ +} + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryWearObserver +// +// Observer for "copy and wear" operation to support knowing +// when the all of the contents have been added to inventory. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLInventoryCopyAndWearObserver : public LLInventoryObserver +{ +public: + LLInventoryCopyAndWearObserver(const LLUUID& cat_id, int count) :mCatID(cat_id), mContentsCount(count), mFolderAdded(FALSE) {} + virtual ~LLInventoryCopyAndWearObserver() {} + virtual void changed(U32 mask); + +protected: + LLUUID mCatID; + int mContentsCount; + BOOL mFolderAdded; +}; + + + +void LLInventoryCopyAndWearObserver::changed(U32 mask) +{ + if((mask & (LLInventoryObserver::ADD)) != 0) + { + if (!mFolderAdded) + { + const std::set<LLUUID>& changed_items = gInventory.getChangedIDs(); + + std::set<LLUUID>::const_iterator id_it = changed_items.begin(); + std::set<LLUUID>::const_iterator id_end = changed_items.end(); + for (;id_it != id_end; ++id_it) + { + if ((*id_it) == mCatID) + { + mFolderAdded = TRUE; + break; + } + } + } + + if (mFolderAdded) + { + LLViewerInventoryCategory* category = gInventory.getCategory(mCatID); + + if (category->getDescendentCount() == mContentsCount) + { + gInventory.removeObserver(this); + wear_inventory_category(category, FALSE, TRUE); + delete this; + } + } + + } +} + + + +void LLFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action) +{ + if ("open" == action) + { + openItem(); + } + else if ("paste" == action) + { + pasteFromClipboard(); + } + else if ("properties" == action) + { + showProperties(); + } + else if ("replaceoutfit" == action) + { + modifyOutfit(FALSE); + } + else if ("addtooutfit" == action) + { + modifyOutfit(TRUE); + } + else if ("removefromoutfit" == action) + { + LLInventoryModel* model = mInventoryPanel->getModel(); + if(!model) return; + LLViewerInventoryCategory* cat = getCategory(); + if(!cat) return; + + remove_inventory_category_from_avatar ( cat ); + } + else if ("purge" == action) + { + LLViewerInventoryCategory* cat; + cat = (LLViewerInventoryCategory*)getCategory(); + + if(cat) + { + model->purgeDescendentsOf(mUUID); + } + LLInventoryObject* obj = model->getObject(mUUID); + if(!obj) return; + obj->removeFromServer(); + model->deleteObject(mUUID); + model->notifyObservers(); + } + else if ("restore" == action) + { + restoreItem(); + } +} + +void LLFolderBridge::openItem() +{ + lldebugs << "LLFolderBridge::openItem()" << llendl; + LLInventoryModel* model = mInventoryPanel->getModel(); + if(!model) return; + model->fetchDescendentsOf(mUUID); +} + +BOOL LLFolderBridge::isItemRenameable() const +{ + LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)getCategory(); + if(cat && (cat->getPreferredType() == LLAssetType::AT_NONE) + && (cat->getOwnerID() == gAgent.getID())) + { + return TRUE; + } + return FALSE; +} + +void LLFolderBridge::restoreItem() +{ + LLViewerInventoryCategory* cat; + cat = (LLViewerInventoryCategory*)getCategory(); + if(cat) + { + LLInventoryModel* model = mInventoryPanel->getModel(); + LLUUID new_parent = model->findCategoryUUIDForType(cat->getType()); + // do not restamp children on restore + LLInvFVBridge::changeCategoryParent(model, cat, new_parent, FALSE); + } +} + +// Icons for folders are based on the preferred type +LLViewerImage* LLFolderBridge::getIcon() const +{ + const char* control = NULL; + LLAssetType::EType preferred_type = LLAssetType::AT_NONE; + LLViewerInventoryCategory* cat = getCategory(); + if(cat) + { + preferred_type = cat->getPreferredType(); + } + switch(preferred_type) + { + case LLAssetType::AT_TEXTURE: + control = "inv_folder_texture.tga"; + break; + case LLAssetType::AT_SOUND: + control = "inv_folder_sound.tga"; + break; + case LLAssetType::AT_CALLINGCARD: + control = "inv_folder_callingcard.tga"; + break; + case LLAssetType::AT_LANDMARK: + control = "inv_folder_landmark.tga"; + break; + case LLAssetType::AT_SCRIPT: + case LLAssetType::AT_LSL_TEXT: + control = "inv_folder_script.tga"; + break; + case LLAssetType::AT_OBJECT: + control = "inv_folder_object.tga"; + break; + case LLAssetType::AT_NOTECARD: + control = "inv_folder_notecard.tga"; + break; + case LLAssetType::AT_CATEGORY: + control = "inv_folder_plain_closed.tga"; + break; + case LLAssetType::AT_CLOTHING: + control = "inv_folder_clothing.tga"; + break; + case LLAssetType::AT_BODYPART: + control = "inv_folder_bodypart.tga"; + break; + case LLAssetType::AT_TRASH: + control = "inv_folder_trash.tga"; + break; + case LLAssetType::AT_SNAPSHOT_CATEGORY: + control = "inv_folder_snapshot.tga"; + break; + case LLAssetType::AT_LOST_AND_FOUND: + control = "inv_folder_lostandfound.tga"; + break; + case LLAssetType::AT_ANIMATION: + control = "inv_folder_animation.tga"; + break; + case LLAssetType::AT_GESTURE: + control = "inv_folder_gesture.tga"; + break; + default: + control = "inv_folder_plain_closed.tga"; + break; + } + LLString uuid_string = gViewerArt.getString(control); + return gImageList.getImage(LLUUID(uuid_string), MIPMAP_FALSE, TRUE); +} + +BOOL LLFolderBridge::renameItem(const LLString& new_name) +{ + if(!isItemRenameable()) return FALSE; + LLInventoryModel* model = mInventoryPanel->getModel(); + if(!model) return FALSE; + LLViewerInventoryCategory* cat = getCategory(); + if(cat && (cat->getName() != new_name)) + { + LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat); + new_cat->rename(new_name); + new_cat->updateServer(FALSE); + model->updateCategory(new_cat); + model->notifyObservers(); + } + // return FALSE because we either notified observers (& therefore + // rebuilt) or we didn't update. + return FALSE; +} + +BOOL LLFolderBridge::removeItem() +{ + if(!isItemRemovable()) + { + return FALSE; + } + // move it to the trash + LLPreview::hide(mUUID); + LLInventoryModel* model = mInventoryPanel->getModel(); + if(!model) return FALSE; + + LLUUID trash_id; + trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH); + + // Look for any gestures and deactivate them + LLInventoryModel::cat_array_t descendent_categories; + LLInventoryModel::item_array_t descendent_items; + gInventory.collectDescendents( mUUID, descendent_categories, descendent_items, FALSE ); + + S32 i; + for (i = 0; i < descendent_items.count(); i++) + { + LLInventoryItem* item = descendent_items[i]; + if (item->getType() == LLAssetType::AT_GESTURE + && gGestureManager.isGestureActive(item->getUUID())) + { + gGestureManager.deactivateGesture(item->getUUID()); + } + } + + // go ahead and do the normal remove if no 'last calling + // cards' are being removed. + LLViewerInventoryCategory* cat = getCategory(); + if(cat) + { + LLInvFVBridge::changeCategoryParent(model, cat, trash_id, TRUE); + } + + // return false anyway, so that if it's called from the folder + // view, it doesn't remove the view - it's just being moved to the + // trash. + return FALSE; + +} + +BOOL LLFolderBridge::isClipboardPasteable() const +{ + if(LLInventoryClipboard::instance().hasContents() && isAgentInventory()) + { + return TRUE; + } + return FALSE; +} + +void LLFolderBridge::pasteFromClipboard() +{ + LLInventoryModel* model = mInventoryPanel->getModel(); + if(model && isClipboardPasteable()) + { + LLInventoryItem* item = NULL; + LLDynamicArray<LLUUID> objects; + LLInventoryClipboard::instance().retrieve(objects); + S32 count = objects.count(); + LLUUID parent_id(mUUID); + for(S32 i = 0; i < count; i++) + { + item = model->getItem(objects.get(i)); + if (item) + { + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + parent_id, + std::string(), + LLPointer<LLInventoryCallback>(NULL)); + } + } + } +} + +void LLFolderBridge::staticFolderOptionsMenu() +{ + if (!sSelf) return; + sSelf->folderOptionsMenu(); +} + +void LLFolderBridge::folderOptionsMenu() +{ + std::vector<LLString> disabled_items; + + LLInventoryModel* model = mInventoryPanel->getModel(); + if(!model) return; + + // calling card related functionality for folders. + + LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD); + if (mCallingCards || checkFolderForContentsOfType(model, is_callingcard)) + { + mItems.push_back("Calling Card Separator"); + mItems.push_back("IM Contacts In Folder"); + mItems.push_back("IM All Contacts In Folder"); + } + + // wearables related functionality for folders. + //is_wearable + LLFindWearables is_wearable; + LLIsType is_object( LLAssetType::AT_OBJECT ); + LLIsType is_gesture( LLAssetType::AT_GESTURE ); + + if (mWearables || + checkFolderForContentsOfType(model, is_wearable) || + checkFolderForContentsOfType(model, is_object) || + checkFolderForContentsOfType(model, is_gesture) ) + { + mItems.push_back("Folder Wearables Separator"); + mItems.push_back("Add To Outfit"); + mItems.push_back("Replace Outfit"); + mItems.push_back("Take Off Items"); + } + hideContextEntries(*mMenu, mItems, disabled_items); +} + +BOOL LLFolderBridge::checkFolderForContentsOfType(LLInventoryModel* model, LLInventoryCollectFunctor& is_type) +{ + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + model->collectDescendentsIf(mUUID, + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH, + is_type); + return ((item_array.count() > 0) ? TRUE : FALSE ); +} + +// Flags unused +void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + lldebugs << "LLFolderBridge::buildContextMenu()" << llendl; +// std::vector<LLString> disabled_items; + LLInventoryModel* model = mInventoryPanel->getModel(); + if(!model) return; + LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH); + if(trash_id == mUUID) + { + // This is the trash. + mItems.push_back("Empty Trash"); + } + else if(model->isObjectDescendentOf(mUUID, trash_id)) + { + // This is a folder in the trash. + mItems.clear(); // clear any items that used to exist + mItems.push_back("Purge Item"); + if (!isItemRemovable()) + { + mDisabledItems.push_back("Purge Item"); + } + + mItems.push_back("Restore Item"); + } + else if(isAgentInventory()) // do not allow creating in library + { + // only mature accounts can create undershirts/underwear + /*if (gAgent.mAccess >= SIM_ACCESS_MATURE) + { + sub_menu->append(new LLMenuItemCallGL("New Undershirt", + &createNewUndershirt, + NULL, + (void*)this)); + sub_menu->append(new LLMenuItemCallGL("New Underpants", + &createNewUnderpants, + NULL, + (void*)this)); + }*/ + +/* BOOL contains_calling_cards = FALSE; + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + + LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD); + model->collectDescendentsIf(mUUID, + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH, + is_callingcard); + if(item_array.count() > 0) contains_calling_cards = TRUE; +*/ + mItems.push_back("New Folder"); + mItems.push_back("New Script"); + mItems.push_back("New Note"); + mItems.push_back("New Gesture"); + mItems.push_back("New Clothes"); + mItems.push_back("New Body Parts"); + + getClipboardEntries(false, mItems, mDisabledItems, flags); + + //Added by spatters to force inventory pull on right-click to display folder options correctly. 07-17-06 + mCallingCards = mWearables = FALSE; + + LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD); + if (checkFolderForContentsOfType(model, is_callingcard)) + { + mCallingCards=TRUE; + } + + LLFindWearables is_wearable; + LLIsType is_object( LLAssetType::AT_OBJECT ); + LLIsType is_gesture( LLAssetType::AT_GESTURE ); + + if (checkFolderForContentsOfType(model, is_wearable) || + checkFolderForContentsOfType(model, is_object) || + checkFolderForContentsOfType(model, is_gesture) ) + { + mWearables=TRUE; + } + + mMenu = &menu; + sSelf = this; + LLRightClickInventoryFetchDescendentsObserver* fetch = new LLRightClickInventoryFetchDescendentsObserver(FALSE); + + LLInventoryFetchDescendentsObserver::folder_ref_t folders; + LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID); + folders.push_back(category->getUUID()); + fetch->fetchDescendents(folders); + inc_busy_count(); + if(fetch->isEverythingComplete()) + { + // everything is already here - call done. + fetch->done(); + } + else + { + // it's all on it's way - add an observer, and the inventory + // will call done for us when everything is here. + gInventory.addObserver(fetch); + } + } + else + { + mItems.push_back("--no options--"); + mDisabledItems.push_back("--no options--"); + } + hideContextEntries(menu, mItems, mDisabledItems); +} + +BOOL LLFolderBridge::hasChildren() const +{ + LLInventoryModel* model = mInventoryPanel->getModel(); + if(!model) return FALSE; + LLInventoryModel::EHasChildren has_children; + has_children = gInventory.categoryHasChildren(mUUID); + return has_children != LLInventoryModel::CHILDREN_NO; +} + +BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data) +{ + //llinfos << "LLFolderBridge::dragOrDrop()" << llendl; + BOOL accept = FALSE; + switch(cargo_type) + { + case DAD_TEXTURE: + case DAD_SOUND: + case DAD_CALLINGCARD: + case DAD_LANDMARK: + case DAD_SCRIPT: + case DAD_OBJECT: + case DAD_NOTECARD: + case DAD_CLOTHING: + case DAD_BODYPART: + case DAD_ANIMATION: + case DAD_GESTURE: + accept = dragItemIntoFolder((LLInventoryItem*)cargo_data, + drop); + break; + case DAD_CATEGORY: + accept = dragCategoryIntoFolder((LLInventoryCategory*)cargo_data, + drop); + break; + default: + break; + } + return accept; +} + +LLViewerInventoryCategory* LLFolderBridge::getCategory() const +{ + LLViewerInventoryCategory* cat = NULL; + LLInventoryModel* model = mInventoryPanel->getModel(); + if(model) + { + cat = (LLViewerInventoryCategory*)model->getCategory(mUUID); + } + return cat; +} + + +// static +void LLFolderBridge::pasteClipboard(void* user_data) +{ + LLFolderBridge* self = (LLFolderBridge*)user_data; + if(self) self->pasteFromClipboard(); +} + +void LLFolderBridge::createNewCategory(void* user_data) +{ + LLFolderBridge* bridge = (LLFolderBridge*)user_data; + if(!bridge) return; + LLInventoryPanel* panel = bridge->mInventoryPanel; + LLInventoryModel* model = panel->getModel(); + if(!model) return; + LLUUID id; + id = model->createNewCategory(bridge->getUUID(), + LLAssetType::AT_NONE, + NULL); + model->notifyObservers(); + + // At this point, the bridge has probably been deleted, but the + // view is still there. + panel->setSelection(id, TAKE_FOCUS_YES); +} + +void LLFolderBridge::createNewShirt(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SHIRT); +} + +void LLFolderBridge::createNewPants(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_PANTS); +} + +void LLFolderBridge::createNewShoes(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SHOES); +} + +void LLFolderBridge::createNewSocks(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SOCKS); +} + +void LLFolderBridge::createNewJacket(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_JACKET); +} + +void LLFolderBridge::createNewSkirt(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SKIRT); +} + +void LLFolderBridge::createNewGloves(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_GLOVES); +} + +void LLFolderBridge::createNewUndershirt(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_UNDERSHIRT); +} + +void LLFolderBridge::createNewUnderpants(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_UNDERPANTS); +} + +void LLFolderBridge::createNewShape(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SHAPE); +} + +void LLFolderBridge::createNewSkin(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SKIN); +} + +void LLFolderBridge::createNewHair(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_HAIR); +} + +void LLFolderBridge::createNewEyes(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_EYES); +} + +// static +void LLFolderBridge::createWearable(LLFolderBridge* bridge, EWearableType type) +{ + if(!bridge) return; + LLUUID parent_id = bridge->getUUID(); + createWearable(parent_id, type); +} + +// Separate function so can be called by global menu as well as right-click +// menu. +// static +void LLFolderBridge::createWearable(LLUUID parent_id, EWearableType type) +{ + LLWearable* wearable = gWearableList.createNewWearable(type); + LLAssetType::EType asset_type = wearable->getAssetType(); + LLInventoryType::EType inv_type = LLInventoryType::IT_WEARABLE; + create_inventory_item(gAgent.getID(), gAgent.getSessionID(), + parent_id, wearable->getTransactionID(), wearable->getName(), + wearable->getDescription(), asset_type, inv_type, wearable->getType(), + wearable->getPermissions().getMaskNextOwner(), + LLPointer<LLInventoryCallback>(NULL)); +} + +void LLFolderBridge::beginIMSession(BOOL only_online) +{ + LLInventoryModel* model = mInventoryPanel->getModel(); + if(!model) return; + LLViewerInventoryCategory* cat = getCategory(); + if(!cat) return; + LLUniqueBuddyCollector is_buddy; + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + model->collectDescendentsIf(mUUID, + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH, + is_buddy); + S32 count = item_array.count(); + if(count > 0) + { + // create the session + gIMView->setFloaterOpen(TRUE); + LLDynamicArray<LLUUID> members; + //members.put(gAgent.getID()); + S32 i; + EInstantMessage type = IM_SESSION_ADD; + if(only_online) + { + LLAvatarTracker& at = LLAvatarTracker::instance(); + LLUUID id; + for(i = 0; i < count; ++i) + { + id = item_array.get(i)->getCreatorUUID(); + if(at.isBuddyOnline(id)) + { + members.put(id); + } + } + } + else + { + type = IM_SESSION_OFFLINE_ADD; + for(i = 0; i < count; ++i) + { + members.put(item_array.get(i)->getCreatorUUID()); + } + } + // the session_id is always the item_id of the inventory folder + gIMView->addSession(cat->getName(), + type, + mUUID, + members); + } +} + +void LLFolderBridge::modifyOutfit(BOOL append) +{ + LLInventoryModel* model = mInventoryPanel->getModel(); + if(!model) return; + LLViewerInventoryCategory* cat = getCategory(); + if(!cat) return; + + wear_inventory_category_on_avatar( cat, append ); +} + +// helper stuff +void move_task_inventory_callback(S32 option, void* user_data) +{ + LLMoveInv* move_inv = (LLMoveInv*)user_data; + LLFloaterOpenObject::LLCatAndWear* cat_and_wear = (LLFloaterOpenObject::LLCatAndWear* )move_inv->mUserData; + LLViewerObject* object = gObjectList.findObject(move_inv->mObjectID); + + if(option == 0 && object) + { + if (cat_and_wear && cat_and_wear->mWear) + { + InventoryObjectList inventory_objects; + object->getInventoryContents(inventory_objects); + int contents_count = inventory_objects.size()-1; //subtract one for containing folder + + LLInventoryCopyAndWearObserver* inventoryObserver = new LLInventoryCopyAndWearObserver(cat_and_wear->mCatID, contents_count); + gInventory.addObserver(inventoryObserver); + } + + two_uuids_list_t::iterator move_it; + for (move_it = move_inv->mMoveList.begin(); + move_it != move_inv->mMoveList.end(); + ++move_it) + { + object->moveInventory(move_it->first, move_it->second); + } + + // update the UI. + dialog_refresh_all(); + } + + if (move_inv->mCallback) + { + move_inv->mCallback(option, move_inv->mUserData); + } + + delete move_inv; +} + +BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, + BOOL drop) +{ + LLInventoryModel* model = mInventoryPanel->getModel(); + if(!model) return FALSE; + + // cannot drag into library + if(!isAgentInventory()) + { + return FALSE; + } + + LLVOAvatar* avatar = gAgent.getAvatarObject(); + if(!avatar) return FALSE; + + LLToolDragAndDrop::ESource source = gToolDragAndDrop->getSource(); + BOOL accept = FALSE; + LLViewerObject* object = NULL; + if(LLToolDragAndDrop::SOURCE_AGENT == source) + { + + BOOL is_movable = TRUE; + switch( inv_item->getType() ) + { + case LLAssetType::AT_ROOT_CATEGORY: + is_movable = FALSE; + break; + + case LLAssetType::AT_CATEGORY: + is_movable = ( LLAssetType::AT_NONE == ((LLInventoryCategory*)inv_item)->getPreferredType() ); + break; + } + + LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH); + BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id); + if(is_movable && move_is_into_trash) + { + switch(inv_item->getType()) + { + case LLAssetType::AT_CLOTHING: + case LLAssetType::AT_BODYPART: + is_movable = !gAgent.isWearingItem(inv_item->getUUID()); + break; + + case LLAssetType::AT_OBJECT: + is_movable = !avatar->isWearingAttachment(inv_item->getUUID()); + break; + } + } + + accept = is_movable && (mUUID != inv_item->getParentUUID()); + if(accept && drop) + { + if (inv_item->getType() == LLAssetType::AT_GESTURE + && gGestureManager.isGestureActive(inv_item->getUUID())) + { + gGestureManager.deactivateGesture(inv_item->getUUID()); + } + // If an item is being dragged between windows, unselect + // everything in the active window so that we don't follow + // the selection to its new location (which is very + // annoying). + LLInventoryPanel* active_panel = LLInventoryView::getActiveInventory()->getPanel(); + if (mInventoryPanel != active_panel) + { + active_panel->unSelectAll(); + } + + // restamp if the move is into the trash. + LLInvFVBridge::changeItemParent( + model, + (LLViewerInventoryItem*)inv_item, + mUUID, + move_is_into_trash); + } + } + else if(LLToolDragAndDrop::SOURCE_WORLD == source) + { + // Make sure the object exists. If we allowed dragging from + // anonymous objects, it would be possible to bypass + // permissions. + object = gObjectList.findObject(inv_item->getParentUUID()); + if(!object) + { + llinfos << "Object not found for drop." << llendl; + return FALSE; + } + + // coming from a task. Need to figure out if the person can + // move/copy this item. + LLPermissions perm(inv_item->getPermissions()); + BOOL is_move = FALSE; + if((perm.allowCopyBy(gAgent.getID(), gAgent.getGroupID()) + && perm.allowTransferTo(gAgent.getID()))) +// || gAgent.isGodlike()) + + { + accept = TRUE; + } + else if(object->permYouOwner()) + { + // If the object cannot be copied, but the object the + // inventory is owned by the agent, then the item can be + // moved from the task to agent inventory. + is_move = TRUE; + accept = TRUE; + } + if(drop && accept) + { + LLMoveInv* move_inv = new LLMoveInv; + move_inv->mObjectID = inv_item->getParentUUID(); + two_uuids_t item_pair(mUUID, inv_item->getUUID()); + move_inv->mMoveList.push_back(item_pair); + move_inv->mCallback = NULL; + move_inv->mUserData = NULL; + if(is_move) + { + warn_move_inventory(object, move_inv); + } + else + { + move_task_inventory_callback(0, (void*)(move_inv)); + } + } + + } + else if(LLToolDragAndDrop::SOURCE_NOTECARD == source) + { + accept = TRUE; + if(drop) + { + copy_inventory_from_notecard(gToolDragAndDrop->getObjectID(), + gToolDragAndDrop->getSourceID(), inv_item); + } + } + else if(LLToolDragAndDrop::SOURCE_LIBRARY == source) + { + LLViewerInventoryItem* item = (LLViewerInventoryItem*)inv_item; + if(item && item->isComplete()) + { + accept = TRUE; + if(drop) + { + copy_inventory_item( + gAgent.getID(), + inv_item->getPermissions().getOwner(), + inv_item->getUUID(), + mUUID, + std::string(), + LLPointer<LLInventoryCallback>(NULL)); + } + } + } + else + { + llwarns << "unhandled drag source" << llendl; + } + return accept; +} + +// +=================================================+ +// | LLScriptBridge (DEPRECTED) | +// +=================================================+ + +LLViewerImage* LLScriptBridge::getIcon() const +{ + return get_item_icon(LLAssetType::AT_SCRIPT, LLInventoryType::IT_LSL, 0); +} + +// +=================================================+ +// | LLTextureBridge | +// +=================================================+ + +LLString LLTextureBridge::sPrefix("Texture: "); + + +LLViewerImage* LLTextureBridge::getIcon() const +{ + return get_item_icon(LLAssetType::AT_TEXTURE, mInvType, 0); +} + +void open_texture(const LLUUID& item_id, + const LLString& title, + BOOL show_keep_discard, + const LLUUID& source_id, + BOOL take_focus) +{ + // See if we can bring an exiting preview to the front + if( !LLPreview::show( item_id, take_focus ) ) + { + // There isn't one, so make a new preview + S32 left, top; + gFloaterView->getNewFloaterPosition(&left, &top); + LLRect rect = gSavedSettings.getRect("PreviewTextureRect"); + rect.translate( left - rect.mLeft, top - rect.mTop ); + + LLPreviewTexture* preview; + preview = new LLPreviewTexture("preview texture", + rect, + title, + item_id, + LLUUID::null, + show_keep_discard); + preview->setSourceID(source_id); + if(take_focus) preview->setFocus(TRUE); + + gFloaterView->adjustToFitScreen(preview, FALSE); + } +} + +void LLTextureBridge::openItem() +{ + LLViewerInventoryItem* item = getItem(); + if(item) + { + open_texture(mUUID, getPrefix() + item->getName(), FALSE); + } +} + +// +=================================================+ +// | LLSoundBridge | +// +=================================================+ + +LLString LLSoundBridge::sPrefix("Sound: "); + + +LLViewerImage* LLSoundBridge::getIcon() const +{ + return get_item_icon(LLAssetType::AT_SOUND, LLInventoryType::IT_SOUND, 0); +} + +void LLSoundBridge::openItem() +{ +// Changed this back to the way it USED to work: +// only open the preview dialog through the contextual right-click menu +// double-click just plays the sound + + LLViewerInventoryItem* item = getItem(); + if(item) + { + openSoundPreview((void*)this); + //send_uuid_sound_trigger(item->getAssetUUID(), 1.0); + } + +// if(!LLPreview::show(mUUID)) +// { +// S32 left, top; +// gFloaterView->getNewFloaterPosition(&left, &top); +// LLRect rect = gSavedSettings.getRect("PreviewSoundRect"); +// rect.translate(left - rect.mLeft, top - rect.mTop); +// new LLPreviewSound("preview sound", +// rect, +// getPrefix() + getName(), +// mUUID)); +// } +} + +void LLSoundBridge::previewItem() +{ + LLViewerInventoryItem* item = getItem(); + if(item) + { + send_sound_trigger(item->getAssetUUID(), 1.0); + } +} + +void LLSoundBridge::openSoundPreview(void* which) +{ + LLSoundBridge *me = (LLSoundBridge *)which; + if(!LLPreview::show(me->mUUID)) + { + S32 left, top; + gFloaterView->getNewFloaterPosition(&left, &top); + LLRect rect = gSavedSettings.getRect("PreviewSoundRect"); + rect.translate(left - rect.mLeft, top - rect.mTop); + LLPreviewSound* preview = new LLPreviewSound("preview sound", + rect, + me->getPrefix() + me->getName(), + me->mUUID); + preview->setFocus(TRUE); + // Keep entirely onscreen. + gFloaterView->adjustToFitScreen(preview, FALSE); + } +} + +void LLSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + lldebugs << "LLTextureBridge::buildContextMenu()" << llendl; + std::vector<LLString> items; + std::vector<LLString> disabled_items; + + if(isInTrash()) + { + items.push_back("Purge Item"); + if (!isItemRemovable()) + { + disabled_items.push_back("Purge Item"); + } + + items.push_back("Restore Item"); + } + else + { + items.push_back("Sound Open"); + items.push_back("Properties"); + + getClipboardEntries(true, items, disabled_items, flags); + } + + items.push_back("Sound Separator"); + items.push_back("Sound Play"); + + hideContextEntries(menu, items, disabled_items); +} + +// +=================================================+ +// | LLLandmarkBridge | +// +=================================================+ + +LLString LLLandmarkBridge::sPrefix("Landmark: "); + +LLViewerImage* LLLandmarkBridge::getIcon() const +{ + return get_item_icon(LLAssetType::AT_LANDMARK, LLInventoryType::IT_LANDMARK, mVisited); +} + +void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + std::vector<LLString> items; + std::vector<LLString> disabled_items; + + lldebugs << "LLLandmarkBridge::buildContextMenu()" << llendl; + if(isInTrash()) + { + items.push_back("Purge Item"); + if (!isItemRemovable()) + { + disabled_items.push_back("Purge Item"); + } + + items.push_back("Restore Item"); + } + else + { + items.push_back("Landmark Open"); + items.push_back("Properties"); + + getClipboardEntries(true, items, disabled_items, flags); + } + + items.push_back("Landmark Separator"); + items.push_back("Teleport To Landmark"); + + hideContextEntries(menu, items, disabled_items); + +} + +// virtual +void LLLandmarkBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action) +{ + if ("teleport" == action) + { + LLViewerInventoryItem* item = getItem(); + if(item) + { + gAgent.teleportViaLandmark(item->getAssetUUID()); + + // we now automatically track the landmark you're teleporting to + // because you'll probably arrive at a telehub instead + if( gFloaterWorldMap ) + { + gFloaterWorldMap->trackLandmark( item->getAssetUUID() ); + } + } + } + else LLItemBridge::performAction(folder, model, action); +} + +void open_landmark(const LLUUID& item_id, + const LLString& title, + BOOL show_keep_discard, + const LLUUID& source_id, + BOOL take_focus) +{ + // See if we can bring an exiting preview to the front + if( !LLPreview::show( item_id, take_focus ) ) + { + // There isn't one, so make a new preview + S32 left, top; + gFloaterView->getNewFloaterPosition(&left, &top); + LLRect rect = gSavedSettings.getRect("PreviewLandmarkRect"); + rect.translate( left - rect.mLeft, top - rect.mTop ); + + LLPreviewLandmark* preview = new LLPreviewLandmark("preview landmark", + rect, + title, + item_id, + show_keep_discard); + preview->setSourceID(source_id); + if(take_focus) preview->setFocus(TRUE); + // keep onscreen + gFloaterView->adjustToFitScreen(preview, FALSE); + } +} + +void LLLandmarkBridge::openItem() +{ + LLViewerInventoryItem* item = getItem(); + if( item ) + { + open_landmark(mUUID, LLString(" ") + getPrefix() + item->getName(), FALSE); + } +} + + +// +=================================================+ +// | LLCallingCardObserver | +// +=================================================+ +void LLCallingCardObserver::changed(U32 mask) +{ + mBridgep->refreshFolderViewItem(); +} + +// +=================================================+ +// | LLCallingCardBridge | +// +=================================================+ + +LLString LLCallingCardBridge::sPrefix("Calling Card: "); + +LLCallingCardBridge::LLCallingCardBridge( LLInventoryPanel* inventory, const LLUUID& uuid ) : + LLItemBridge(inventory, uuid) +{ + mObserver = new LLCallingCardObserver(this); + LLAvatarTracker::instance().addObserver(mObserver); +} + +LLCallingCardBridge::~LLCallingCardBridge() +{ + LLAvatarTracker::instance().removeObserver(mObserver); + delete mObserver; +} + +void LLCallingCardBridge::refreshFolderViewItem() +{ + LLFolderViewItem* itemp = mInventoryPanel->getRootFolder()->getItemByID(mUUID); + if (itemp) + { + itemp->refresh(); + } +} + +// virtual +void LLCallingCardBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action) +{ + if ("begin_im" == action) + { + LLViewerInventoryItem *item = getItem(); + if (item && (item->getCreatorUUID() != gAgent.getID()) && + (!item->getCreatorUUID().isNull())) + { + gIMView->setFloaterOpen(TRUE); + gIMView->addSession(item->getName(), IM_NOTHING_SPECIAL, item->getCreatorUUID()); + } + } + else if ("lure" == action) + { + LLViewerInventoryItem *item = getItem(); + if (item && (item->getCreatorUUID() != gAgent.getID()) && + (!item->getCreatorUUID().isNull())) + { + handle_lure(item->getCreatorUUID()); + } + } + else LLItemBridge::performAction(folder, model, action); +} + +LLViewerImage* LLCallingCardBridge::getIcon() const +{ + BOOL online = FALSE; + LLViewerInventoryItem* item = getItem(); + if(item) + { + online = LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()); + } + return get_item_icon(LLAssetType::AT_CALLINGCARD, LLInventoryType::IT_CALLINGCARD, online); +} + +LLString LLCallingCardBridge::getLabelSuffix() const +{ + LLViewerInventoryItem* item = getItem(); + if( item && LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()) ) + { + return LLItemBridge::getLabelSuffix() + " (online)"; + } + else + { + return LLItemBridge::getLabelSuffix(); + } +} + +void LLCallingCardBridge::openItem() +{ + LLViewerInventoryItem* item = getItem(); + if(item && !item->getCreatorUUID().isNull()) + { + BOOL online; + online = LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()); + LLFloaterAvatarInfo::showFromFriend(item->getCreatorUUID(), online); + } +} + +void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + lldebugs << "LLCallingCardBridge::buildContextMenu()" << llendl; + std::vector<LLString> items; + std::vector<LLString> disabled_items; + + if(isInTrash()) + { + items.push_back("Purge Item"); + if (!isItemRemovable()) + { + disabled_items.push_back("Purge Item"); + } + + items.push_back("Restore Item"); + } + else + { + items.push_back("Open"); + items.push_back("Properties"); + + getClipboardEntries(true, items, disabled_items, flags); + + LLInventoryItem* item = getItem(); + BOOL good_card = (item + && (LLUUID::null != item->getCreatorUUID()) + && (item->getCreatorUUID() != gAgent.getID())); + + items.push_back("Send Instant Message"); + items.push_back("Offer Teleport..."); + + if (!good_card) + { + disabled_items.push_back("Send Instant Message"); + disabled_items.push_back("Offer Teleport..."); + } + } + hideContextEntries(menu, items, disabled_items); +} + +BOOL LLCallingCardBridge::dragOrDrop(MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data) +{ + LLViewerInventoryItem* item = getItem(); + BOOL rv = FALSE; + if(item) + { + // check the type + switch(cargo_type) + { + case DAD_TEXTURE: + case DAD_SOUND: + case DAD_LANDMARK: + case DAD_SCRIPT: + case DAD_CLOTHING: + case DAD_OBJECT: + case DAD_NOTECARD: + case DAD_BODYPART: + case DAD_ANIMATION: + case DAD_GESTURE: + { + LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data; + const LLPermissions& perm = inv_item->getPermissions(); + if(gInventory.getItem(inv_item->getUUID()) + && perm.allowOperationBy(PERM_TRANSFER, gAgent.getID())) + { + rv = TRUE; + if(drop) + { + LLToolDragAndDrop::giveInventory(item->getCreatorUUID(), + (LLInventoryItem*)cargo_data); + } + } + 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. + rv = FALSE; + } + break; + } + case DAD_CATEGORY: + { + LLInventoryCategory* inv_cat = (LLInventoryCategory*)cargo_data; + if( gInventory.getCategory( inv_cat->getUUID() ) ) + { + rv = TRUE; + if(drop) + { + LLToolDragAndDrop::giveInventoryCategory( + item->getCreatorUUID(), + inv_cat); + } + } + 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. + rv = FALSE; + } + break; + } + } + } + return rv; +} + +// +=================================================+ +// | LLNotecardBridge | +// +=================================================+ + +LLString LLNotecardBridge::sPrefix("Note: "); + + +LLViewerImage* LLNotecardBridge::getIcon() const +{ + return get_item_icon(LLAssetType::AT_NOTECARD, LLInventoryType::IT_NOTECARD, 0); +} + +void open_notecard(const LLUUID& item_id, + const LLString& title, + BOOL show_keep_discard, + const LLUUID& source_id, + BOOL take_focus) +{ + // See if we can bring an existing preview to the front + if(!LLPreview::show(item_id, take_focus)) + { + // There isn't one, so make a new preview + S32 left, top; + gFloaterView->getNewFloaterPosition(&left, &top); + LLRect rect = gSavedSettings.getRect("NotecardEditorRect"); + rect.translate(left - rect.mLeft, top - rect.mTop); + LLPreviewNotecard* preview; + preview = new LLPreviewNotecard("preview notecard", + rect, + title, + item_id, + LLUUID::null, + LLUUID::null, + show_keep_discard); + preview->setSourceID(source_id); + if(take_focus) preview->setFocus(TRUE); + // Force to be entirely onscreen. + gFloaterView->adjustToFitScreen(preview, FALSE); + + //if (source_id.notNull()) + //{ + // // look for existing tabbed view for content from same source + // LLPreview* existing_preview = LLPreview::getPreviewForSource(source_id); + // if (existing_preview) + // { + // // found existing preview from this source + // // is it already hosted in a multi-preview window? + // LLMultiPreview* preview_hostp = (LLMultiPreview*)existing_preview->getHost(); + // if (!preview_hostp) + // { + // // create new multipreview if it doesn't exist + // LLMultiPreview* preview_hostp = new LLMultiPreview(existing_preview->getRect()); + + // preview_hostp->addFloater(existing_preview); + // } + // // add this preview to existing host + // preview_hostp->addFloater(preview); + // } + //} + + } +} + +void LLNotecardBridge::openItem() +{ + LLViewerInventoryItem* item = getItem(); + if (item) + { + open_notecard(mUUID, getPrefix() + item->getName(), FALSE); + } +} + + +// +=================================================+ +// | LLGestureBridge | +// +=================================================+ + +LLString LLGestureBridge::sPrefix("Gesture: "); + +LLViewerImage* LLGestureBridge::getIcon() const +{ + return get_item_icon(LLAssetType::AT_GESTURE, LLInventoryType::IT_GESTURE, 0); +} + +LLFontGL::StyleFlags LLGestureBridge::getLabelStyle() const +{ + if( gGestureManager.isGestureActive(mUUID) ) + { + return LLFontGL::BOLD; + } + else + { + return LLFontGL::NORMAL; + } +} + +LLString LLGestureBridge::getLabelSuffix() const +{ + if( gGestureManager.isGestureActive(mUUID) ) + { + return LLItemBridge::getLabelSuffix() + " (active)"; + } + else + { + return LLItemBridge::getLabelSuffix(); + } +} + +// virtual +void LLGestureBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action) +{ + if ("activate" == action) + { + gGestureManager.activateGesture(mUUID); + + LLViewerInventoryItem* item = gInventory.getItem(mUUID); + if (!item) return; + + // Since we just changed the suffix to indicate (active) + // the server doesn't need to know, just the viewer. + gInventory.updateItem(item); + gInventory.notifyObservers(); + } + else if ("deactivate" == action) + { + gGestureManager.deactivateGesture(mUUID); + + LLViewerInventoryItem* item = gInventory.getItem(mUUID); + if (!item) return; + + // Since we just changed the suffix to indicate (active) + // the server doesn't need to know, just the viewer. + gInventory.updateItem(item); + gInventory.notifyObservers(); + } + else LLItemBridge::performAction(folder, model, action); +} + +void LLGestureBridge::openItem() +{ + LLViewerInventoryItem* item = getItem(); + if (!item) return; + + // See if we can bring an existing preview to the front + if(!LLPreview::show(mUUID)) + { + LLUUID item_id = mUUID; + LLString title = getPrefix() + item->getName(); + LLUUID object_id = LLUUID::null; + + // TODO: save the rectangle + LLPreviewGesture* preview = LLPreviewGesture::show(title, item_id, object_id); + preview->setFocus(TRUE); + + // Force to be entirely onscreen. + gFloaterView->adjustToFitScreen(preview, FALSE); + } +} + +BOOL LLGestureBridge::removeItem() +{ + // Force close the preview window, if it exists + LLPreview::hide(mUUID); + gGestureManager.deactivateGesture(mUUID); + return LLItemBridge::removeItem(); +} + +void LLGestureBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + lldebugs << "LLGestureBridge::buildContextMenu()" << llendl; + std::vector<LLString> items; + std::vector<LLString> disabled_items; + if(isInTrash()) + { + items.push_back("Purge Item"); + if (!isItemRemovable()) + { + disabled_items.push_back("Purge Item"); + } + + items.push_back("Restore Item"); + } + else + { + items.push_back("Open"); + items.push_back("Properties"); + + getClipboardEntries(true, items, disabled_items, flags); + + items.push_back("Gesture Separator"); + items.push_back("Activate"); + items.push_back("Deactivate"); + + /*menu.append(new LLMenuItemCallGL("Activate", + handleActivateGesture, + enableActivateGesture, + (void*)this)); + menu.append(new LLMenuItemCallGL("Deactivate", + handleDeactivateGesture, + enableDeactivateGesture, + (void*)this));*/ + } + hideContextEntries(menu, items, disabled_items); +} + +// +=================================================+ +// | LLAnimationBridge | +// +=================================================+ + +LLString LLAnimationBridge::sPrefix("Animation: "); + + +LLViewerImage* LLAnimationBridge::getIcon() const +{ + return get_item_icon(LLAssetType::AT_ANIMATION, LLInventoryType::IT_ANIMATION, 0); +} + +void LLAnimationBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + std::vector<LLString> items; + std::vector<LLString> disabled_items; + + lldebugs << "LLAnimationBridge::buildContextMenu()" << llendl; + if(isInTrash()) + { + items.push_back("Purge Item"); + if (!isItemRemovable()) + { + disabled_items.push_back("Purge Item"); + } + + items.push_back("Restore Item"); + } + else + { + items.push_back("Animation Open"); + items.push_back("Properties"); + + getClipboardEntries(true, items, disabled_items, flags); + } + + items.push_back("Animation Separator"); + items.push_back("Animation Play"); + items.push_back("Animation Audition"); + + hideContextEntries(menu, items, disabled_items); + +} + +// virtual +void LLAnimationBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action) +{ + S32 activate = 0; + + if ((action == "playworld") || (action == "playlocal")) + { + + if ("playworld" == action) activate = 1; + if ("playlocal" == action) activate = 2; + + // See if we can bring an existing preview to the front + if( !LLPreview::show( mUUID ) ) + { + // There isn't one, so make a new preview + LLViewerInventoryItem* item = getItem(); + if( item ) + { + S32 left, top; + gFloaterView->getNewFloaterPosition(&left, &top); + LLRect rect = gSavedSettings.getRect("PreviewAnimRect"); + rect.translate( left - rect.mLeft, top - rect.mTop ); + LLPreviewAnim* preview = new LLPreviewAnim("preview anim", + rect, + getPrefix() + item->getName(), + mUUID, + activate); + // Force to be entirely onscreen. + gFloaterView->adjustToFitScreen(preview, FALSE); + } + } + } + else + { + LLItemBridge::performAction(folder, model, action); + } +} + +void LLAnimationBridge::openItem() +{ + // See if we can bring an existing preview to the front + if( !LLPreview::show( mUUID ) ) + { + // There isn't one, so make a new preview + LLViewerInventoryItem* item = getItem(); + if( item ) + { + S32 left, top; + gFloaterView->getNewFloaterPosition(&left, &top); + LLRect rect = gSavedSettings.getRect("PreviewAnimRect"); + rect.translate( left - rect.mLeft, top - rect.mTop ); + LLPreviewAnim* preview = new LLPreviewAnim("preview anim", + rect, + getPrefix() + item->getName(), + mUUID, + 0); + preview->setFocus(TRUE); + // Force to be entirely onscreen. + gFloaterView->adjustToFitScreen(preview, FALSE); + } + } +} + +// +=================================================+ +// | LLObjectBridge | +// +=================================================+ + +// static +LLString LLObjectBridge::sPrefix("Object: "); + +// static +LLUUID LLObjectBridge::sContextMenuItemID; + +BOOL LLObjectBridge::isItemRemovable() +{ + LLVOAvatar* avatar = gAgent.getAvatarObject(); + if(!avatar) return FALSE; + if(avatar->isWearingAttachment(mUUID)) return FALSE; + return LLInvFVBridge::isItemRemovable(); +} + +LLViewerImage* LLObjectBridge::getIcon() const +{ + return get_item_icon(LLAssetType::AT_OBJECT, mInvType, mAttachPt); +} + +void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment); + +// virtual +void LLObjectBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action) +{ + if ("attach" == action) + { + LLUUID object_id = mUUID; + LLViewerInventoryItem* item; + item = (LLViewerInventoryItem*)gInventory.getItem(object_id); + if(item && gInventory.isObjectDescendentOf(object_id, gAgent.getInventoryRootID())) + { + rez_attachment(item, NULL); + } + else if(item && item->isComplete()) + { + // must be in library. copy it to our inventory and put it on. + LLPointer<LLInventoryCallback> cb = new RezAttachmentCallback(0); + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + LLUUID::null, + std::string(), + cb); + } + gFocusMgr.setKeyboardFocus(NULL, NULL); + } + else if ("detach" == action) + { + LLInventoryItem* item = gInventory.getItem(mUUID); + if( item ) + { + gMessageSystem->newMessageFast(_PREHASH_DetachAttachmentIntoInv); + gMessageSystem->nextBlockFast(_PREHASH_ObjectData ); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + gMessageSystem->addUUIDFast(_PREHASH_ItemID, item->getUUID() ); + + gMessageSystem->sendReliable( gAgent.getRegion()->getHost() ); + } + // this object might have been selected, so let the selection manager know it's gone now + gSelectMgr->remove(gObjectList.findObject(item->getUUID())); + } + else LLItemBridge::performAction(folder, model, action); +} + +void LLObjectBridge::openItem() +{ + /* Disabled -- this preview isn't useful. JC */ + // CP: actually, this code is required - made changes to match LLAnimationBridge::openItem() idiom + // The properties preview is useful, converting to show object properties. - DaveP + LLShowProps::showProperties(mUUID); +} + +LLFontGL::StyleFlags LLObjectBridge::getLabelStyle() const +{ + LLVOAvatar* avatar = gAgent.getAvatarObject(); + if( avatar && avatar->isWearingAttachment( mUUID ) ) + { + return LLFontGL::BOLD; + } + else + { + return LLFontGL::NORMAL; + } +} + +LLString LLObjectBridge::getLabelSuffix() const +{ + LLVOAvatar* avatar = gAgent.getAvatarObject(); + if( avatar && avatar->isWearingAttachment( mUUID ) ) + { + LLString attachment_point_name = avatar->getAttachedPointName(mUUID); + LLString::toLower(attachment_point_name); + return LLItemBridge::getLabelSuffix() + LLString(" (worn on ") + attachment_point_name + LLString(")"); + } + else + { + return LLItemBridge::getLabelSuffix(); + } +} + +void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment) +{ + LLAttachmentRezAction* rez_action = new LLAttachmentRezAction; + rez_action->mItemID = item->getUUID(); + rez_action->mAttachPt = gAgent.getAvatarObject()->mAttachmentPoints.reverseLookup(attachment); + + if (attachment && attachment->getObject(0)) + { + gViewerWindow->alertXml("ReplaceAttachment", confirm_replace_attachment_rez, (void*)rez_action); + } + else + { + confirm_replace_attachment_rez(0/*YES*/, (void*)rez_action); + } +} + +void confirm_replace_attachment_rez(S32 option, void* user_data) +{ + LLAttachmentRezAction* rez_action = (LLAttachmentRezAction*)user_data; + if (option == 0/*YES*/) + { + if (rez_action) + { + LLViewerInventoryItem* itemp = gInventory.getItem(rez_action->mItemID); + + if (itemp) + { + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_RezSingleAttachmentFromInv); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_ObjectData); + msg->addUUIDFast(_PREHASH_ItemID, itemp->getUUID()); + msg->addUUIDFast(_PREHASH_OwnerID, itemp->getPermissions().getOwner()); + msg->addU8Fast(_PREHASH_AttachmentPt, rez_action->mAttachPt); + pack_permissions_slam(msg, itemp->getFlags(), itemp->getPermissions()); + msg->addStringFast(_PREHASH_Name, itemp->getName()); + msg->addStringFast(_PREHASH_Description, itemp->getDescription()); + msg->sendReliable(gAgent.getRegion()->getHost()); + } + } + } + delete rez_action; +} + +void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + std::vector<LLString> items; + std::vector<LLString> disabled_items; + if(isInTrash()) + { + items.push_back("Purge Item"); + if (!isItemRemovable()) + { + disabled_items.push_back("Purge Item"); + } + + items.push_back("Restore Item"); + } + else + { + items.push_back("Properties"); + + getClipboardEntries(true, items, disabled_items, flags); + + LLObjectBridge::sContextMenuItemID = mUUID; + + LLInventoryItem* item = getItem(); + if(item) + { + LLVOAvatar *avatarp = gAgent.getAvatarObject(); + if( !avatarp ) + { + return; + } + + if( avatarp->isWearingAttachment( mUUID ) ) + { + items.push_back("Detach From Yourself"); + } + else + if( !isInTrash() ) + { + items.push_back("Attach Separator"); + items.push_back("Object Wear"); + items.push_back("Attach To"); + items.push_back("Attach To HUD"); + + LLMenuGL* attach_menu = menu.getChildMenuByName("Attach To", TRUE); + LLMenuGL* attach_hud_menu = menu.getChildMenuByName("Attach To HUD", TRUE); + LLVOAvatar *avatarp = gAgent.getAvatarObject(); + if (attach_menu && (attach_menu->getChildCount() == 0) && + attach_hud_menu && (attach_hud_menu->getChildCount() == 0) && + avatarp) + { + for (LLViewerJointAttachment* attachment = avatarp->mAttachmentPoints.getFirstData(); + attachment; + attachment = gAgent.getAvatarObject()->mAttachmentPoints.getNextData()) + { + LLMenuItemCallGL *new_item; + if (attachment->getIsHUDAttachment()) + { + attach_hud_menu->append(new_item = new LLMenuItemCallGL(attachment->getName(), + NULL, //&LLObjectBridge::attachToAvatar, + NULL, &attach_label, (void*)attachment)); + } + else + { + attach_menu->append(new_item = new LLMenuItemCallGL(attachment->getName(), + NULL, //&LLObjectBridge::attachToAvatar, + NULL, &attach_label, (void*)attachment)); + } + + LLSimpleListener* callback = mInventoryPanel->getListenerByName("Inventory.AttachObject"); + + if (callback) + { + new_item->addListener(callback, "on_click", LLSD(attachment->getName())); + } + } + } + } + } + } + hideContextEntries(menu, items, disabled_items); +} + +BOOL LLObjectBridge::renameItem(const LLString& new_name) +{ + if(!isItemRenameable()) return FALSE; + LLPreview::rename(mUUID, getPrefix() + new_name); + LLInventoryModel* model = mInventoryPanel->getModel(); + if(!model) return FALSE; + LLViewerInventoryItem* item = getItem(); + if(item && (item->getName() != new_name)) + { + LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); + new_item->rename(new_name); + buildDisplayName(new_item, mDisplayName); + new_item->updateServer(FALSE); + model->updateItem(new_item); + model->notifyObservers(); + + LLVOAvatar* avatar = gAgent.getAvatarObject(); + if( avatar ) + { + LLViewerObject* obj = avatar->getWornAttachment( item->getUUID() ); + if( obj ) + { + gSelectMgr->deselectAll(); + gSelectMgr->addAsIndividual( obj, SELECT_ALL_TES, FALSE ); + gSelectMgr->setObjectName( new_name ); + gSelectMgr->deselectAll(); + } + } + } + // return FALSE because we either notified observers (& therefore + // rebuilt) or we didn't update. + return FALSE; +} + +// +=================================================+ +// | LLLSLTextBridge | +// +=================================================+ + +LLString LLLSLTextBridge::sPrefix("Script: "); + +LLViewerImage* LLLSLTextBridge::getIcon() const +{ + return get_item_icon(LLAssetType::AT_SCRIPT, LLInventoryType::IT_LSL, 0); +} + +void LLLSLTextBridge::openItem() +{ + // See if we can bring an exiting preview to the front + if(!LLPreview::show(mUUID)) + { + LLViewerInventoryItem* item = getItem(); + if (item) + { + // There isn't one, so make a new preview + S32 left, top; + gFloaterView->getNewFloaterPosition(&left, &top); + LLRect rect = gSavedSettings.getRect("PreviewScriptRect"); + rect.translate(left - rect.mLeft, top - rect.mTop); + + LLPreviewLSL* preview = new LLPreviewLSL("preview lsl text", + rect, + getPrefix() + item->getName(), + mUUID); + preview->setFocus(TRUE); + // keep onscreen + gFloaterView->adjustToFitScreen(preview, FALSE); + } + } +} + +// +=================================================+ +// | LLWearableBridge | +// +=================================================+ + +// HACK to get from avatar inventory to avatar +void wear_inventory_item_on_avatar( LLInventoryItem* item ) +{ + if(item) + { + lldebugs << "wear_inventory_item_on_avatar( " << item->getName() + << " )" << llendl; + + gWearableList.getAsset(item->getAssetUUID(), + item->getName(), + item->getType(), + LLWearableBridge::onWearOnAvatarArrived, + new LLUUID(item->getUUID())); + } +} + +struct LLFoundData +{ + LLFoundData(const LLUUID& item_id, + const LLUUID& asset_id, + const LLString& name, + LLAssetType::EType asset_type) : + mItemID(item_id), + mAssetID(asset_id), + mAssetType(asset_type), + mName(name), + mWearable( NULL ) {} + + LLUUID mItemID; + LLUUID mAssetID; + LLString mName; + LLAssetType::EType mAssetType; + LLWearable* mWearable; +}; + +struct LLWearableHoldingPattern +{ + LLWearableHoldingPattern() : mResolved(0) {} + ~LLWearableHoldingPattern() { mFoundList.deleteAllData(); } + LLDoubleLinkedList<LLFoundData> mFoundList; + S32 mResolved; +}; + + +class LLOutfitObserver : public LLInventoryFetchObserver +{ +public: + LLOutfitObserver(const LLUUID& cat_id, bool copy_items, bool append) : + mCatID(cat_id), + mCopyItems(copy_items), + mAppend(append) + {} + ~LLOutfitObserver() {} + virtual void done(); //public + +protected: + LLUUID mCatID; + bool mCopyItems; + bool mAppend; +}; + +class LLWearInventoryCategoryCallback : public LLInventoryCallback +{ +public: + LLWearInventoryCategoryCallback(const LLUUID& cat_id, bool append) + { + mCatID = cat_id; + mAppend = append; + } + void fire(const LLUUID& item_id) + { + /* + * Do nothing. We only care about the destructor + */ + } + ~LLWearInventoryCategoryCallback() + { + wear_inventory_category_on_avatar(gInventory.getCategory(mCatID), mAppend); + } +private: + LLUUID mCatID; + bool mAppend; +}; + +void LLOutfitObserver::done() +{ + // We now have an outfit ready to be copied to agent inventory. Do + // it, and wear that outfit normally. + if(mCopyItems) + { + LLInventoryCategory* cat = gInventory.getCategory(mCatID); + LLString name; + if(!cat) + { + // should never happen. + name = "New Outfit"; + } + else + { + name = cat->getName(); + } + LLViewerInventoryItem* item = NULL; + item_ref_t::iterator it = mComplete.begin(); + item_ref_t::iterator end = mComplete.end(); + LLUUID pid; + for(; it < end; ++it) + { + item = (LLViewerInventoryItem*)gInventory.getItem(*it); + if(item) + { + if(LLInventoryType::IT_GESTURE == item->getInventoryType()) + { + pid = gInventory.findCategoryUUIDForType(LLAssetType::AT_GESTURE); + } + else + { + pid = gInventory.findCategoryUUIDForType(LLAssetType::AT_CLOTHING); + } + break; + } + } + if(pid.isNull()) + { + pid = gAgent.getInventoryRootID(); + } + + LLUUID cat_id = gInventory.createNewCategory( + pid, + LLAssetType::AT_NONE, + name); + mCatID = cat_id; + LLPointer<LLInventoryCallback> cb = new LLWearInventoryCategoryCallback(mCatID, mAppend); + it = mComplete.begin(); + for(; it < end; ++it) + { + item = (LLViewerInventoryItem*)gInventory.getItem(*it); + if(item) + { + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + cat_id, + std::string(), + cb); + } + } + } + else + { + // Wear the inventory category. + wear_inventory_category_on_avatar(gInventory.getCategory(mCatID), mAppend); + } +} + +class LLOutfitFetch : public LLInventoryFetchDescendentsObserver +{ +public: + LLOutfitFetch(bool copy_items, bool append) : mCopyItems(copy_items), mAppend(append) {} + ~LLOutfitFetch() {} + virtual void done(); +protected: + bool mCopyItems; + bool mAppend; +}; + +void LLOutfitFetch::done() +{ + // What we do here is get the complete information on the items in + // the library, and set up an observer that will wait for that to + // happen. + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + gInventory.collectDescendents(mCompleteFolders.front(), + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH); + S32 count = item_array.count(); + if(!count) + { + llwarns << "Nothing fetched in category " << mCompleteFolders.front() + << llendl; + dec_busy_count(); + gInventory.removeObserver(this); + delete this; + return; + } + + LLOutfitObserver* outfit; + outfit = new LLOutfitObserver(mCompleteFolders.front(), mCopyItems, mAppend); + LLInventoryFetchObserver::item_ref_t ids; + for(S32 i = 0; i < count; ++i) + { + ids.push_back(item_array.get(i)->getUUID()); + } + + // clean up, and remove this as an observer since the call to the + // outfit could notify observers and throw us into an infinite + // loop. + dec_busy_count(); + gInventory.removeObserver(this); + delete this; + + // increment busy count and either tell the inventory to check & + // call done, or add this object to the inventory for observation. + inc_busy_count(); + + // do the fetch + outfit->fetchItems(ids); + if(outfit->isEverythingComplete()) + { + // everything is already here - call done. + outfit->done(); + } + else + { + // it's all on it's way - add an observer, and the inventory + // will call done for us when everything is here. + gInventory.addObserver(outfit); + } +} + +void wear_outfit_by_name(const char* name) +{ + llinfos << "Wearing category " << name << llendl; + inc_busy_count(); + + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + LLNameCategoryCollector has_name(name); + gInventory.collectDescendentsIf(gAgent.getInventoryRootID(), + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH, + has_name); + bool copy_items = false; + LLInventoryCategory* cat = NULL; + if (cat_array.count() > 0) + { + // Just wear the first one that matches + cat = cat_array.get(0); + } + else + { + gInventory.collectDescendentsIf(LLUUID::null, + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH, + has_name); + if(cat_array.count() > 0) + { + cat = cat_array.get(0); + copy_items = true; + } + } + + if(cat) + { + wear_inventory_category(cat, copy_items, false); + } + else + { + llwarns << "Couldn't find outfit " <<name<< " in wear_outfit_by_name()" + << llendl; + } + + dec_busy_count(); +} + +void wear_inventory_category(LLInventoryCategory* category, bool copy, bool append) +{ + if(!category) return; + + lldebugs << "wear_inventory_category( " << category->getName() + << " )" << llendl; + // What we do here is get the complete information on the items in + // the inventory, and set up an observer that will wait for that to + // happen. + LLOutfitFetch* outfit; + outfit = new LLOutfitFetch(copy, append); + LLInventoryFetchDescendentsObserver::folder_ref_t folders; + folders.push_back(category->getUUID()); + outfit->fetchDescendents(folders); + inc_busy_count(); + if(outfit->isEverythingComplete()) + { + // everything is already here - call done. + outfit->done(); + } + else + { + // it's all on it's way - add an observer, and the inventory + // will call done for us when everything is here. + gInventory.addObserver(outfit); + } +} + +// HACK to get from avatar inventory to avatar +void wear_inventory_category_on_avatar( LLInventoryCategory* category, BOOL append ) +{ + // Avoid unintentionally overwriting old wearables. We have to do + // this up front to avoid having to deal with the case of multiple + // wearables being dirty. + if(!category) return; + lldebugs << "wear_inventory_category_on_avatar( " << category->getName() + << " )" << llendl; + + LLWearInfo* userdata = new LLWearInfo; + userdata->mAppend = append; + userdata->mCategoryID = category->getUUID(); + + if( gFloaterCustomize ) + { + gFloaterCustomize->askToSaveAllIfDirty( + wear_inventory_category_on_avatar_step2, + userdata); + } + else + { + wear_inventory_category_on_avatar_step2( + TRUE, + userdata ); + } +} + + +void wear_inventory_category_on_avatar_step2( BOOL proceed, void* userdata ) +{ + LLWearInfo* wear_info = (LLWearInfo*)userdata; + if (!wear_info) return; + + // Find all the wearables that are in the category's subtree. + lldebugs << "wear_inventory_category_on_avatar_step2()" << llendl; + if(proceed) + { + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + LLFindWearables is_wearable; + gInventory.collectDescendentsIf(wear_info->mCategoryID, + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH, + is_wearable); + S32 i; + S32 wearable_count = item_array.count(); + + LLInventoryModel::cat_array_t obj_cat_array; + LLInventoryModel::item_array_t obj_item_array; + LLIsType is_object( LLAssetType::AT_OBJECT ); + gInventory.collectDescendentsIf(wear_info->mCategoryID, + obj_cat_array, + obj_item_array, + LLInventoryModel::EXCLUDE_TRASH, + is_object); + S32 obj_count = obj_item_array.count(); + + // Find all gestures in this folder + LLInventoryModel::cat_array_t gest_cat_array; + LLInventoryModel::item_array_t gest_item_array; + LLIsType is_gesture( LLAssetType::AT_GESTURE ); + gInventory.collectDescendentsIf(wear_info->mCategoryID, + gest_cat_array, + gest_item_array, + LLInventoryModel::EXCLUDE_TRASH, + is_gesture); + S32 gest_count = gest_item_array.count(); + + if( !wearable_count && !obj_count && !gest_count) + { + gViewerWindow->alertXml("CouldNotPutOnOutfit"); + delete wear_info; + return; + } + + // Processes that take time should show the busy cursor + if (wearable_count > 0 || obj_count > 0) + { + inc_busy_count(); + } + + // Activate all gestures in this folder + if (gest_count > 0) + { + llinfos << "Activating " << gest_count << " gestures" << llendl; + + gGestureManager.activateGestures(gest_item_array); + + // Update the inventory item labels to reflect the fact + // they are active. + LLViewerInventoryCategory* catp = gInventory.getCategory(wear_info->mCategoryID); + if (catp) + { + gInventory.updateCategory(catp); + gInventory.notifyObservers(); + } + } + + if(wearable_count > 0) + { + // Note: can't do normal iteration, because if all the + // wearables can be resolved immediately, then the + // callback will be called (and this object deleted) + // before the final getNextData(). + LLWearableHoldingPattern* holder = new LLWearableHoldingPattern; + LLFoundData* found; + LLDynamicArray<LLFoundData*> found_container; + for(i = 0; i < wearable_count; ++i) + { + found = new LLFoundData(item_array.get(i)->getUUID(), + item_array.get(i)->getAssetUUID(), + item_array.get(i)->getName(), + item_array.get(i)->getType()); + holder->mFoundList.addData(found); + found_container.put(found); + } + for(i = 0; i < wearable_count; ++i) + { + gAddToOutfit = wear_info->mAppend; + + found = found_container.get(i); + gWearableList.getAsset(found->mAssetID, + found->mName, + found->mAssetType, + wear_inventory_category_on_avatar_loop, + (void*)holder); + } + } + + + //If not appending and the folder doesn't contain only gestures, take off all attachments. + if (!wear_info->mAppend + && !(wearable_count == 0 && obj_count == 0 && gest_count > 0) ) + { + LLAgent::userRemoveAllAttachments(NULL); + } + + if( obj_count > 0 ) + { + // We've found some attachements. Add these. + + LLVOAvatar* avatar = gAgent.getAvatarObject(); + if( avatar ) + { + // Build a compound message to send all the objects that need to be rezzed. + + // Limit number of packets to send + const S32 MAX_PACKETS_TO_SEND = 10; + const S32 OBJECTS_PER_PACKET = 4; + const S32 MAX_OBJECTS_TO_SEND = MAX_PACKETS_TO_SEND * OBJECTS_PER_PACKET; + if( obj_count > MAX_OBJECTS_TO_SEND ) + { + obj_count = MAX_OBJECTS_TO_SEND; + } + + // Create an id to keep the parts of the compound message together + LLUUID compound_msg_id; + compound_msg_id.generate(); + LLMessageSystem* msg = gMessageSystem; + + for(i = 0; i < obj_count; ++i) + { + if( 0 == (i % OBJECTS_PER_PACKET) ) + { + // Start a new message chunk + msg->newMessageFast(_PREHASH_RezMultipleAttachmentsFromInv); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_HeaderData); + msg->addUUIDFast(_PREHASH_CompoundMsgID, compound_msg_id ); + msg->addU8Fast(_PREHASH_TotalObjects, obj_count ); + msg->addBOOLFast(_PREHASH_FirstDetachAll, !wear_info->mAppend ); + } + + LLInventoryItem* item = obj_item_array.get(i); + msg->nextBlockFast(_PREHASH_ObjectData ); + msg->addUUIDFast(_PREHASH_ItemID, item->getUUID() ); + msg->addUUIDFast(_PREHASH_OwnerID, item->getPermissions().getOwner()); + msg->addU8Fast(_PREHASH_AttachmentPt, 0 ); // Wear at the previous or default attachment point + pack_permissions_slam(msg, item->getFlags(), item->getPermissions()); + msg->addStringFast(_PREHASH_Name, item->getName()); + msg->addStringFast(_PREHASH_Description, item->getDescription()); + + if( (i+1 == obj_count) || ((OBJECTS_PER_PACKET-1) == (i % OBJECTS_PER_PACKET)) ) + { + // End of message chunk + msg->sendReliable( gAgent.getRegion()->getHost() ); + } + } + } + } + } + delete wear_info; + wear_info = NULL; +} + +void wear_inventory_category_on_avatar_loop(LLWearable* wearable, void* data) +{ + LLWearableHoldingPattern* holder = (LLWearableHoldingPattern*)data; + BOOL append= gAddToOutfit; + + if(wearable) + { + for(LLFoundData* data = holder->mFoundList.getFirstData(); + data; + data = holder->mFoundList.getNextData() ) + { + if(wearable->getID() == data->mAssetID) + { + data->mWearable = wearable; + break; + } + } + } + holder->mResolved += 1; + if(holder->mResolved >= holder->mFoundList.getLength()) + { + wear_inventory_category_on_avatar_step3(holder, append); + } +} + +void wear_inventory_category_on_avatar_step3(LLWearableHoldingPattern* holder, BOOL append) +{ + lldebugs << "wear_inventory_category_on_avatar_step3()" << llendl; + LLInventoryItem::item_array_t items; + LLDynamicArray< LLWearable* > wearables; + + // For each wearable type, find the first instance in the category + // that we recursed through. + for( S32 i = 0; i < WT_COUNT; i++ ) + { + for(LLFoundData* data = holder->mFoundList.getFirstData(); + data; + data = holder->mFoundList.getNextData()) + { + LLWearable* wearable = data->mWearable; + if( wearable && ((S32)wearable->getType() == i) ) + { + LLViewerInventoryItem* item; + item = (LLViewerInventoryItem*)gInventory.getItem(data->mItemID); + if( item && (item->getAssetUUID() == wearable->getID()) ) + { + //RN: after discussing with Brashears, I disabled this code + //Metadata should reside in the item, not the asset + //And this code does not handle failed asset uploads properly +// if(!wearable->isMatchedToInventoryItem(item )) +// { +// wearable = gWearableList.createWearableMatchedToInventoryItem( wearable, item ); +// // Now that we have an asset that matches the +// // item, update the item to point to the new +// // asset. +// item->setAssetUUID(wearable->getID()); +// item->updateAssetOnServer(); +// } + items.put(item); + wearables.put(wearable); + } + break; + } + } + } + + if(wearables.count() > 0) + { + gAgent.setWearableOutfit(items, wearables, !append); + gInventory.notifyObservers(); + } + + delete holder; + + dec_busy_count(); +} + +void remove_inventory_category_from_avatar( LLInventoryCategory* category ) +{ + if(!category) return; + lldebugs << "remove_inventory_category_from_avatar( " << category->getName() + << " )" << llendl; + + + LLUUID* uuid = new LLUUID(category->getUUID()); + + if( gFloaterCustomize ) + { + gFloaterCustomize->askToSaveAllIfDirty( + remove_inventory_category_from_avatar_step2, + uuid); + } + else + { + remove_inventory_category_from_avatar_step2( + TRUE, + uuid ); + } +} + + +void remove_inventory_category_from_avatar_step2( BOOL proceed, void* userdata) +{ + + // Find all the wearables that are in the category's subtree. + LLUUID* category_id = (LLUUID *)userdata; + + lldebugs << "remove_inventory_category_from_avatar_step2()" << llendl; + if(proceed) + { + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + LLFindWearables is_wearable; + gInventory.collectDescendentsIf(*category_id, + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH, + is_wearable); + S32 i; + S32 wearable_count = item_array.count(); + + LLInventoryModel::cat_array_t obj_cat_array; + LLInventoryModel::item_array_t obj_item_array; + LLIsType is_object( LLAssetType::AT_OBJECT ); + gInventory.collectDescendentsIf(*category_id, + obj_cat_array, + obj_item_array, + LLInventoryModel::EXCLUDE_TRASH, + is_object); + S32 obj_count = obj_item_array.count(); + + // Find all gestures in this folder + LLInventoryModel::cat_array_t gest_cat_array; + LLInventoryModel::item_array_t gest_item_array; + LLIsType is_gesture( LLAssetType::AT_GESTURE ); + gInventory.collectDescendentsIf(*category_id, + gest_cat_array, + gest_item_array, + LLInventoryModel::EXCLUDE_TRASH, + is_gesture); + S32 gest_count = gest_item_array.count(); + + if (wearable_count > 0) //Loop through wearables. If worn, remove. + { + for(i = 0; i < wearable_count; ++i) + { + if( gAgent.isWearingItem (item_array.get(i)->getUUID()) ) + { + gWearableList.getAsset(item_array.get(i)->getAssetUUID(), + item_array.get(i)->getName(), + item_array.get(i)->getType(), + LLWearableBridge::onRemoveFromAvatarArrived, + new LLUUID(item_array.get(i)->getUUID())); + + } + } + } + + + if (obj_count > 0) + { + for(i = 0; i < obj_count; ++i) + { + gMessageSystem->newMessageFast(_PREHASH_DetachAttachmentIntoInv); + gMessageSystem->nextBlockFast(_PREHASH_ObjectData ); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + gMessageSystem->addUUIDFast(_PREHASH_ItemID, obj_item_array.get(i)->getUUID() ); + + gMessageSystem->sendReliable( gAgent.getRegion()->getHost() ); + + // this object might have been selected, so let the selection manager know it's gone now + gSelectMgr->remove(gObjectList.findObject( obj_item_array.get(i)->getUUID()) ); + } + } + + if (gest_count > 0) + { + for(i = 0; i < gest_count; ++i) + { + if ( gGestureManager.isGestureActive( gest_item_array.get(i)->getUUID()) ) + { + gGestureManager.deactivateGesture( gest_item_array.get(i)->getUUID() ); + gInventory.updateItem( gest_item_array.get(i) ); + gInventory.notifyObservers(); + } + + } + } + } + delete category_id; + category_id = NULL; +} + +BOOL LLWearableBridge::renameItem(const LLString& new_name) +{ + if( gAgent.isWearingItem( mUUID ) ) + { + gAgent.setWearableName( mUUID, new_name ); + } + return LLItemBridge::renameItem(new_name); +} + +BOOL LLWearableBridge::isItemRemovable() +{ + if(gAgent.isWearingItem(mUUID)) return FALSE; + return LLInvFVBridge::isItemRemovable(); +} + +LLFontGL::StyleFlags LLWearableBridge::getLabelStyle() const +{ + if( gAgent.isWearingItem( mUUID ) ) + { + // llinfos << "BOLD" << llendl; + return LLFontGL::BOLD; + } + else + { + return LLFontGL::NORMAL; + } +} + +LLString LLWearableBridge::getLabelSuffix() const +{ + if( gAgent.isWearingItem( mUUID ) ) + { + return LLItemBridge::getLabelSuffix() + " (worn)"; + } + else + { + return LLItemBridge::getLabelSuffix(); + } +} + +LLViewerImage* LLWearableBridge::getIcon() const +{ + return get_item_icon(mAssetType, mInvType, mWearableType); +} + +// virtual +void LLWearableBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action) +{ + if ("wear" == action) + { + wearOnAvatar(); + } + else if ("edit" == action) + { + editOnAvatar(); + return; + } + else if ("take_off" == action) + { + if(gAgent.isWearingItem(mUUID)) + { + LLViewerInventoryItem* item = getItem(); + if (item) + { + gWearableList.getAsset(item->getAssetUUID(), + item->getName(), + item->getType(), + LLWearableBridge::onRemoveFromAvatarArrived, + new LLUUID(mUUID)); + } + } + } + else LLItemBridge::performAction(folder, model, action); +} + +void LLWearableBridge::openItem() +{ + if( isInTrash() ) + { + gViewerWindow->alertXml("CannotWearTrash"); + } + else if(isAgentInventory()) + { + if( !gAgent.isWearingItem( mUUID ) ) + { + wearOnAvatar(); + } + } + else + { + // must be in the inventory library. copy it to our inventory + // and put it on right away. + LLViewerInventoryItem* item = getItem(); + if(item && item->isComplete()) + { + LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback(); + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + LLUUID::null, + std::string(), + cb); + } + else if(item) + { + // *FIX: Could in theory fetch and then do the operation above. + gViewerWindow->alertXml("CannotWearInfoNotComplete"); + } + } +} + +void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + lldebugs << "LLWearableBridge::buildContextMenu()" << llendl; + std::vector<LLString> items; + std::vector<LLString> disabled_items; + if(isInTrash()) + { + items.push_back("Purge Item"); + if (!isItemRemovable()) + { + disabled_items.push_back("Purge Item"); + } + + items.push_back("Restore Item"); + } + else + { + BOOL no_open = ((flags & SUPPRESS_OPEN_ITEM) == SUPPRESS_OPEN_ITEM); + if (!no_open) + { + items.push_back("Open"); + } + + items.push_back("Properties"); + + getClipboardEntries(true, items, disabled_items, flags); + + items.push_back("Wearable Separator"); + items.push_back("Wearable Wear"); + items.push_back("Wearable Edit"); + if ((flags & FIRST_SELECTED_ITEM) == 0) + { + disabled_items.push_back("Wearable Edit"); + } + /*menu.appendSeparator(); + menu.append(new LLMenuItemCallGL("Wear", + LLWearableBridge::onWearOnAvatar, + LLWearableBridge::canWearOnAvatar, + (void*)this)); + menu.append(new LLMenuItemCallGL("Edit", + LLWearableBridge::onEditOnAvatar, + LLWearableBridge::canEditOnAvatar, + (void*)this));*/ + + LLViewerInventoryItem* item = getItem(); + if( item && (item->getType() == LLAssetType::AT_CLOTHING) ) + { + items.push_back("Take Off"); + /*menu.append(new LLMenuItemCallGL("Take Off", + LLWearableBridge::onRemoveFromAvatar, + LLWearableBridge::canRemoveFromAvatar, + (void*)this));*/ + } + } + hideContextEntries(menu, items, disabled_items); +} + +// Called from menus +// static +BOOL LLWearableBridge::canWearOnAvatar(void* user_data) +{ + LLWearableBridge* self = (LLWearableBridge*)user_data; + if(!self) return FALSE; + if(!self->isAgentInventory()) + { + LLViewerInventoryItem* item = (LLViewerInventoryItem*)self->getItem(); + if(!item || !item->isComplete()) return FALSE; + } + return (!gAgent.isWearingItem(self->mUUID)); +} + +// Called from menus +// static +void LLWearableBridge::onWearOnAvatar(void* user_data) +{ + LLWearableBridge* self = (LLWearableBridge*)user_data; + if(!self) return; + self->wearOnAvatar(); +} + +void LLWearableBridge::wearOnAvatar() +{ + // Don't wear anything until initial wearables are loaded, can + // destroy clothing items. + if (!gAgent.areWearablesLoaded()) + { + gViewerWindow->alertXml("CanNotChangeAppearanceUntilLoaded"); + return; + } + + LLViewerInventoryItem* item = getItem(); + if(item) + { + if(!isAgentInventory()) + { + LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback(); + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + LLUUID::null, + std::string(), + cb); + } + else + { + wear_inventory_item_on_avatar(item); + } + } +} + +// static +void LLWearableBridge::onWearOnAvatarArrived( LLWearable* wearable, void* userdata ) +{ + LLUUID* item_id = (LLUUID*) userdata; + if(wearable) + { + LLViewerInventoryItem* item = NULL; + item = (LLViewerInventoryItem*)gInventory.getItem(*item_id); + if(item) + { + if(item->getAssetUUID() == wearable->getID()) + { + //RN: after discussing with Brashears, I disabled this code + //Metadata should reside in the item, not the asset + //And this code does not handle failed asset uploads properly + +// if(!wearable->isMatchedToInventoryItem(item)) +// { +// LLWearable* new_wearable = gWearableList.createWearableMatchedToInventoryItem( wearable, item ); +// +// // Now that we have an asset that matches the +// // item, update the item to point to the new +// // asset. +// item->setAssetUUID(new_wearable->getID()); +// item->updateAssetOnServer(); +// wearable = new_wearable; +// } + gAgent.setWearable(item, wearable); + gInventory.notifyObservers(); + //self->getFolderItem()->refreshFromRoot(); + } + else + { + llinfos << "By the time wearable asset arrived, its inv item already pointed to a different asset." << llendl; + } + } + } + delete item_id; +} + +// static +BOOL LLWearableBridge::canEditOnAvatar(void* user_data) +{ + LLWearableBridge* self = (LLWearableBridge*)user_data; + if(!self) return FALSE; + + return (gAgent.isWearingItem(self->mUUID)); +} + +// static +void LLWearableBridge::onEditOnAvatar(void* user_data) +{ + LLWearableBridge* self = (LLWearableBridge*)user_data; + if(self) + { + self->editOnAvatar(); + } +} + +void LLWearableBridge::editOnAvatar() +{ + LLWearable* wearable = gAgent.getWearableFromWearableItem(mUUID); + if( wearable ) + { + // Set the tab to the right wearable. + LLFloaterCustomize::setCurrentWearableType( wearable->getType() ); + + if( CAMERA_MODE_CUSTOMIZE_AVATAR != gAgent.getCameraMode() ) + { + // Start Avatar Customization + gAgent.changeCameraToCustomizeAvatar(); + } + } +} + +// static +BOOL LLWearableBridge::canRemoveFromAvatar(void* user_data) +{ + LLWearableBridge* self = (LLWearableBridge*)user_data; + if( self && (LLAssetType::AT_BODYPART != self->mAssetType) ) + { + return gAgent.isWearingItem( self->mUUID ); + } + return FALSE; +} + +// static +void LLWearableBridge::onRemoveFromAvatar(void* user_data) +{ + LLWearableBridge* self = (LLWearableBridge*)user_data; + if(!self) return; + if(gAgent.isWearingItem(self->mUUID)) + { + LLViewerInventoryItem* item = self->getItem(); + if (item) + { + gWearableList.getAsset(item->getAssetUUID(), + item->getName(), + item->getType(), + onRemoveFromAvatarArrived, + new LLUUID(self->mUUID)); + } + } +} + +// static +void LLWearableBridge::onRemoveFromAvatarArrived(LLWearable* wearable, + void* userdata) +{ + LLUUID* item_id = (LLUUID*) userdata; + if(wearable) + { + if( gAgent.isWearingItem( *item_id ) ) + { + EWearableType type = wearable->getType(); + + if( !(type==WT_SHAPE || type==WT_SKIN || type==WT_HAIR ) ) //&& + //!((gAgent.mAccess >= SIM_ACCESS_MATURE) && ( type==WT_UNDERPANTS || type==WT_UNDERSHIRT )) ) + { + gAgent.removeWearable( type ); + } + } + } + delete item_id; +} |