diff options
32 files changed, 854 insertions, 121 deletions
diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp index d431071c25..835cdbca04 100644 --- a/indra/llcommon/llassettype.cpp +++ b/indra/llcommon/llassettype.cpp @@ -282,6 +282,12 @@ bool LLAssetType::lookupIsProtectedCategoryType(EType asset_type) return true; } +// static +bool LLAssetType::lookupIsEnsembleCategoryType(EType asset_type) +{ + return (asset_type >= AT_FOLDER_ENSEMBLE_START && + asset_type <= AT_FOLDER_ENSEMBLE_END); +} // static. Generate a good default description void LLAssetType::generateDescriptionFor(LLAssetType::EType asset_type, diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h index 8ab7510494..5e51188541 100644 --- a/indra/llcommon/llassettype.h +++ b/indra/llcommon/llassettype.h @@ -186,6 +186,7 @@ public: static const char* lookupCategoryName(EType asset_type); static bool lookupIsProtectedCategoryType(EType asset_type); + static bool lookupIsEnsembleCategoryType(EType asset_type); /* TODO: Change return types from "const char *" to "const std::string &". This is fairly straightforward, but requires changing some calls to use .c_str(). diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp index 59aca12de2..e2a77f1d1e 100644 --- a/indra/llinventory/llinventory.cpp +++ b/indra/llinventory/llinventory.cpp @@ -133,6 +133,11 @@ LLAssetType::EType LLInventoryObject::getActualType() const return mType; } +BOOL LLInventoryObject::getIsLinkType() const +{ + return LLAssetType::lookupIsLinkType(mType); +} + // See LLInventoryItem override. // virtual const LLUUID& LLInventoryObject::getLinkedUUID() const diff --git a/indra/llinventory/llinventory.h b/indra/llinventory/llinventory.h index 5b8f7ba661..2b4d8ed831 100644 --- a/indra/llinventory/llinventory.h +++ b/indra/llinventory/llinventory.h @@ -97,7 +97,7 @@ public: virtual const std::string& getName() const; virtual LLAssetType::EType getType() const; LLAssetType::EType getActualType() const; // bypasses indirection for linked items - + BOOL getIsLinkType() const; // mutators - will not call updateServer(); void setUUID(const LLUUID& new_uuid); void rename(const std::string& new_name); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index a25ea01d11..e52b523a7f 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -278,6 +278,8 @@ set(viewer_SOURCE_FILES llnotify.cpp lloutputmonitorctrl.cpp lloverlaybar.cpp + llpanelappearance.cpp + llpanelappearancetab.cpp llpanelavatar.cpp llpanelavatarrow.cpp llpanelavatartag.cpp @@ -305,6 +307,8 @@ set(viewer_SOURCE_FILES llpanellandmarks.cpp llpanellandmedia.cpp llpanellogin.cpp + llpanellookinfo.cpp + llpanellooks.cpp llpanelmedia.cpp llpanelmeprofile.cpp llpanelmovetip.cpp @@ -719,6 +723,8 @@ set(viewer_HEADER_FILES llnotify.h lloutputmonitorctrl.h lloverlaybar.h + llpanelappearance.h + llpanelappearancetab.h llpanelavatar.h llpanelavatarrow.h llpanelavatartag.h @@ -746,6 +752,8 @@ set(viewer_HEADER_FILES llpanellandmarks.h llpanellandmedia.h llpanellogin.h + llpanellookinfo.h + llpanellooks.h llpanelmedia.h llpanelmeprofile.h llpanelmovetip.h diff --git a/indra/newview/app_settings/foldertypes.xml b/indra/newview/app_settings/foldertypes.xml index 4d4d479bdd..698158308e 100644 --- a/indra/newview/app_settings/foldertypes.xml +++ b/indra/newview/app_settings/foldertypes.xml @@ -9,50 +9,66 @@ asset_num="27" xui_name="head" icon_name="inv_folder_outfit_head.tga" + allowed="hair,eyes" /> <ensemble asset_num="28" xui_name="gloves" icon_name="inv_folder_outfit_gloves.tga" + allowed="gloves" /> <ensemble asset_num="29" xui_name="jacket" icon_name="inv_folder_outfit_jacket.tga" + allowed="jacket" /> <ensemble asset_num="30" xui_name="pants" icon_name="inv_folder_outfit_pants.tga" + allowed="pants,underpants" /> <ensemble asset_num="31" xui_name="shape" icon_name="inv_folder_outfit_shape.tga" + allowed="shape,skin,hair,eyes" /> <ensemble asset_num="32" xui_name="shoes" icon_name="inv_folder_outfit_shoes.tga" + allowed="shoes,socks" /> <ensemble asset_num="33" xui_name="shirt" icon_name="inv_folder_outfit_shirt.tga" + allowed="shirt,undershirt" /> <ensemble asset_num="34" xui_name="skirt" icon_name="inv_folder_outfit_skirt.tga" + allowed="" /> <ensemble asset_num="35" xui_name="underpants" icon_name="inv_folder_outfit_underpants.tga" + allowed="underpants" /> <ensemble asset_num="36" xui_name="undershirt" icon_name="inv_folder_outfit_undershirt.tga" + allowed="undershirt" + /> + <ensemble + asset_num="47" + xui_name="outfit" + icon_name="inv_folder_outfit.tga" + allowed="outfit" /> </ensemble_defs> diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index 22875cbca2..e5e456acb8 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -37,6 +37,7 @@ #include "llfloatercustomize.h" #include "llfloaterinventory.h" +#include "llinventorybridge.h" #include "llinventorymodel.h" #include "llnotify.h" #include "llviewerregion.h" @@ -46,6 +47,9 @@ #include <boost/scoped_ptr.hpp> +// For viewer2.0 internal demo, don't use current outfit folder contents at all during initial startup. Will reenable +// this once we're sure this works completely. +// #define USE_CURRENT_OUTFIT_FOLDER LLAgentWearables gAgentWearables; @@ -662,13 +666,6 @@ BOOL LLAgentWearables::isWearingItem(const LLUUID& item_id, BOOL include_linked_ return FALSE; } -struct InitialWearableData -{ - S32 mType; - U32 mIndex; - LLUUID mItemID; -}; - // MULTI-WEARABLE: update for multiple // static void LLAgentWearables::processAgentInitialWearablesUpdate(LLMessageSystem* mesgsys, void** user_data) @@ -696,14 +693,18 @@ void LLAgentWearables::processAgentInitialWearablesUpdate(LLMessageSystem* mesgs return; } + // Get the UUID of the current outfit folder (will be created if it doesn't exist) + LLUUID current_outfit_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CURRENT_OUTFIT); + + LLOutfitFolderFetch* outfit = new LLOutfitFolderFetch(); + //lldebugs << "processAgentInitialWearablesUpdate()" << llendl; // Add wearables - LLUUID asset_id_array[WT_COUNT]; - LLUUID item_id_array[WT_COUNT]; // MULTI-WEARABLE: TODO: update once messages change. Currently use results to populate the zeroth element. gAgentWearables.mItemsAwaitingWearableUpdate.clear(); for (S32 i=0; i < num_wearables; i++) { + // Parse initial werables data from message system U8 type_u8 = 0; gMessageSystem->getU8Fast(_PREHASH_WearableData, _PREHASH_WearableType, type_u8, i); if (type_u8 >= WT_COUNT) @@ -711,10 +712,10 @@ void LLAgentWearables::processAgentInitialWearablesUpdate(LLMessageSystem* mesgs continue; } const EWearableType type = (EWearableType) type_u8; - + LLUUID item_id; gMessageSystem->getUUIDFast(_PREHASH_WearableData, _PREHASH_ItemID, item_id, i); - + LLUUID asset_id; gMessageSystem->getUUIDFast(_PREHASH_WearableData, _PREHASH_AssetID, asset_id, i); if (asset_id.isNull()) @@ -728,34 +729,77 @@ void LLAgentWearables::processAgentInitialWearablesUpdate(LLMessageSystem* mesgs { continue; } - - // MULTI-WEARABLE: extend arrays to index by type + index. - gAgentWearables.mItemsAwaitingWearableUpdate.insert(item_id); - item_id_array[type] = item_id; - asset_id_array[type] = asset_id; + + // MULTI-WEARABLE: TODO: update once messages change. Currently use results to populate the zeroth element. + + // Store initial wearables data until we know whether we have the current outfit folder or need to use the data. + InitialWearableData * temp_wearable_data = new InitialWearableData(type, 0, item_id, asset_id); // MULTI-WEARABLE: update + outfit->mAgentInitialWearables.push_back(temp_wearable_data); + } lldebugs << " " << LLWearableDictionary::getTypeLabel(type) << 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. + LLInventoryFetchDescendentsObserver::folder_ref_t folders; + folders.push_back(current_outfit_id); + outfit->fetchDescendents(folders); + 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); + } + } +} - // now that we have the asset ids...request the wearable assets - for (S32 i = 0; i < WT_COUNT; i++) +// static +void LLAgentWearables::fetchInitialWearables(initial_wearable_data_vec_t & current_outfit_links, initial_wearable_data_vec_t & message_wearables) +{ +#ifdef USE_CURRENT_OUTFIT_FOLDER + if (!current_outfit_links.empty()) + { + for (U8 i = 0; i < current_outfit_links.size(); ++i) { - // MULTI-WEARABLE: TODO: update once messages change. - // Currently use results to populate the zeroth element. - if (!item_id_array[i].isNull()) - { - InitialWearableData *wear_data = new InitialWearableData; - wear_data->mType = i; - wear_data->mIndex = 0; // MULTI-WEARABLE: update - wear_data->mItemID = item_id_array[i]; - LLWearableList::instance().getAsset(asset_id_array[i], - LLStringUtil::null, - LLWearableDictionary::getAssetType((EWearableType) i), - onInitialWearableAssetArrived, (void*)wear_data); - } + // Fetch the wearables in the current outfit folder + LLWearableList::instance().getAsset(current_outfit_links[i]->mAssetID, + LLStringUtil::null, + LLWearableDictionary::getAssetType(current_outfit_links[i]->mType), + onInitialWearableAssetArrived, (void*)(current_outfit_links[i])); + } + } + else +#endif + if (!message_wearables.empty()) // We have an empty current outfit folder, use the message data instead. + { + LLUUID current_outfit_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CURRENT_OUTFIT); + for (U8 i = 0; i < message_wearables.size(); ++i) + { + // Populate the current outfit folder with links to the wearables passed in the message +#ifdef USE_CURRENT_OUTFIT_FOLDER + std::string link_name = "WearableLink"; + link_inventory_item(gAgent.getID(), message_wearables[i]->mItemID, current_outfit_id, link_name, + LLAssetType::AT_LINK, LLPointer<LLInventoryCallback>(NULL)); +#endif + // Fetch the wearables + LLWearableList::instance().getAsset(message_wearables[i]->mAssetID, + LLStringUtil::null, + LLWearableDictionary::getAssetType(message_wearables[i]->mType), + onInitialWearableAssetArrived, (void*)(message_wearables[i])); } } + else + { + LL_WARNS("Wearables") << "No current outfit folder iterms found and no initial wearables fallback message received." << LL_ENDL; + } } // A single wearable that the avatar was wearing on start-up has arrived from the database. @@ -763,7 +807,7 @@ void LLAgentWearables::processAgentInitialWearablesUpdate(LLMessageSystem* mesgs void LLAgentWearables::onInitialWearableAssetArrived(LLWearable* wearable, void* userdata) { boost::scoped_ptr<InitialWearableData> wear_data((InitialWearableData*)userdata); - const EWearableType type = (EWearableType)wear_data->mType; + const EWearableType type = wear_data->mType; const U32 index = wear_data->mIndex; LLVOAvatarSelf* avatar = gAgent.getAvatarObject(); @@ -775,10 +819,11 @@ void LLAgentWearables::onInitialWearableAssetArrived(LLWearable* wearable, void* if (wearable) { llassert(type == wearable->getType()); + // MULTI-WEARABLE: is this always zeroth element? Change sometime. wearable->setItemID(wear_data->mItemID); - gAgentWearables.setWearable(type,index,wearable); + gAgentWearables.setWearable(type, index, wearable); gAgentWearables.mItemsAwaitingWearableUpdate.erase(wear_data->mItemID); - + // disable composites if initial textures are baked avatar->setupComposites(); @@ -954,6 +999,8 @@ void LLAgentWearables::createStandardWearablesAllDone() mAvatarObject->onFirstTEMessageReceived(); } +// Note: wearables_to_include should be a list of EWearableType types +// attachments_to_include should be a list of attachment points void LLAgentWearables::makeNewOutfit(const std::string& new_folder_name, const LLDynamicArray<S32>& wearables_to_include, const LLDynamicArray<S32>& attachments_to_include, @@ -1087,6 +1134,98 @@ void LLAgentWearables::makeNewOutfit(const std::string& new_folder_name, } } +// Note: wearables_to_include should be a list of EWearableType types +// attachments_to_include should be a list of attachment points +void LLAgentWearables::makeNewOutfitLinks(const std::string& new_folder_name, + const LLDynamicArray<S32>& wearables_to_include, + const LLDynamicArray<S32>& attachments_to_include, + BOOL rename_clothing) +{ + if (mAvatarObject.isNull()) + { + return; + } + + // First, make a folder in the Clothes directory. + LLUUID folder_id = gInventory.createNewCategory( + gInventory.findCategoryUUIDForType(LLAssetType::AT_MY_OUTFITS), + LLAssetType::AT_OUTFIT, + new_folder_name); + +// bool found_first_item = false; + + /////////////////// + // Wearables + + if (wearables_to_include.count()) + { + // Then, iterate though each of the wearables and save links to them in the folder. + S32 i; + S32 count = wearables_to_include.count(); + LLDynamicArray<LLUUID> delete_items; + LLPointer<LLRefCount> cbdone = NULL; + for (i = 0; i < count; ++i) + { + const S32 type = wearables_to_include[i]; + for (U32 j=0; j<getWearableCount((EWearableType)i); j++) + { + LLWearable* old_wearable = getWearable((EWearableType)type,j); + if (old_wearable) + { + std::string new_name; + if (rename_clothing) + { + new_name = new_folder_name; + new_name.append(" "); + new_name.append(old_wearable->getTypeLabel()); + LLStringUtil::truncate(new_name, DB_INV_ITEM_NAME_STR_LEN); + } + + LLViewerInventoryItem* item = gInventory.getItem(getWearableItemID((EWearableType) type, j)); + // BAP TODO + LLPointer<LLInventoryCallback> cb = NULL; + link_inventory_item(gAgent.getID(), + item->getUUID(), + folder_id, + new_name, + LLAssetType::AT_LINK, + cb); + } + } + } + gInventory.notifyObservers(); + } + + + /////////////////// + // Attachments + + if (attachments_to_include.count()) + { + for (S32 i = 0; i < attachments_to_include.count(); i++) + { + S32 attachment_pt = attachments_to_include[i]; + LLViewerJointAttachment* attachment = get_if_there(mAvatarObject->mAttachmentPoints, attachment_pt, (LLViewerJointAttachment*)NULL); + if (!attachment) continue; + LLViewerObject* attached_object = attachment->getObject(); + if (!attached_object) continue; + const LLUUID& item_id = attachment->getItemID(); + if (item_id.isNull()) continue; + LLInventoryItem* item = gInventory.getItem(item_id); + if (!item) continue; + + // BAP link here + LLPointer<LLInventoryCallback> cb = NULL; + link_inventory_item(gAgent.getID(), + item->getUUID(), + folder_id, + item->getName(), + LLAssetType::AT_LINK, + cb); + } + } +} + void LLAgentWearables::makeNewOutfitDone(S32 type, U32 index) { LLUUID first_item_id = getWearableItemID((EWearableType)type, index); @@ -1635,3 +1774,56 @@ void LLAgentWearables::updateServer() sendAgentWearablesUpdate(); gAgent.sendAgentSetAppearance(); } + +void LLAgentWearables::LLOutfitFolderFetch::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(); + LLAgentWearables::initial_wearable_data_vec_t current_outfit_links; + current_outfit_links.reserve(count); + + for(S32 i = 0; i < count; ++i) + { + // A bit of a hack since wearables database doesn't contain asset types... + // Perform indirection in case this assetID is in fact a link. This only works + // because of the assumption that all assetIDs and itemIDs are unique (i.e. + // no assetID is also used as an itemID elsewhere); therefore if the assetID + // exists as an itemID in the user's inventory, then this must be a link. + const LLInventoryItem *linked_item = gInventory.getItem(item_array.get(i)->getUUID()); + LLAssetType::EType asset_type = (LLAssetType::EType) 0; + if (linked_item) + { + asset_type = linked_item->getType(); + LLInventoryItem * base_item = gInventory.getItem(linked_item->getLinkedUUID()); + if (base_item) + { + EWearableType type = (EWearableType) (base_item->getFlags() & LLInventoryItem::II_FLAGS_WEARABLES_MASK); + // MULTI-WEARABLE: update + InitialWearableData * temp_wearable_data = new InitialWearableData(type, 0, linked_item->getLinkedUUID(), base_item->getAssetUUID()); + current_outfit_links.push_back(temp_wearable_data); + } + else + { + llwarns << "Null base_item in LLOutfitFolderFetch::done, linkedUUID is " << linked_item->getLinkedUUID().asString() << llendl; + } + } + else + { + llwarns << "Null linked_item in LLOutfitFolderFetch::done, UUID is " << item_array.get(i)->getUUID().asString() << llendl; + } + } + + gInventory.removeObserver(this); + LLAgentWearables::fetchInitialWearables(current_outfit_links, mAgentInitialWearables); + mAgentInitialWearables.clear(); + delete this; +} + diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h index 977efd71b4..971fd9ee37 100644 --- a/indra/newview/llagentwearables.h +++ b/indra/newview/llagentwearables.h @@ -36,15 +36,34 @@ #include "llmemory.h" #include "lluuid.h" #include "llinventory.h" +#include "llinventorymodel.h" #include "llviewerinventory.h" +#include "llvoavatardefines.h" class LLInventoryItem; class LLVOAvatarSelf; class LLWearable; +// Forward Declaration +class LLInventoryFetchDescendentsObserver; + class LLAgentWearables { //-------------------------------------------------------------------- + // Data Types + //-------------------------------------------------------------------- + typedef struct _InitialWearableData + { + EWearableType mType; + U32 mIndex; + LLUUID mItemID; + LLUUID mAssetID; + _InitialWearableData(EWearableType type, U32 index, LLUUID itemID, LLUUID assetID) : + mType(type), mIndex(index), mItemID(itemID), mAssetID(assetID) { } + } InitialWearableData; + typedef std::vector<InitialWearableData *> initial_wearable_data_vec_t; + + //-------------------------------------------------------------------- // Constructors / destructors / Initializers //-------------------------------------------------------------------- public: @@ -85,13 +104,14 @@ public: U32 getWearableCount(const EWearableType type) const; + //-------------------------------------------------------------------- + // Setters + //-------------------------------------------------------------------- + private: // Low-level data structure setter - public access is via setWearableItem, etc. void setWearable(const EWearableType type, U32 index, LLWearable *wearable); - //-------------------------------------------------------------------- - // Setters - //-------------------------------------------------------------------- public: void setWearableItem(LLInventoryItem* new_item, LLWearable* wearable, bool do_append = false); void setWearableOutfit(const LLInventoryItem::item_array_t& items, const LLDynamicArray< LLWearable* >& wearables, BOOL remove); @@ -127,7 +147,9 @@ protected: // Server Communication //-------------------------------------------------------------------- public: + // Processes the initial wearables update message (if necessary, since the outfit folder makes it redundant) static void processAgentInitialWearablesUpdate(LLMessageSystem* mesgsys, void** user_data); + static void fetchInitialWearables(initial_wearable_data_vec_t & current_outfit_links, initial_wearable_data_vec_t & message_wearables); protected: void sendAgentWearablesUpdate(); void sendAgentWearablesRequest(); @@ -139,10 +161,19 @@ protected: // Outfits //-------------------------------------------------------------------- public: + // Note: wearables_to_include should be a list of EWearableType types + // attachments_to_include should be a list of attachment points void makeNewOutfit(const std::string& new_folder_name, const LLDynamicArray<S32>& wearables_to_include, const LLDynamicArray<S32>& attachments_to_include, - BOOL rename_clothing);protected: + BOOL rename_clothing); + + // Note: wearables_to_include should be a list of EWearableType types + // attachments_to_include should be a list of attachment points + void makeNewOutfitLinks(const std::string& new_folder_name, + const LLDynamicArray<S32>& wearables_to_include, + const LLDynamicArray<S32>& attachments_to_include, + BOOL rename_clothing); private: void makeNewOutfitDone(S32 type, U32 index); @@ -223,6 +254,17 @@ private: U32 mTodo; LLPointer<LLRefCount> mCB; }; + + // Outfit folder fetching callback structure. + class LLOutfitFolderFetch : public LLInventoryFetchDescendentsObserver + { + public: + LLOutfitFolderFetch() {} + ~LLOutfitFolderFetch() {} + virtual void done(); + + LLAgentWearables::initial_wearable_data_vec_t mAgentInitialWearables; + }; }; // LLAgentWearables diff --git a/indra/newview/llfloaterinventory.cpp b/indra/newview/llfloaterinventory.cpp index 33a99facad..718719fe57 100644 --- a/indra/newview/llfloaterinventory.cpp +++ b/indra/newview/llfloaterinventory.cpp @@ -1274,9 +1274,9 @@ void LLInventoryPanel::draw() LLPanel::draw(); } -void LLInventoryPanel::setFilterTypes(U32 filter_types) +void LLInventoryPanel::setFilterTypes(U64 filter_types, BOOL filter_for_categories) { - mFolders->getFilter()->setFilterTypes(filter_types); + mFolders->getFilter()->setFilterTypes(filter_types, filter_for_categories); } void LLInventoryPanel::setFilterPermMask(PermissionMask filter_perm_mask) diff --git a/indra/newview/llfloaterinventory.h b/indra/newview/llfloaterinventory.h index 734ab5032e..fd61e121ea 100644 --- a/indra/newview/llfloaterinventory.h +++ b/indra/newview/llfloaterinventory.h @@ -133,7 +133,7 @@ public: void setSelectCallback(const LLFolderView::signal_t::slot_type& cb) { if (mFolders) mFolders->setSelectCallback(cb); } void clearSelection(); LLInventoryFilter* getFilter() { return mFolders->getFilter(); } - void setFilterTypes(U32 filter); + void setFilterTypes(U64 filter, BOOL filter_for_categories = FALSE); // if filter_for_categories is true, operate on folder preferred asset type U32 getFilterTypes() const { return mFolders->getFilterTypes(); } void setFilterPermMask(PermissionMask filter_perm_mask); U32 getFilterPermMask() const { return mFolders->getFilterPermissions(); } diff --git a/indra/newview/llfloaterproperties.cpp b/indra/newview/llfloaterproperties.cpp index cafaa6dd79..8ac00832c9 100644 --- a/indra/newview/llfloaterproperties.cpp +++ b/indra/newview/llfloaterproperties.cpp @@ -244,7 +244,7 @@ void LLFloaterProperties::refreshFromItem(LLInventoryItem* item) const BOOL can_agent_sell = gAgent.allowOperation(PERM_OWNER, perm, GP_OBJECT_SET_SALE) && !cannot_restrict_permissions; - const BOOL is_link = LLAssetType::lookupIsLinkType(i->getActualType()); + const BOOL is_link = i->getIsLinkType(); // You need permission to modify the object to modify an inventory // item in it. diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index c54eafb67a..4a5a775a05 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -1103,26 +1103,7 @@ void LLFolderView::changeType(LLInventoryModel *model, LLAssetType::EType new_fo if (!folder_bridge) return; LLViewerInventoryCategory *cat = folder_bridge->getCategory(); if (!cat) return; - - const LLUUID &folder_id = cat->getUUID(); - const LLUUID &parent_id = cat->getParentUUID(); - const std::string &name = cat->getName(); - - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_UpdateInventoryFolder); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_FolderData); - msg->addUUIDFast(_PREHASH_FolderID, folder_id); - msg->addUUIDFast(_PREHASH_ParentID, parent_id); - msg->addS8Fast(_PREHASH_Type, new_folder_type); - msg->addStringFast(_PREHASH_Name, name); - gAgent.sendReliableMessage(); - - cat->setPreferredType(new_folder_type); - gInventory.addChangedMask(LLInventoryObserver::LABEL, folder_id); - gInventory.updateLinkedObjects(folder_id); + cat->changeType(new_folder_type); } void LLFolderView::autoOpenItem( LLFolderViewFolder* item ) diff --git a/indra/newview/llfoldervieweventlistener.h b/indra/newview/llfoldervieweventlistener.h index eb06123b46..254ce4062a 100644 --- a/indra/newview/llfoldervieweventlistener.h +++ b/indra/newview/llfoldervieweventlistener.h @@ -62,6 +62,7 @@ public: virtual LLFontGL::StyleFlags getLabelStyle() const = 0; virtual std::string getLabelSuffix() const = 0; virtual void openItem( void ) = 0; + virtual void closeItem( void ) = 0; virtual void previewItem( void ) = 0; virtual void selectItem(void) = 0; virtual void showProperties(void) = 0; diff --git a/indra/newview/llfolderviewitem.cpp b/indra/newview/llfolderviewitem.cpp index a6a8da2a76..69ce2f0e0e 100644 --- a/indra/newview/llfolderviewitem.cpp +++ b/indra/newview/llfolderviewitem.cpp @@ -1912,12 +1912,16 @@ void LLFolderViewFolder::setOpenArrangeRecursively(BOOL openitem, ERecurseType r { BOOL was_open = mIsOpen; mIsOpen = openitem; - if(!was_open && openitem) + if (mListener) { - if(mListener) + if(!was_open && openitem) { mListener->openItem(); } + else if(was_open && !openitem) + { + mListener->closeItem(); + } } if (recurse == RECURSE_DOWN || recurse == RECURSE_UP_DOWN) diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 2ef643e3f7..5f634496d3 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -113,7 +113,7 @@ void dec_busy_count() struct LLWearableHoldingPattern; void wear_add_inventory_item_on_avatar(LLInventoryItem* item); void wear_inventory_category_on_avatar(LLInventoryCategory* category, BOOL append); -void wear_inventory_category_on_avatar_step2( BOOL proceed, LLUUID category, BOOL append); +void wear_inventory_category_on_avatar_step2( BOOL proceed, LLUUID category, BOOL append, BOOL follow_folder_links); 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); @@ -219,7 +219,7 @@ void LLInvFVBridge::renameLinkedItems(const LLUUID &item_id, const std::string& LLInventoryItem* itemp = model->getItem(mUUID); if (!itemp) return; - if (LLAssetType::lookupIsLinkType(itemp->getActualType())) + if (itemp->getIsLinkType()) { return; } @@ -654,7 +654,7 @@ BOOL LLInvFVBridge::isLinkedObjectInTrash() const if (isInTrash()) return TRUE; LLInventoryObject *obj = getInventoryObject(); - if (obj && LLAssetType::lookupIsLinkType(obj->getActualType())) + if (obj && obj->getIsLinkType()) { LLInventoryModel* model = getInventoryModel(); if(!model) return FALSE; @@ -1023,7 +1023,7 @@ void LLItemBridge::restoreToWorld() void LLItemBridge::gotoItem(LLFolderView *folder) { LLInventoryObject *obj = getInventoryObject(); - if (obj && LLAssetType::lookupIsLinkType(obj->getActualType())) + if (obj && obj->getIsLinkType()) { LLInventoryPanel* active_panel = LLFloaterInventory::getActiveInventory()->getPanel(); if (active_panel) @@ -1089,7 +1089,7 @@ LLFontGL::StyleFlags LLItemBridge::getLabelStyle() const } const LLViewerInventoryItem* item = getItem(); - if (item && LLAssetType::lookupIsLinkType(item->getActualType())) + if (item && item->getIsLinkType()) { font |= LLFontGL::ITALIC; } @@ -1116,7 +1116,7 @@ std::string LLItemBridge::getLabelSuffix() const BOOL broken_link = LLAssetType::lookupIsLinkType(item->getType()); if (broken_link) return BROKEN_LINK; - BOOL link = LLAssetType::lookupIsLinkType(item->getActualType()); + BOOL link = item->getIsLinkType(); if (link) return LINK; BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID()); @@ -1241,7 +1241,7 @@ BOOL LLItemBridge::isItemCopyable() const // All items can be copied, not all can be pasted. // The only time an item can't be copied is if it's a link // return (item->getPermissions().allowCopyBy(gAgent.getID())); - if (LLAssetType::lookupIsLinkType(item->getActualType())) + if (item->getIsLinkType()) { return FALSE; } @@ -1441,6 +1441,13 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id); BOOL is_movable = (!LLAssetType::lookupIsProtectedCategoryType(inv_cat->getPreferredType())); + LLUUID current_outfit_id = model->findCategoryUUIDForType(LLAssetType::AT_CURRENT_OUTFIT); + BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); + if (move_is_into_current_outfit) + { + // BAP - restrictions? + is_movable = true; + } if( is_movable ) { gInventory.collectDescendents( cat_id, descendent_categories, descendent_items, FALSE ); @@ -1507,13 +1514,27 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, } } - // Reparent the folder and restamp children if it's moving - // into trash. - LLInvFVBridge::changeCategoryParent( - model, - (LLViewerInventoryCategory*)inv_cat, - mUUID, - move_is_into_trash); + if (current_outfit_id == mUUID) // if target is current outfit folder we use link + { + link_inventory_item( + gAgent.getID(), + inv_cat->getUUID(), + mUUID, + inv_cat->getName(), + LLAssetType::AT_LINK_FOLDER, + LLPointer<LLInventoryCallback>(NULL)); + } + else + { + + // 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) @@ -1899,7 +1920,29 @@ void LLFolderBridge::openItem() lldebugs << "LLFolderBridge::openItem()" << llendl; LLInventoryModel* model = getInventoryModel(); if(!model) return; - model->fetchDescendentsOf(mUUID); + bool fetching_inventory = model->fetchDescendentsOf(mUUID); + // Only change folder type if we have the folder contents. + if (!fetching_inventory) + { + // Disabling this for now, it's causing crash when new items are added to folders + // since folder type may change before new item item has finished processing. + // determineFolderType(); + } +} + +void LLFolderBridge::closeItem() +{ + determineFolderType(); +} + +void LLFolderBridge::determineFolderType() +{ + if (isUpToDate()) + { + LLInventoryModel* model = getInventoryModel(); + LLViewerInventoryCategory* category = model->getCategory(mUUID); + category->determineFolderType(); + } } BOOL LLFolderBridge::isItemRenameable() const @@ -2013,6 +2056,15 @@ LLUIImagePtr LLFolderBridge::getIcon(LLAssetType::EType preferred_type) //TODO - need icon control = "inv_folder_plain_closed.tga"; break; + case LLAssetType::AT_OUTFIT: + control = "inv_folder_outfit.tga"; + break; + case LLAssetType::AT_CURRENT_OUTFIT: + control = "inv_folder_current_outfit.tga"; + break; + case LLAssetType::AT_MY_OUTFITS: + control = "inv_folder_my_outfits.tga"; + break; default: control = "inv_folder_plain_closed.tga"; break; @@ -2199,6 +2251,10 @@ void LLFolderBridge::folderOptionsMenu() } mItems.push_back(std::string("Take Off Items")); } + if (LLAssetType::AT_CURRENT_OUTFIT == category->getPreferredType()) + { + mItems.push_back(std::string("Replace Outfit")); + } hideContextEntries(*mMenu, mItems, disabled_items); } @@ -2253,6 +2309,8 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) else if(isAgentInventory()) // do not allow creating in library { mItems.push_back(std::string("New Folder")); + mItems.push_back(std::string("New Outfit")); + mItems.push_back(std::string("New My Outfits")); mItems.push_back(std::string("New Script")); mItems.push_back(std::string("New Note")); mItems.push_back(std::string("New Gesture")); @@ -2492,7 +2550,9 @@ void LLFolderBridge::modifyOutfit(BOOL append) LLViewerInventoryCategory* cat = getCategory(); if(!cat) return; - wear_inventory_category_on_avatar( cat, append ); + // BAP - was: + // wear_inventory_category_on_avatar( cat, append ); + wear_inventory_category( cat, FALSE, append ); } // helper stuff @@ -3500,7 +3560,7 @@ LLFontGL::StyleFlags LLObjectBridge::getLabelStyle() const } LLInventoryItem* item = getItem(); - if (LLAssetType::lookupIsLinkType(item->getActualType())) + if (item && item->getIsLinkType()) { font |= LLFontGL::ITALIC; } @@ -3599,7 +3659,7 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags) else { LLInventoryItem* item = getItem(); - if (item && LLAssetType::lookupIsLinkType(item->getActualType())) + if (item && item->getIsLinkType()) { items.push_back(std::string("Goto Link")); } @@ -4086,17 +4146,18 @@ void wear_inventory_category_on_avatar( LLInventoryCategory* category, BOOL appe lldebugs << "wear_inventory_category_on_avatar( " << category->getName() << " )" << llendl; + BOOL follow_folder_links = (category->getPreferredType() == LLAssetType::AT_CURRENT_OUTFIT || category->getPreferredType() == LLAssetType::AT_OUTFIT ); if( gFloaterCustomize ) { - gFloaterCustomize->askToSaveIfDirty(boost::bind(wear_inventory_category_on_avatar_step2, _1, category->getUUID(), append)); + gFloaterCustomize->askToSaveIfDirty(boost::bind(wear_inventory_category_on_avatar_step2, _1, category->getUUID(), append, follow_folder_links)); } else { - wear_inventory_category_on_avatar_step2(TRUE, category->getUUID(), append ); + wear_inventory_category_on_avatar_step2(TRUE, category->getUUID(), append, follow_folder_links ); } } -void wear_inventory_category_on_avatar_step2( BOOL proceed, LLUUID category, BOOL append ) +void wear_inventory_category_on_avatar_step2( BOOL proceed, LLUUID category, BOOL append, BOOL follow_folder_links ) { // Find all the wearables that are in the category's subtree. lldebugs << "wear_inventory_category_on_avatar_step2()" << llendl; @@ -4109,7 +4170,8 @@ void wear_inventory_category_on_avatar_step2( BOOL proceed, LLUUID category, BOO cat_array, item_array, LLInventoryModel::EXCLUDE_TRASH, - is_wearable); + is_wearable, + follow_folder_links); S32 i; S32 wearable_count = item_array.count(); @@ -4120,7 +4182,8 @@ void wear_inventory_category_on_avatar_step2( BOOL proceed, LLUUID category, BOO obj_cat_array, obj_item_array, LLInventoryModel::EXCLUDE_TRASH, - is_object); + is_object, + follow_folder_links); S32 obj_count = obj_item_array.count(); // Find all gestures in this folder @@ -4131,7 +4194,8 @@ void wear_inventory_category_on_avatar_step2( BOOL proceed, LLUUID category, BOO gest_cat_array, gest_item_array, LLInventoryModel::EXCLUDE_TRASH, - is_gesture); + is_gesture, + follow_folder_links); S32 gest_count = gest_item_array.count(); if( !wearable_count && !obj_count && !gest_count) @@ -4139,11 +4203,26 @@ void wear_inventory_category_on_avatar_step2( BOOL proceed, LLUUID category, BOO LLNotifications::instance().add("CouldNotPutOnOutfit"); return; } + + const LLUUID ¤t_outfit_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CURRENT_OUTFIT); - // Processes that take time should show the busy cursor if (wearable_count > 0 || obj_count > 0) { + // Processes that take time should show the busy cursor inc_busy_count(); + + // Remove all current outfit folder links if we're now replacing the contents. + if (!append) + { + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + gInventory.collectDescendents(current_outfit_id, cat_array, item_array, + LLInventoryModel::EXCLUDE_TRASH); + for (i = 0; i < item_array.count(); ++i) + { + gInventory.purgeObject(item_array.get(i)->getUUID()); + } + } } // Activate all gestures in this folder @@ -4184,8 +4263,14 @@ void wear_inventory_category_on_avatar_step2( BOOL proceed, LLUUID category, BOO for(i = 0; i < wearable_count; ++i) { gAddToOutfit = append; - found = found_container.get(i); + + // Populate the current outfit folder with links to the newly added wearables + std::string link_name = "WearableLink"; + link_inventory_item(gAgent.getID(), found->mItemID, current_outfit_id, link_name, + LLAssetType::AT_LINK, LLPointer<LLInventoryCallback>(NULL)); + + // Fetch the wearables about to be worn. LLWearableList::instance().getAsset(found->mAssetID, found->mName, found->mAssetType, @@ -4240,9 +4325,9 @@ void wear_inventory_category_on_avatar_step2( BOOL proceed, LLUUID category, BOO msg->addBOOLFast(_PREHASH_FirstDetachAll, !append ); } - LLInventoryItem* item = obj_item_array.get(i); + const LLInventoryItem* item = obj_item_array.get(i).get(); msg->nextBlockFast(_PREHASH_ObjectData ); - msg->addUUIDFast(_PREHASH_ItemID, item->getUUID() ); + msg->addUUIDFast(_PREHASH_ItemID, item->getLinkedUUID()); 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()); @@ -4254,9 +4339,26 @@ void wear_inventory_category_on_avatar_step2( BOOL proceed, LLUUID category, BOO // End of message chunk msg->sendReliable( gAgent.getRegion()->getHost() ); } + + } + + for(i = 0; i < obj_count; ++i) + { + const std::string link_name = "AttachmentLink"; + const LLInventoryItem* item = obj_item_array.get(i).get(); + link_inventory_item(gAgent.getID(), item->getLinkedUUID(), current_outfit_id, link_name, + LLAssetType::AT_LINK, LLPointer<LLInventoryCallback>(NULL)); } } } + + // Create a link to the folder that we wore. + LLViewerInventoryCategory* catp = gInventory.getCategory(category); + if (catp) + { + link_inventory_item(gAgent.getID(), category, current_outfit_id, catp->getName(), + LLAssetType::AT_LINK_FOLDER, LLPointer<LLInventoryCallback>(NULL)); + } } } @@ -4583,7 +4685,7 @@ void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags) items.push_back(std::string("Open")); } - if (item && LLAssetType::lookupIsLinkType(item->getActualType())) + if (item && item->getIsLinkType()) { items.push_back(std::string("Goto Link")); } diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index 915dfec629..5cfebe6c15 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -157,6 +157,7 @@ public: } virtual std::string getLabelSuffix() const { return LLStringUtil::null; } virtual void openItem() {} + virtual void closeItem() {} virtual void gotoItem(LLFolderView *folder) {} // for links virtual void previewItem() {openItem();} virtual void showProperties(); @@ -271,6 +272,7 @@ public: BOOL drop); virtual void performAction(LLFolderView* folder, LLInventoryModel* model, std::string action); virtual void openItem(); + virtual void closeItem(); virtual BOOL isItemRenameable() const; virtual void selectItem(); virtual void restoreItem(); @@ -325,6 +327,8 @@ protected: BOOL checkFolderForContentsOfType(LLInventoryModel* model, LLInventoryCollectFunctor& typeToCheck); void modifyOutfit(BOOL append); + void determineFolderType(); + public: static LLFolderBridge* sSelf; static void staticFolderOptionsMenu(); diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index 9cbe11f5c9..596211f16c 100644 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -39,6 +39,7 @@ #include "llfolderviewitem.h" #include "llinventorymodel.h" // gInventory.backgroundFetchActive() #include "llviewercontrol.h" +#include "llviewerinventory.h" // linden library includes #include "lltrans.h" @@ -51,12 +52,13 @@ LLInventoryFilter::LLInventoryFilter(const std::string& name) mModified(FALSE), mNeedTextRebuild(TRUE) { - mFilterOps.mFilterTypes = 0xffffffff; + mFilterOps.mFilterTypes = 0xffffffffffffffffULL; mFilterOps.mMinDate = time_min(); mFilterOps.mMaxDate = time_max(); mFilterOps.mHoursAgo = 0; mFilterOps.mShowFolderState = SHOW_NON_EMPTY_FOLDERS; mFilterOps.mPermissions = PERM_NONE; + mFilterOps.mFilterForCategories = FALSE; mOrder = SO_FOLDERS_BY_NAME; // This gets overridden by a pref immediately @@ -94,7 +96,25 @@ BOOL LLInventoryFilter::check(LLFolderViewItem* item) } LLFolderViewEventListener* listener = item->getListener(); mSubStringMatchOffset = mFilterSubString.size() ? item->getSearchableLabel().find(mFilterSubString) : std::string::npos; - BOOL passed = (0x1 << listener->getInventoryType() & mFilterOps.mFilterTypes || listener->getInventoryType() == LLInventoryType::IT_NONE) + + bool passed_type = false; + if (mFilterOps.mFilterForCategories) + { + if (listener->getInventoryType() == LLInventoryType::IT_CATEGORY) + { + LLViewerInventoryCategory *cat = gInventory.getCategory(listener->getUUID()); + if (cat) + { + passed_type |= ((1LL << cat->getPreferredType() & mFilterOps.mFilterTypes) != U64(0)); + } + } + } + else + { + passed_type |= ((1LL << listener->getInventoryType() & mFilterOps.mFilterTypes) != U64(0)) || listener->getInventoryType() == LLInventoryType::IT_NONE; + } + + BOOL passed = passed_type && (mFilterSubString.size() == 0 || mSubStringMatchOffset != std::string::npos) && ((listener->getPermissionMask() & mFilterOps.mPermissions) == mFilterOps.mPermissions) && (listener->getCreationDate() >= earliest && listener->getCreationDate() <= mFilterOps.mMaxDate); @@ -124,7 +144,7 @@ BOOL LLInventoryFilter::isNotDefault() BOOL LLInventoryFilter::isActive() { - return mFilterOps.mFilterTypes != 0xffffffff + return mFilterOps.mFilterTypes != 0xffffffffffffffffULL || mFilterSubString.size() || mFilterOps.mPermissions != PERM_NONE || mFilterOps.mMinDate != time_min() @@ -144,7 +164,7 @@ BOOL LLInventoryFilter::isModifiedAndClear() return ret; } -void LLInventoryFilter::setFilterTypes(U32 types) +void LLInventoryFilter::setFilterTypes(U64 types, BOOL filter_for_categories) { if (mFilterOps.mFilterTypes != types) { @@ -168,8 +188,8 @@ void LLInventoryFilter::setFilterTypes(U32 types) { setModified(FILTER_MORE_RESTRICTIVE); } - } + mFilterOps.mFilterForCategories = filter_for_categories; } void LLInventoryFilter::setFilterSubString(const std::string& string) @@ -374,7 +394,7 @@ void LLInventoryFilter::setModified(EFilterBehavior behavior) BOOL LLInventoryFilter::isFilterWith(LLInventoryType::EType t) { - return mFilterOps.mFilterTypes & (0x01 << t); + return mFilterOps.mFilterTypes & (1LL << t); } std::string LLInventoryFilter::getFilterText() diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h index 7c5f6681cf..670b1f000b 100644 --- a/indra/newview/llinventoryfilter.h +++ b/indra/newview/llinventoryfilter.h @@ -64,7 +64,7 @@ public: LLInventoryFilter(const std::string& name); virtual ~LLInventoryFilter(); - void setFilterTypes(U32 types); + void setFilterTypes(U64 types, BOOL filter_for_categories = FALSE); // if filter_for_categories is true, operate on folder preferred asset type U32 getFilterTypes() const { return mFilterOps.mFilterTypes; } void setFilterSubString(const std::string& string); @@ -120,7 +120,8 @@ public: protected: struct filter_ops { - U32 mFilterTypes; + U64 mFilterTypes; + BOOL mFilterForCategories; time_t mMinDate; time_t mMaxDate; U32 mHoursAgo; diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index b4bd312cc0..d5d2897383 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -434,7 +434,8 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id, cat_array_t& cats, item_array_t& items, BOOL include_trash, - LLInventoryCollectFunctor& add) + LLInventoryCollectFunctor& add, + BOOL follow_folder_links) { // Start with categories if(!include_trash) @@ -458,9 +459,38 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id, } } - // Move onto items LLViewerInventoryItem* item = NULL; item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, id); + + // Follow folder links recursively. Currently never goes more + // than one level deep (for current outfit support) + // Note: if making it fully recursive, need more checking against infinite loops. + if (follow_folder_links && item_array) + { + S32 count = item_array->count(); + for(S32 i = 0; i < count; ++i) + { + item = item_array->get(i); + if (item->getActualType() == LLAssetType::AT_LINK_FOLDER) + { + // BAP either getLinkedCategory() should return non-const, or the functor should take const. + LLViewerInventoryCategory *linked_cat = const_cast<LLViewerInventoryCategory*>(item->getLinkedCategory()); + if (linked_cat) + { + if(add(linked_cat,NULL)) + { + // BAP should this be added here? May not + // matter if it's only being used in current + // outfit traversal. + cats.put(LLPointer<LLViewerInventoryCategory>(linked_cat)); + } + collectDescendentsIf(linked_cat->getUUID(), cats, items, include_trash, add, FALSE); + } + } + } + } + + // Move onto items if(item_array) { S32 count = item_array->count(); @@ -872,7 +902,7 @@ void LLInventoryModel::purgeLinkedObjects(const LLUUID &id) LLInventoryObject* objectp = getObject(id); if (!objectp) return; - if (LLAssetType::lookupIsLinkType(objectp->getActualType())) + if (objectp->getIsLinkType()) { return; } @@ -1196,14 +1226,14 @@ void LLInventoryModel::fetchInventoryResponder::error(U32 status, const std::str gInventory.notifyObservers("fetchinventory"); } -void LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) +bool LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) { LLViewerInventoryCategory* cat = getCategory(folder_id); if(!cat) { llwarns << "Asked to fetch descendents of non-existent folder: " << folder_id << llendl; - return; + return false; } //S32 known_descendents = 0; ///cat_array_t* categories = get_ptr_in_map(mParentChildCategoryTree, folder_id); @@ -1216,10 +1246,7 @@ void LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) //{ // known_descendents += items->count(); //} - if(!cat->fetchDescendents()) - { - //llinfos << "Not fetching descendents" << llendl; - } + return cat->fetchDescendents(); } //Initialize statics. @@ -1337,6 +1364,7 @@ void fetchDescendentsResponder::result(const LLSD& content) { cat->setVersion(version); cat->setDescendentCount(descendents); + cat->determineFolderType(); } } @@ -4059,7 +4087,7 @@ bool LLAssetIDMatches::operator()(LLInventoryCategory* cat, LLInventoryItem* ite bool LLLinkedItemIDMatches::operator()(LLInventoryCategory* cat, LLInventoryItem* item) { return (item && - (LLAssetType::lookupIsLinkType(item->getActualType())) && + (item->getIsLinkType()) && (item->getLinkedUUID() == mBaseItemID)); // A linked item's assetID will be the compared-to item's itemID. } diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index f470a77985..e38447a8f2 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -185,7 +185,8 @@ public: cat_array_t& categories, item_array_t& items, BOOL include_trash, - LLInventoryCollectFunctor& add); + LLInventoryCollectFunctor& add, + BOOL follow_folder_links = FALSE); // Collect all items in inventory that are linked to item_id. // Assumes item_id is itself not a linked item. @@ -296,8 +297,9 @@ public: // minimal functionality before the actual arrival of inventory. //void mock(const LLUUID& root_id); - // make sure we have the descendents in the structure. - void fetchDescendentsOf(const LLUUID& folder_id); + // Make sure we have the descendents in the structure. Returns true + // if a fetch was performed. + bool fetchDescendentsOf(const LLUUID& folder_id); // Add categories to a list to be fetched in bulk. static void bulkFetch(std::string url); diff --git a/indra/newview/lllookshistorypanel.h b/indra/newview/lllookshistorypanel.h new file mode 100644 index 0000000000..986c9a1c4d --- /dev/null +++ b/indra/newview/lllookshistorypanel.h @@ -0,0 +1,72 @@ +/** + * @file llpanelteleporthistory.h + * @brief Teleport history represented by a scrolling list + * class definition + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2001-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLPANELLOOKSHISTORY_H +#define LL_LLPANELLOOKSHISTORY_H + +#include "lluictrlfactory.h" +#include "llscrolllistctrl.h" + +#include "llpanelappearancetab.h" +#include "lllookshistory.h" + +class LLLooksHistoryPanel : public LLPanelAppearanceTab +{ +public: + LLLooksHistoryPanel(); + virtual ~LLLooksHistoryPanel(); + + /*virtual*/ BOOL postBuild(); + /*virtual*/ void onSearchEdit(const std::string& string); + /*virtual*/ void onShowOnMap(); + /*virtual*/ void onLooks(); + ///*virtual*/ void onCopySLURL(); + + void showLooksHistory(); + void handleItemSelect(const LLSD& data); + + static void onDoubleClickItem(void* user_data); + +private: + enum LOOKS_HISTORY_COLUMN_ORDER + { + LIST_ICON, + LIST_ITEM_TITLE, + LIST_INDEX + }; + + LLLooksHistory* mLooksHistory; + LLScrollListCtrl* mHistoryItems; + std::string mFilterSubString; +}; + +#endif //LL_LLPANELLOOKSHISTORY_H diff --git a/indra/newview/llpanelappearancetab.h b/indra/newview/llpanelappearancetab.h new file mode 100644 index 0000000000..8a9ba66ec0 --- /dev/null +++ b/indra/newview/llpanelappearancetab.h @@ -0,0 +1,65 @@ +/** + * @file llpanelplacestab.h + * @brief Tabs interface for Side Bar "Places" panel + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2004-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLPANELAPPEARANCETAB_H +#define LL_LLPANELAPPEARANCETAB_H + +#include "llpanel.h" + +#include "llpanelappearance.h" + +class LLPanelAppearanceTab : public LLPanel +{ +public: + LLPanelAppearanceTab(LLPanelAppearance *parent) : + LLPanel(), + mParent(parent) + {} + virtual ~LLPanelAppearanceTab() {} + + virtual void onSearchEdit(const std::string& string) = 0; + virtual void updateVerbs() = 0; // Updates buttons at the bottom of Appearance panel + virtual void onWear() = 0; + virtual void onEdit() = 0; + virtual void onNew() = 0; + + bool isTabVisible(); // Check if parent TabContainer is visible. + + void setPanelAppearanceButtons(LLPanelAppearance* panel); + + +protected: + LLButton* mWearBtn; + LLButton* mEditBtn; + LLPanelAppearance* mParent; +}; + +#endif //LL_LLPANELAPPEARANCETAB_H diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 820abe2fbd..78e8f084c7 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -37,6 +37,7 @@ #include "indra_constants.h" #include "llagent.h" +#include "llfoldertype.h" #include "llviewercontrol.h" #include "llconsole.h" #include "llinventorymodel.h" @@ -587,6 +588,79 @@ bool LLViewerInventoryCategory::exportFileLocal(LLFILE* fp) const return true; } +void LLViewerInventoryCategory::determineFolderType() +{ + LLAssetType::EType original_type = getPreferredType(); + if (LLAssetType::lookupIsProtectedCategoryType(original_type)) + return; + + U64 folder_valid = 0; + U64 folder_invalid = 0; + LLInventoryModel::cat_array_t category_array; + LLInventoryModel::item_array_t item_array; + gInventory.collectDescendents(getUUID(),category_array,item_array,FALSE); + + // For ensembles + if (category_array.empty()) + { + for (LLInventoryModel::item_array_t::iterator item_iter = item_array.begin(); + item_iter != item_array.end(); + item_iter++) + { + const LLViewerInventoryItem *item = (*item_iter); + if (item->getIsLinkType()) + return; + if (item->getInventoryType() == LLInventoryType::IT_WEARABLE) + { + U32 flags = item->getFlags(); + if (flags > WT_COUNT) + return; + const EWearableType wearable_type = EWearableType(flags); + const std::string& wearable_name = LLWearableDictionary::getTypeName(wearable_type); + U64 valid_folder_types = LLFolderType::lookupValidFolderTypes(wearable_name); + folder_valid |= valid_folder_types; + folder_invalid |= ~valid_folder_types; + } + } + for (U8 i = LLAssetType::AT_FOLDER_ENSEMBLE_START; i <= LLAssetType::AT_FOLDER_ENSEMBLE_END; i++) + { + if ((folder_valid & (1LL << i)) && + !(folder_invalid & (1LL << i))) + { + changeType((LLAssetType::EType)i); + return; + } + } + } + if (LLAssetType::lookupIsEnsembleCategoryType(original_type)) + { + changeType(LLAssetType::AT_NONE); + } +} + +void LLViewerInventoryCategory::changeType(LLAssetType::EType new_folder_type) +{ + const LLUUID &folder_id = getUUID(); + const LLUUID &parent_id = getParentUUID(); + const std::string &name = getName(); + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_UpdateInventoryFolder); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_FolderData); + msg->addUUIDFast(_PREHASH_FolderID, folder_id); + msg->addUUIDFast(_PREHASH_ParentID, parent_id); + msg->addS8Fast(_PREHASH_Type, new_folder_type); + msg->addStringFast(_PREHASH_Name, name); + gAgent.sendReliableMessage(); + + setPreferredType(new_folder_type); + gInventory.addChangedMask(LLInventoryObserver::LABEL, folder_id); + gInventory.updateLinkedObjects(folder_id); +} + ///---------------------------------------------------------------------------- /// Local function definitions ///---------------------------------------------------------------------------- @@ -880,16 +954,23 @@ void menu_create_inventory_item(LLFolderView* folder, LLFolderBridge *bridge, co { std::string type = userdata.asString(); - if ("category" == type) + if (("category" == type) || ("current" == type) || ("outfit" == type) || ("my_otfts" == type) ) { + LLAssetType::EType a_type = LLAssetType::AT_NONE; + if ("current" == type) + a_type = LLAssetType::AT_CURRENT_OUTFIT; + if ("outfit" == type) + a_type = LLAssetType::AT_OUTFIT; + if ("my_otfts" == type) + a_type = LLAssetType::AT_MY_OUTFITS; LLUUID category; if (bridge) { - category = gInventory.createNewCategory(bridge->getUUID(), LLAssetType::AT_NONE, LLStringUtil::null); + category = gInventory.createNewCategory(bridge->getUUID(), a_type, LLStringUtil::null); } else { - category = gInventory.createNewCategory(gInventory.getRootFolderID(), LLAssetType::AT_NONE, LLStringUtil::null); + category = gInventory.createNewCategory(gInventory.getRootFolderID(), a_type, LLStringUtil::null); } gInventory.notifyObservers(); folder->setSelectionByID(category, TRUE); @@ -1029,6 +1110,11 @@ const std::string& LLViewerInventoryItem::getName() const const LLPermissions& LLViewerInventoryItem::getPermissions() const { + if (const LLViewerInventoryItem *linked_item = getLinkedItem()) + { + return linked_item->getPermissions(); + } + // Use the actual permissions of the symlink, not its parent. return LLInventoryItem::getPermissions(); } @@ -1070,6 +1156,13 @@ LLInventoryType::EType LLViewerInventoryItem::getInventoryType() const return linked_item->getInventoryType(); } + // Categories don't have types. If this item is an AT_FOLDER_LINK, + // treat it as a category. + if (getLinkedCategory()) + { + return LLInventoryType::IT_CATEGORY; + } + return LLInventoryItem::getInventoryType(); } @@ -1079,7 +1172,6 @@ U32 LLViewerInventoryItem::getFlags() const { return linked_item->getFlags(); } - return LLInventoryItem::getFlags(); } diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 0ee9b21d3a..10309d023b 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -206,7 +206,8 @@ public: // other than cacheing. bool exportFileLocal(LLFILE* fp) const; bool importFileLocal(LLFILE* fp); - + void determineFolderType(); + void changeType(LLAssetType::EType new_folder_type); protected: LLUUID mOwnerID; S32 mVersion; diff --git a/indra/newview/skins/default/textures/taskpanel/TabIcon_Appearance_Off.png b/indra/newview/skins/default/textures/taskpanel/TabIcon_Appearance_Off.png Binary files differnew file mode 100644 index 0000000000..2dc32a576b --- /dev/null +++ b/indra/newview/skins/default/textures/taskpanel/TabIcon_Appearance_Off.png diff --git a/indra/newview/skins/default/textures/taskpanel/TabIcon_Appearance_Selected.png b/indra/newview/skins/default/textures/taskpanel/TabIcon_Appearance_Selected.png Binary files differnew file mode 100644 index 0000000000..bea218a2fb --- /dev/null +++ b/indra/newview/skins/default/textures/taskpanel/TabIcon_Appearance_Selected.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 69742c6ecd..4b6f8ac43c 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -174,6 +174,9 @@ <texture name="Stepper_Up_Off" file_name="widgets/Stepper_Up_Off.png" preload="true"/> <texture name="Stepper_Up_Press" file_name="widgets/Stepper_Up_Press.png" preload="true"/> + <texture name="TabIcon_Appearance_Off" file_name="taskpanel/TabIcon_Appearance_Off.png" preload="false" /> + <texture name="TabIcon_Appearance_Over" file_name="taskpanel/TabIcon_Appearance_Over.png" preload="false"/> + <texture name="TabIcon_Appearance_Selected" file_name="taskpanel/TabIcon_Appearance_Selected.png" preload="false" /> <texture name="TabIcon_Close_Off" file_name="taskpanel/TabIcon_Close_Off.png" preload="false" /> <texture name="TabIcon_Close_Over" file_name="taskpanel/TabIcon_Close_Over.png" preload="false"/> <texture name="TabIcon_Home_Off" file_name="taskpanel/TabIcon_Home_Off.png" preload="false" /> diff --git a/indra/newview/skins/default/xui/en/floater_inventory.xml b/indra/newview/skins/default/xui/en/floater_inventory.xml index 37c6cbf391..610c62a21a 100644 --- a/indra/newview/skins/default/xui/en/floater_inventory.xml +++ b/indra/newview/skins/default/xui/en/floater_inventory.xml @@ -207,6 +207,22 @@ parameter="category" /> </menu_item_call> <menu_item_call + label="New Outfit" + layout="topleft" + name="New Outfit"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="outfit" /> + </menu_item_call> + <menu_item_call + label="New My Outfits" + layout="topleft" + name="New My Outfits"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="my_otfts" /> + </menu_item_call> + <menu_item_call label="New Script" layout="topleft" name="New Script"> diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml index c788f8f095..dd8acea4ed 100644 --- a/indra/newview/skins/default/xui/en/menu_inventory.xml +++ b/indra/newview/skins/default/xui/en/menu_inventory.xml @@ -85,6 +85,22 @@ parameter="current" /> </menu_item_call> <menu_item_call + label="New Outfit" + layout="topleft" + name="New Outfit"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="outfit" /> + </menu_item_call> + <menu_item_call + label="New My Outfits" + layout="topleft" + name="New My Outfits"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="my_otfts" /> + </menu_item_call> + <menu_item_call label="New Script" layout="topleft" name="New Script"> @@ -322,6 +338,14 @@ function="Inventory.DoToSelected" parameter="change_folder_type_undershirt" /> </menu_item_call> + <menu_item_call + label="Outfit" + layout="topleft" + name="Outfit"> + <menu_item_call.on_click + function="Inventory.DoToSelected" + parameter="change_folder_type_outfit" /> + </menu_item_call> </menu> <menu_item_call label="Teleport" diff --git a/indra/newview/skins/default/xui/en/panel_side_tray.xml b/indra/newview/skins/default/xui/en/panel_side_tray.xml index a7b66d8555..58be74a598 100644 --- a/indra/newview/skins/default/xui/en/panel_side_tray.xml +++ b/indra/newview/skins/default/xui/en/panel_side_tray.xml @@ -74,7 +74,7 @@ class="panel_places" name="panel_places" filename="panel_places.xml" - label="Places" + label="Places" border="true" /> </sidetray_tab> @@ -586,5 +586,25 @@ </sidetray_tab> --> + + <sidetray_tab + name="sidebar_appearance" + tab_title="Appearance" + description="Change your looks and appearance." + image="TabIcon_Appearance_Off" + mouse_opaque="false" + background_opaque="false" + background_visible="true" + bg_opaque_color="0.5 0.5 0.5 1.0" + > + <panel + class="panel_appearance" + name="panel_appearance" + filename="panel_appearance.xml" + border="true" + /> + </sidetray_tab> + + </side_tray> diff --git a/indra/newview/skins/default/xui/en/panel_sidetray_home_tab.xml b/indra/newview/skins/default/xui/en/panel_sidetray_home_tab.xml index a85662603d..f10161cecd 100644 --- a/indra/newview/skins/default/xui/en/panel_sidetray_home_tab.xml +++ b/indra/newview/skins/default/xui/en/panel_sidetray_home_tab.xml @@ -85,6 +85,31 @@ mouse_opaque="false" name="tab_description" > Change your profile, your look and quick links to your outfits. </text> + </panel> + <panel + left="10" width="280" height="130" + background_visible="true" + background_opaque="false" + bg_alpha_color="0.3 0.3 0.3 1.0" + name="sidebar_appearance" + follows="left|top|right" + class="panel_sidetray_home_info"> + <text + top="-10" width="200" left="5" height="30" follows="left|right|top" + font="SansSerifHugeBold" text_color="white" word_wrap="true" + mouse_opaque="false" name="tab_name" > + Appearance + </text> + <icon + top="-10" right="-10" width="20" height="20" follows="top|right" + color="1 1 1 1" enabled="true" image_name="inv_item_shirt.tga" + mouse_opaque="false" name="tab_icon"/> + <text + top="-40" left="10" right="-10" height="120" follows="left|right|bottom" + font="SansSerifBig" text_color="white" word_wrap="true" + mouse_opaque="false" name="tab_description" > + Change your appearance. + </text> </panel> </panel>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index ec4898448a..0ed473a01a 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -409,6 +409,8 @@ this texture in your inventory <string name="InvFolder Animations">Animations</string> <string name="InvFolder Gestures">Gestures</string> <string name="InvFolder favorite">Favorites</string> + <string name="InvFolder Current Outfit">Current Outfit</string> + <string name="InvFolder My Outfits">My Outfits</string> <!-- inventory FVBridge --> <string name="Buy">Buy</string> |