summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLoren Shih <seraph@lindenlab.com>2009-08-12 19:03:20 +0000
committerLoren Shih <seraph@lindenlab.com>2009-08-12 19:03:20 +0000
commit7bbc5cdea6beb4e05c26d1472f789fe6fa536ee3 (patch)
tree9e496156bb377c9bdf0b793a1b07b72dfa4f032c
parent0bf4b5f2222ffb8171be094613363427f3b8470a (diff)
svn merge -r129617:130277 svn+ssh://svn.lindenlab.com/svn/linden/branches/avatar-pipeline/currently-worn-folder-5 into svn+ssh://svn.lindenlab.com/svn/linden/branches/viewer/viewer-2.0.0-3
For DEV-34223 : AVP Current Outfit Folder For DEV-37485 : AVP Appearance Side Panel For DEV-35335 : AVP Automatic Folder Classification This merges the Appearance Side Panel / Ensemble Typing / Current Outfit Folder work for the AVP team.
-rw-r--r--indra/llcommon/llassettype.cpp6
-rw-r--r--indra/llcommon/llassettype.h1
-rw-r--r--indra/llinventory/llinventory.cpp5
-rw-r--r--indra/llinventory/llinventory.h2
-rw-r--r--indra/newview/CMakeLists.txt8
-rw-r--r--indra/newview/app_settings/foldertypes.xml16
-rw-r--r--indra/newview/llagentwearables.cpp260
-rw-r--r--indra/newview/llagentwearables.h50
-rw-r--r--indra/newview/llfloaterinventory.cpp4
-rw-r--r--indra/newview/llfloaterinventory.h2
-rw-r--r--indra/newview/llfloaterproperties.cpp2
-rw-r--r--indra/newview/llfolderview.cpp21
-rw-r--r--indra/newview/llfoldervieweventlistener.h1
-rw-r--r--indra/newview/llfolderviewitem.cpp8
-rw-r--r--indra/newview/llinventorybridge.cpp160
-rw-r--r--indra/newview/llinventorybridge.h4
-rw-r--r--indra/newview/llinventoryfilter.cpp32
-rw-r--r--indra/newview/llinventoryfilter.h5
-rw-r--r--indra/newview/llinventorymodel.cpp48
-rw-r--r--indra/newview/llinventorymodel.h8
-rw-r--r--indra/newview/lllookshistorypanel.h72
-rw-r--r--indra/newview/llpanelappearancetab.h65
-rw-r--r--indra/newview/llviewerinventory.cpp100
-rw-r--r--indra/newview/llviewerinventory.h3
-rw-r--r--indra/newview/skins/default/textures/taskpanel/TabIcon_Appearance_Off.pngbin0 -> 808 bytes
-rw-r--r--indra/newview/skins/default/textures/taskpanel/TabIcon_Appearance_Selected.pngbin0 -> 835 bytes
-rw-r--r--indra/newview/skins/default/textures/textures.xml3
-rw-r--r--indra/newview/skins/default/xui/en/floater_inventory.xml16
-rw-r--r--indra/newview/skins/default/xui/en/menu_inventory.xml24
-rw-r--r--indra/newview/skins/default/xui/en/panel_side_tray.xml22
-rw-r--r--indra/newview/skins/default/xui/en/panel_sidetray_home_tab.xml25
-rw-r--r--indra/newview/skins/default/xui/en/strings.xml2
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 &current_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
new file mode 100644
index 0000000000..2dc32a576b
--- /dev/null
+++ b/indra/newview/skins/default/textures/taskpanel/TabIcon_Appearance_Off.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/taskpanel/TabIcon_Appearance_Selected.png b/indra/newview/skins/default/textures/taskpanel/TabIcon_Appearance_Selected.png
new file mode 100644
index 0000000000..bea218a2fb
--- /dev/null
+++ b/indra/newview/skins/default/textures/taskpanel/TabIcon_Appearance_Selected.png
Binary files differ
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>