summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/CMakeLists.txt2
-rw-r--r--indra/newview/app_settings/settings.xml2
-rw-r--r--indra/newview/llagentwearables.cpp62
-rw-r--r--indra/newview/llagentwearables.h14
-rw-r--r--indra/newview/llappearancemgr.cpp88
-rw-r--r--indra/newview/llappearancemgr.h6
-rw-r--r--indra/newview/llavatarlist.cpp1
-rw-r--r--indra/newview/llavatarlist.h5
-rw-r--r--indra/newview/llavatarlistitem.cpp2
-rw-r--r--indra/newview/llavatarlistitem.h9
-rw-r--r--indra/newview/llbottomtray.cpp43
-rw-r--r--indra/newview/llbottomtray.h4
-rw-r--r--indra/newview/llcofwearables.cpp143
-rw-r--r--indra/newview/llcofwearables.h9
-rw-r--r--indra/newview/llinventorybridge.cpp11
-rw-r--r--indra/newview/llinventoryfunctions.cpp29
-rw-r--r--indra/newview/llinventoryfunctions.h14
-rw-r--r--indra/newview/llinventorymodel.cpp5
-rw-r--r--indra/newview/llinventorymodel.h1
-rw-r--r--indra/newview/lllistcontextmenu.cpp125
-rw-r--r--indra/newview/lllistcontextmenu.h84
-rw-r--r--indra/newview/lloutfitslist.cpp54
-rw-r--r--indra/newview/lloutfitslist.h5
-rw-r--r--indra/newview/llpanelmaininventory.cpp2
-rw-r--r--indra/newview/llpanelobjectinventory.cpp6
-rw-r--r--indra/newview/llpaneloutfitedit.cpp30
-rw-r--r--indra/newview/llpaneloutfitedit.h4
-rw-r--r--indra/newview/llpaneloutfitsinventory.cpp7
-rw-r--r--indra/newview/llpanelpeoplemenus.cpp66
-rw-r--r--indra/newview/llpanelpeoplemenus.h31
-rw-r--r--indra/newview/llpanelteleporthistory.h1
-rw-r--r--indra/newview/llparticipantlist.cpp7
-rw-r--r--indra/newview/llparticipantlist.h4
-rw-r--r--indra/newview/llspeakers.cpp39
-rw-r--r--indra/newview/llspeakers.h8
-rw-r--r--indra/newview/llviewermenu.cpp39
-rw-r--r--indra/newview/llwearableitemslist.cpp144
-rw-r--r--indra/newview/llwearableitemslist.h29
-rw-r--r--indra/newview/llxmlrpctransaction.cpp7
-rw-r--r--indra/newview/skins/default/xui/en/inspect_group.xml6
-rw-r--r--indra/newview/skins/default/xui/en/menu_cof_attachment.xml21
-rw-r--r--indra/newview/skins/default/xui/en/menu_cof_body_part.xml22
-rw-r--r--indra/newview/skins/default/xui/en/menu_cof_clothing.xml42
-rw-r--r--indra/newview/skins/default/xui/en/menu_cof_gear.xml16
-rw-r--r--indra/newview/skins/default/xui/en/menu_outfit_gear.xml50
-rw-r--r--indra/newview/skins/default/xui/en/menu_outfit_tab.xml41
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml113
-rw-r--r--indra/newview/skins/default/xui/en/menu_wearable_list_item.xml58
-rw-r--r--indra/newview/skins/default/xui/en/outfit_accordion_tab.xml1
-rw-r--r--indra/newview/skins/default/xui/en/panel_bottomtray.xml3
-rw-r--r--indra/newview/skins/default/xui/en/panel_cof_wearables.xml3
-rw-r--r--indra/newview/skins/default/xui/en/panel_edit_shape.xml2
-rw-r--r--indra/newview/skins/default/xui/en/panel_outfit_edit.xml1
-rw-r--r--indra/newview/skins/default/xui/en/strings.xml6
54 files changed, 1282 insertions, 245 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 7094d68292..ddd5d47e78 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -268,6 +268,7 @@ set(viewer_SOURCE_FILES
lllandmarkactions.cpp
lllandmarklist.cpp
lllistbrowser.cpp
+ lllistcontextmenu.cpp
lllistview.cpp
lllocaltextureobject.cpp
lllocationhistory.cpp
@@ -786,6 +787,7 @@ set(viewer_HEADER_FILES
lllandmarklist.h
lllightconstants.h
lllistbrowser.h
+ lllistcontextmenu.h
lllistview.h
lllocaltextureobject.h
lllocationhistory.h
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index a4aa90289d..59b6115fab 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -8182,7 +8182,7 @@
<key>Type</key>
<string>S32</string>
<key>Value</key>
- <integer>0</integer>
+ <integer>1</integer>
</map>
<key>ShowObjectRenderingCost</key>
<map>
diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp
index 6b5e43973e..68c4fa1ea0 100644
--- a/indra/newview/llagentwearables.cpp
+++ b/indra/newview/llagentwearables.cpp
@@ -49,6 +49,7 @@
#include "llmd5.h"
#include "llnotificationsutil.h"
#include "llpaneloutfitsinventory.h"
+#include "llsidepanelappearance.h"
#include "llsidetray.h"
#include "lltexlayer.h"
#include "llviewerregion.h"
@@ -539,9 +540,15 @@ void LLAgentWearables::setWearableName(const LLUUID& item_id, const std::string&
BOOL LLAgentWearables::isWearableModifiable(LLWearableType::EType type, U32 index) const
{
LLUUID item_id = getWearableItemID(type, index);
- if (!item_id.isNull())
+ return item_id.notNull() ? isWearableModifiable(item_id) : FALSE;
+}
+
+BOOL LLAgentWearables::isWearableModifiable(const LLUUID& item_id) const
+{
+ const LLUUID& linked_id = gInventory.getLinkedItemID(item_id);
+ if (linked_id.notNull())
{
- LLInventoryItem* item = gInventory.getItem(item_id);
+ LLInventoryItem* item = gInventory.getItem(linked_id);
if (item && item->getPermissions().allowModifyBy(gAgent.getID(),
gAgent.getGroupID()))
{
@@ -595,12 +602,13 @@ LLInventoryItem* LLAgentWearables::getWearableInventoryItem(LLWearableType::ETyp
const LLWearable* LLAgentWearables::getWearableFromItemID(const LLUUID& item_id) const
{
+ const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
for (S32 i=0; i < LLWearableType::WT_COUNT; i++)
{
for (U32 j=0; j < getWearableCount((LLWearableType::EType)i); j++)
{
const LLWearable * curr_wearable = getWearable((LLWearableType::EType)i, j);
- if (curr_wearable && (curr_wearable->getItemID() == item_id))
+ if (curr_wearable && (curr_wearable->getItemID() == base_item_id))
{
return curr_wearable;
}
@@ -812,6 +820,16 @@ LLWearable* LLAgentWearables::getTopWearable(const LLWearableType::EType type)
return getWearable(type, count-1);
}
+LLWearable* LLAgentWearables::getBottomWearable(const LLWearableType::EType type)
+{
+ if (getWearableCount(type) == 0)
+ {
+ return NULL;
+ }
+
+ return getWearable(type, 0);
+}
+
U32 LLAgentWearables::getWearableCount(const LLWearableType::EType type) const
{
wearableentry_map_t::const_iterator wearable_iter = mWearableDatas.find(type);
@@ -860,12 +878,7 @@ const LLUUID LLAgentWearables::getWearableAssetID(LLWearableType::EType type, U3
BOOL LLAgentWearables::isWearingItem(const LLUUID& item_id) const
{
- const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
- if (getWearableFromItemID(base_item_id) != NULL)
- {
- return TRUE;
- }
- return FALSE;
+ return getWearableFromItemID(item_id) != NULL;
}
// MULTI-WEARABLE: DEPRECATED (see backwards compatibility)
@@ -1862,6 +1875,20 @@ void LLAgentWearables::checkWearablesLoaded() const
#endif
}
+// Returns false if the given wearable is already topmost/bottommost
+// (depending on closer_to_body parameter).
+bool LLAgentWearables::canMoveWearable(const LLUUID& item_id, bool closer_to_body)
+{
+ const LLWearable* wearable = getWearableFromItemID(item_id);
+ if (!wearable) return false;
+
+ LLWearableType::EType wtype = wearable->getType();
+ const LLWearable* marginal_wearable = closer_to_body ? getBottomWearable(wtype) : getTopWearable(wtype);
+ if (!marginal_wearable) return false;
+
+ return wearable != marginal_wearable;
+}
+
BOOL LLAgentWearables::areWearablesLoaded() const
{
checkWearablesLoaded();
@@ -1932,6 +1959,23 @@ bool LLAgentWearables::moveWearable(const LLViewerInventoryItem* item, bool clos
return false;
}
+// static
+void LLAgentWearables::editWearable(const LLUUID& item_id)
+{
+ LLViewerInventoryItem* item;
+ LLWearable* wearable;
+
+ if ((item = gInventory.getLinkedItem(item_id)) &&
+ (wearable = gAgentWearables.getWearableFromAssetID(item->getAssetUUID())) &&
+ gAgentWearables.isWearableModifiable(item->getUUID()) &&
+ item->isFinished())
+ {
+ LLPanel* panel = LLSideTray::getInstance()->showPanel("panel_outfit_edit", LLSD());
+ // copied from LLPanelOutfitEdit::onEditWearableClicked()
+ LLSidepanelAppearance::editWearable(wearable, panel->getParent());
+ }
+}
+
void LLAgentWearables::updateServer()
{
sendAgentWearablesUpdate();
diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h
index 734bd9fd47..1f19d1045b 100644
--- a/indra/newview/llagentwearables.h
+++ b/indra/newview/llagentwearables.h
@@ -71,17 +71,18 @@ protected:
public:
BOOL isWearingItem(const LLUUID& item_id) const;
BOOL isWearableModifiable(LLWearableType::EType type, U32 index /*= 0*/) const;
+ BOOL isWearableModifiable(const LLUUID& item_id) const;
+
BOOL isWearableCopyable(LLWearableType::EType type, U32 index /*= 0*/) const;
BOOL areWearablesLoaded() const;
void updateWearablesLoaded();
void checkWearablesLoaded() const;
+ bool canMoveWearable(const LLUUID& item_id, bool closer_to_body);
// Note: False for shape, skin, eyes, and hair, unless you have MORE than 1.
bool canWearableBeRemoved(const LLWearable* wearable) const;
void animateAllWearableParams(F32 delta, BOOL upload_bake);
-
- bool moveWearable(const LLViewerInventoryItem* item, bool closer_to_body);
//--------------------------------------------------------------------
// Accessors
@@ -96,6 +97,7 @@ public:
LLWearable* getWearable(const LLWearableType::EType type, U32 index /*= 0*/);
const LLWearable* getWearable(const LLWearableType::EType type, U32 index /*= 0*/) const;
LLWearable* getTopWearable(const LLWearableType::EType type);
+ LLWearable* getBottomWearable(const LLWearableType::EType type);
U32 getWearableCount(const LLWearableType::EType type) const;
U32 getWearableCount(const U32 tex_index) const;
@@ -134,6 +136,14 @@ protected:
void recoverMissingWearableDone();
//--------------------------------------------------------------------
+ // Editing/moving wearables
+ //--------------------------------------------------------------------
+
+public:
+ static void editWearable(const LLUUID& item_id);
+ bool moveWearable(const LLViewerInventoryItem* item, bool closer_to_body);
+
+ //--------------------------------------------------------------------
// Removing wearables
//--------------------------------------------------------------------
public:
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index e481611ace..e017fffa54 100644
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -152,27 +152,6 @@ public:
};
-//Inventory collect functor collecting wearables of a specific wearable type
-class LLFindClothesOfType : public LLInventoryCollectFunctor
-{
-public:
- LLFindClothesOfType(LLWearableType::EType type) : mWearableType(type) {}
- virtual ~LLFindClothesOfType() {}
- virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item)
- {
- if (!item) return false;
- if (item->getType() != LLAssetType::AT_CLOTHING) return false;
-
- LLViewerInventoryItem *vitem = dynamic_cast<LLViewerInventoryItem*>(item);
- if (!vitem || vitem->getWearableType() != mWearableType) return false;
-
- return true;
- }
-
- const LLWearableType::EType mWearableType;
-};
-
-
LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy():
mFireCount(0)
{
@@ -671,7 +650,7 @@ const LLUUID LLAppearanceMgr::getBaseOutfitUUID()
return outfit_cat->getUUID();
}
-bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_update)
+bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_update, bool replace)
{
if (item_id_to_wear.isNull()) return false;
@@ -692,6 +671,14 @@ bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_up
LLNotificationsUtil::add("CanNotChangeAppearanceUntilLoaded");
return false;
}
+
+ // Remove the existing wearables of the same type.
+ // Remove existing body parts anyway because we must not be able to wear e.g. two skins.
+ if (replace || item_to_wear->getType() == LLAssetType::AT_BODYPART)
+ {
+ removeCOFLinksOfType(item_to_wear->getWearableType(), false);
+ }
+
addCOFItemLink(item_to_wear, do_update);
break;
case LLAssetType::AT_OBJECT:
@@ -711,6 +698,35 @@ void LLAppearanceMgr::changeOutfit(bool proceed, const LLUUID& category, bool ap
LLAppearanceMgr::instance().updateCOF(category,append);
}
+void LLAppearanceMgr::replaceCurrentOutfit(const LLUUID& new_outfit)
+{
+ LLViewerInventoryCategory* cat = gInventory.getCategory(new_outfit);
+ wearInventoryCategory(cat, false, false);
+}
+
+void LLAppearanceMgr::addCategoryToCurrentOutfit(const LLUUID& cat_id)
+{
+ LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
+ wearInventoryCategory(cat, false, true);
+}
+
+void LLAppearanceMgr::takeOffOutfit(const LLUUID& cat_id)
+{
+ LLInventoryModel::cat_array_t cats;
+ LLInventoryModel::item_array_t items;
+ LLFindWearables collector;
+
+ gInventory.collectDescendentsIf(cat_id, cats, items, FALSE, collector);
+
+ LLInventoryModel::item_array_t::const_iterator it = items.begin();
+ const LLInventoryModel::item_array_t::const_iterator it_end = items.end();
+ for( ; it_end != it; ++it)
+ {
+ LLViewerInventoryItem* item = *it;
+ removeItemFromAvatar(item->getUUID());
+ }
+}
+
// Create a copy of src_id + contents as a subfolder of dst_id.
void LLAppearanceMgr::shallowCopyCategory(const LLUUID& src_id, const LLUUID& dst_id,
LLPointer<LLInventoryCallback> cb)
@@ -1563,6 +1579,29 @@ void LLAppearanceMgr::removeCOFItemLinks(const LLUUID& item_id, bool do_update)
}
}
+void LLAppearanceMgr::removeCOFLinksOfType(LLWearableType::EType type, bool do_update)
+{
+ LLFindWearablesOfType filter_wearables_of_type(type);
+ LLInventoryModel::cat_array_t cats;
+ LLInventoryModel::item_array_t items;
+ LLInventoryModel::item_array_t::const_iterator it;
+
+ gInventory.collectDescendentsIf(getCOF(), cats, items, true, filter_wearables_of_type);
+ for (it = items.begin(); it != items.end(); ++it)
+ {
+ const LLViewerInventoryItem* item = *it;
+ if (item->getIsLinkType()) // we must operate on links only
+ {
+ gInventory.purgeObject(item->getUUID());
+ }
+ }
+
+ if (do_update)
+ {
+ updateAppearanceFromCOF();
+ }
+}
+
bool sort_by_linked_uuid(const LLViewerInventoryItem* item1, const LLViewerInventoryItem* item2)
{
if (!item1 || !item2)
@@ -1893,7 +1932,6 @@ void LLAppearanceMgr::removeItemFromAvatar(const LLUUID& id_to_remove)
}
}
-
bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_body)
{
if (!item || !item->isWearableType()) return false;
@@ -1902,11 +1940,11 @@ bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_b
LLInventoryModel::cat_array_t cats;
LLInventoryModel::item_array_t items;
- LLFindClothesOfType filter_wearables_of_type(item->getWearableType());
+ LLFindWearablesOfType filter_wearables_of_type(item->getWearableType());
gInventory.collectDescendentsIf(getCOF(), cats, items, true, filter_wearables_of_type);
if (items.empty()) return false;
- //*TODO all items are not guarantied to have valid descriptions (check?)
+ // We assume that the items have valid descriptions.
std::sort(items.begin(), items.end(), WearablesOrderComparator(item->getWearableType()));
if (closer_to_body && items.front() == item) return false;
diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h
index 516dada39d..96541beb7d 100644
--- a/indra/newview/llappearancemgr.h
+++ b/indra/newview/llappearancemgr.h
@@ -58,6 +58,9 @@ public:
void wearCategoryFinal(LLUUID& cat_id, bool copy_items, bool append);
void wearOutfitByName(const std::string& name);
void changeOutfit(bool proceed, const LLUUID& category, bool append);
+ void replaceCurrentOutfit(const LLUUID& new_outfit);
+ void takeOffOutfit(const LLUUID& cat_id);
+ void addCategoryToCurrentOutfit(const LLUUID& cat_id);
// Copy all items and the src category itself.
void shallowCopyCategory(const LLUUID& src_id, const LLUUID& dst_id,
@@ -81,7 +84,7 @@ public:
const LLUUID getBaseOutfitUUID();
// Wear/attach an item (from a user's inventory) on the agent
- bool wearItemOnAvatar(const LLUUID& item_to_wear, bool do_update = true);
+ bool wearItemOnAvatar(const LLUUID& item_to_wear, bool do_update = true, bool replace = false);
// Update the displayed outfit name in UI.
void updatePanelOutfitName(const std::string& name);
@@ -111,6 +114,7 @@ public:
// Remove COF entries
void removeCOFItemLinks(const LLUUID& item_id, bool do_update = true);
+ void removeCOFLinksOfType(LLWearableType::EType type, bool do_update = true);
// Add COF link to ensemble folder.
void addEnsembleLink(LLInventoryCategory* item, bool do_update = true);
diff --git a/indra/newview/llavatarlist.cpp b/indra/newview/llavatarlist.cpp
index 24290ac089..3275d784a3 100644
--- a/indra/newview/llavatarlist.cpp
+++ b/indra/newview/llavatarlist.cpp
@@ -46,6 +46,7 @@
#include "llavatariconctrl.h"
#include "llcallingcard.h" // for LLAvatarTracker
#include "llcachename.h"
+#include "lllistcontextmenu.h"
#include "llrecentpeople.h"
#include "lluuid.h"
#include "llvoiceclient.h"
diff --git a/indra/newview/llavatarlist.h b/indra/newview/llavatarlist.h
index a9320055ca..fffc6e6e73 100644
--- a/indra/newview/llavatarlist.h
+++ b/indra/newview/llavatarlist.h
@@ -38,6 +38,7 @@
#include "llavatarlistitem.h"
class LLTimer;
+class LLListContextMenu;
/**
* Generic list of avatars.
@@ -77,7 +78,7 @@ public:
uuid_vec_t& getIDs() { return mIDs; }
bool contains(const LLUUID& id);
- void setContextMenu(LLAvatarListItem::ContextMenu* menu) { mContextMenu = menu; }
+ void setContextMenu(LLListContextMenu* menu) { mContextMenu = menu; }
void setSessionID(const LLUUID& session_id) { mSessionID = session_id; }
const LLUUID& getSessionID() { return mSessionID; }
@@ -127,7 +128,7 @@ private:
uuid_vec_t mIDs;
LLUUID mSessionID;
- LLAvatarListItem::ContextMenu* mContextMenu;
+ LLListContextMenu* mContextMenu;
commit_signal_t mRefreshCompleteSignal;
mouse_signal_t mItemDoubleClickSignal;
diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp
index 5a8ad73c83..c74075d67f 100644
--- a/indra/newview/llavatarlistitem.cpp
+++ b/indra/newview/llavatarlistitem.cpp
@@ -1,6 +1,6 @@
/**
* @file llavatarlistitem.cpp
- * @avatar list item source file
+ * @brief avatar list item source file
*
* $LicenseInfo:firstyear=2009&license=viewergpl$
*
diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h
index c6fac7a9f1..ba9c3574d5 100644
--- a/indra/newview/llavatarlistitem.h
+++ b/indra/newview/llavatarlistitem.h
@@ -1,6 +1,6 @@
/**
* @file llavatarlistitem.h
- * @avatar list item header file
+ * @brief avatar list item header file
*
* $LicenseInfo:firstyear=2009&license=viewergpl$
*
@@ -67,13 +67,6 @@ public:
IS_OFFLINE,
} EItemState;
- class ContextMenu
- {
- public:
- virtual void show(LLView* spawning_view, const uuid_vec_t& selected_uuids, S32 x, S32 y) = 0;
- virtual void hide() = 0;
- };
-
/**
* Creates an instance of LLAvatarListItem.
*
diff --git a/indra/newview/llbottomtray.cpp b/indra/newview/llbottomtray.cpp
index caf43f5ddc..ae97460468 100644
--- a/indra/newview/llbottomtray.cpp
+++ b/indra/newview/llbottomtray.cpp
@@ -156,10 +156,6 @@ LLBottomTray::LLBottomTray(const LLSD&)
, mMovementButton(NULL)
, mResizeState(RS_NORESIZE)
, mBottomTrayContextMenu(NULL)
-, mMovementPanel(NULL)
-, mCamPanel(NULL)
-, mSnapshotPanel(NULL)
-, mGesturePanel(NULL)
, mCamButton(NULL)
, mBottomTrayLite(NULL)
, mIsInLiteMode(false)
@@ -421,22 +417,12 @@ void LLBottomTray::updateContextMenu(S32 x, S32 y, MASK mask)
bool in_edit_box = edit_box->pointInView(local_x, local_y);
- LLMenuItemGL* menu_item;
- menu_item = mBottomTrayContextMenu->findChild<LLMenuItemGL>("NearbyChatBar_Cut");
- if(menu_item)
- menu_item->setVisible(in_edit_box);
- menu_item = mBottomTrayContextMenu->findChild<LLMenuItemGL>("NearbyChatBar_Copy");
- if(menu_item)
- menu_item->setVisible(in_edit_box);
- menu_item = mBottomTrayContextMenu->findChild<LLMenuItemGL>("NearbyChatBar_Paste");
- if(menu_item)
- menu_item->setVisible(in_edit_box);
- menu_item = mBottomTrayContextMenu->findChild<LLMenuItemGL>("NearbyChatBar_Delete");
- if(menu_item)
- menu_item->setVisible(in_edit_box);
- menu_item = mBottomTrayContextMenu->findChild<LLMenuItemGL>("NearbyChatBar_Select_All");
- if(menu_item)
- menu_item->setVisible(in_edit_box);
+ mBottomTrayContextMenu->setItemVisible("Separator", in_edit_box);
+ mBottomTrayContextMenu->setItemVisible("NearbyChatBar_Cut", in_edit_box);
+ mBottomTrayContextMenu->setItemVisible("NearbyChatBar_Copy", in_edit_box);
+ mBottomTrayContextMenu->setItemVisible("NearbyChatBar_Paste", in_edit_box);
+ mBottomTrayContextMenu->setItemVisible("NearbyChatBar_Delete", in_edit_box);
+ mBottomTrayContextMenu->setItemVisible("NearbyChatBar_Select_All", in_edit_box);
}
void LLBottomTray::showGestureButton(BOOL visible)
@@ -483,12 +469,8 @@ BOOL LLBottomTray::postBuild()
mNearbyChatBar = getChild<LLNearbyChatBar>("chat_bar");
mToolbarStack = getChild<LLLayoutStack>("toolbar_stack");
- mMovementPanel = getChild<LLPanel>("movement_panel");
- mMovementButton = mMovementPanel->getChild<LLButton>("movement_btn");
- mGesturePanel = getChild<LLPanel>("gesture_panel");
- mCamPanel = getChild<LLPanel>("cam_panel");
- mCamButton = mCamPanel->getChild<LLButton>("camera_btn");
- mSnapshotPanel = getChild<LLPanel>("snapshot_panel");
+ mMovementButton = getChild<LLButton>("movement_btn");
+ mCamButton = getChild<LLButton>("camera_btn");
setRightMouseDownCallback(boost::bind(&LLBottomTray::showBottomTrayContextMenu,this, _2, _3,_4));
mSpeakPanel = getChild<LLPanel>("speak_panel");
@@ -1177,12 +1159,11 @@ bool LLBottomTray::canButtonBeShown(EResizeState processed_object_type) const
void LLBottomTray::initResizeStateContainers()
{
- // *TODO: get rid of mGesturePanel, mMovementPanel, mCamPanel, mSnapshotPanel instance members
// init map with objects should be processed for each type
- mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_GESTURES, mGesturePanel));
- mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_MOVEMENT, mMovementPanel));
- mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_CAMERA, mCamPanel));
- mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_SNAPSHOT, mSnapshotPanel));
+ mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_GESTURES, getChild<LLPanel>("gesture_panel")));
+ mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_MOVEMENT, getChild<LLPanel>("movement_panel")));
+ mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_CAMERA, getChild<LLPanel>("cam_panel")));
+ mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_SNAPSHOT, getChild<LLPanel>("snapshot_panel")));
mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_SIDEBAR, getChild<LLPanel>("sidebar_btn_panel")));
mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_BUILD, getChild<LLPanel>("build_btn_panel")));
mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_SEARCH, getChild<LLPanel>("search_btn_panel")));
diff --git a/indra/newview/llbottomtray.h b/indra/newview/llbottomtray.h
index 889dc42097..c0887df39a 100644
--- a/indra/newview/llbottomtray.h
+++ b/indra/newview/llbottomtray.h
@@ -385,10 +385,6 @@ protected:
LLNearbyChatBar* mNearbyChatBar;
LLLayoutStack* mToolbarStack;
LLMenuGL* mBottomTrayContextMenu;
- LLPanel* mMovementPanel;
- LLPanel* mCamPanel;
- LLPanel* mSnapshotPanel;
- LLPanel* mGesturePanel;
LLButton* mCamButton;
LLButton* mMovementButton;
LLBottomTrayLite* mBottomTrayLite;
diff --git a/indra/newview/llcofwearables.cpp b/indra/newview/llcofwearables.cpp
index 7c4ceb3458..dfc203111a 100644
--- a/indra/newview/llcofwearables.cpp
+++ b/indra/newview/llcofwearables.cpp
@@ -35,9 +35,13 @@
#include "llcofwearables.h"
#include "llagentdata.h"
+#include "llagentwearables.h"
#include "llappearancemgr.h"
#include "llinventory.h"
#include "llinventoryfunctions.h"
+#include "lllistcontextmenu.h"
+#include "llmenugl.h"
+#include "llviewermenu.h"
#include "llwearableitemslist.h"
static LLRegisterPanelClassWrapper<LLCOFAccordionListAdaptor> t_cof_accodion_list_adaptor("accordion_list_adaptor");
@@ -49,14 +53,130 @@ const LLSD REARRANGE = LLSD().with("rearrange", LLSD());
static const LLWearableItemNameComparator WEARABLE_NAME_COMPARATOR;
+//////////////////////////////////////////////////////////////////////////
+
+class CofAttachmentContextMenu : public LLListContextMenu
+{
+protected:
+
+ /*virtual*/ LLContextMenu* createMenu()
+ {
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+
+ functor_t take_off = boost::bind(&LLAppearanceMgr::removeItemFromAvatar, LLAppearanceMgr::getInstance(), _1);
+ registrar.add("Attachment.Detach", boost::bind(handleMultiple, take_off, mUUIDs));
+
+ return createFromFile("menu_cof_attachment.xml");
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+class CofClothingContextMenu : public LLListContextMenu
+{
+protected:
+
+ /*virtual*/ LLContextMenu* createMenu()
+ {
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+ LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
+ LLUUID selected_id = mUUIDs.back();
+ functor_t take_off = boost::bind(&LLAppearanceMgr::removeItemFromAvatar, LLAppearanceMgr::getInstance(), _1);
+
+ registrar.add("Clothing.TakeOff", boost::bind(handleMultiple, take_off, mUUIDs));
+ registrar.add("Clothing.MoveUp", boost::bind(moveWearable, selected_id, false));
+ registrar.add("Clothing.MoveDown", boost::bind(moveWearable, selected_id, true));
+ registrar.add("Clothing.Edit", boost::bind(LLAgentWearables::editWearable, selected_id));
+
+ enable_registrar.add("Clothing.OnEnable", boost::bind(&CofClothingContextMenu::onEnable, this, _2));
+
+ return createFromFile("menu_cof_clothing.xml");
+ }
+
+ bool onEnable(const LLSD& data)
+ {
+ std::string param = data.asString();
+ LLUUID selected_id = mUUIDs.back();
+
+ if ("move_up" == param)
+ {
+ return gAgentWearables.canMoveWearable(selected_id, false);
+ }
+ else if ("move_down" == param)
+ {
+ return gAgentWearables.canMoveWearable(selected_id, true);
+ }
+ else if ("edit" == param)
+ {
+ return gAgentWearables.isWearableModifiable(selected_id);
+ }
+ return true;
+ }
+
+ // We don't use LLAppearanceMgr::moveWearable() directly because
+ // the item may be invalidated between setting the callback and calling it.
+ static bool moveWearable(const LLUUID& item_id, bool closer_to_body)
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(item_id);
+ return LLAppearanceMgr::instance().moveWearable(item, closer_to_body);
+ }
+
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+class CofBodyPartContextMenu : public LLListContextMenu
+{
+protected:
+
+ /*virtual*/ LLContextMenu* createMenu()
+ {
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+ LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
+ LLUUID selected_id = mUUIDs.back();
+
+ registrar.add("BodyPart.Replace", boost::bind(&LLAppearanceMgr::wearItemOnAvatar,
+ LLAppearanceMgr::getInstance(), selected_id, true, true));
+ registrar.add("BodyPart.Edit", boost::bind(LLAgentWearables::editWearable, selected_id));
+
+ enable_registrar.add("BodyPart.OnEnable", boost::bind(&CofBodyPartContextMenu::onEnable, this, _2));
+
+ return createFromFile("menu_cof_body_part.xml");
+ }
+
+ bool onEnable(const LLSD& data)
+ {
+ std::string param = data.asString();
+ LLUUID selected_id = mUUIDs.back();
+
+ if ("edit" == param)
+ {
+ return gAgentWearables.isWearableModifiable(selected_id);
+ }
+
+ return true;
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////
+
LLCOFWearables::LLCOFWearables() : LLPanel(),
mAttachments(NULL),
mClothing(NULL),
mBodyParts(NULL),
mLastSelectedList(NULL)
{
+ mClothingMenu = new CofClothingContextMenu();
+ mAttachmentMenu = new CofAttachmentContextMenu();
+ mBodyPartMenu = new CofBodyPartContextMenu();
};
+LLCOFWearables::~LLCOFWearables()
+{
+ delete mClothingMenu;
+ delete mAttachmentMenu;
+ delete mBodyPartMenu;
+}
// virtual
BOOL LLCOFWearables::postBuild()
@@ -65,6 +185,9 @@ BOOL LLCOFWearables::postBuild()
mClothing = getChild<LLFlatListView>("list_clothing");
mBodyParts = getChild<LLFlatListView>("list_body_parts");
+ mClothing->setRightMouseDownCallback(boost::bind(&LLCOFWearables::onListRightClick, this, _1, _2, _3, mClothingMenu));
+ mAttachments->setRightMouseDownCallback(boost::bind(&LLCOFWearables::onListRightClick, this, _1, _2, _3, mAttachmentMenu));
+ mBodyParts->setRightMouseDownCallback(boost::bind(&LLCOFWearables::onListRightClick, this, _1, _2, _3, mBodyPartMenu));
//selection across different list/tabs is not supported
mAttachments->setCommitCallback(boost::bind(&LLCOFWearables::onSelectionChange, this, mAttachments));
@@ -304,6 +427,14 @@ LLUUID LLCOFWearables::getSelectedUUID()
return mLastSelectedList->getSelectedUUID();
}
+bool LLCOFWearables::getSelectedUUIDs(uuid_vec_t& selected_ids)
+{
+ if (!mLastSelectedList) return false;
+
+ mLastSelectedList->getSelectedUUIDs(selected_ids);
+ return selected_ids.size() != 0;
+}
+
void LLCOFWearables::clear()
{
mAttachments->clear();
@@ -311,4 +442,16 @@ void LLCOFWearables::clear()
mBodyParts->clear();
}
+void LLCOFWearables::onListRightClick(LLUICtrl* ctrl, S32 x, S32 y, LLListContextMenu* menu)
+{
+ if(menu)
+ {
+ uuid_vec_t selected_uuids;
+ if(getSelectedUUIDs(selected_uuids))
+ {
+ menu->show(ctrl, selected_uuids, x, y);
+ }
+ }
+}
+
//EOF
diff --git a/indra/newview/llcofwearables.h b/indra/newview/llcofwearables.h
index 583ee96247..590aa709dd 100644
--- a/indra/newview/llcofwearables.h
+++ b/indra/newview/llcofwearables.h
@@ -40,6 +40,7 @@
#include "llappearancemgr.h"
#include "llinventorymodel.h"
+class LLListContextMenu;
class LLPanelClothingListItem;
class LLPanelBodyPartsListItem;
class LLPanelDeletableWearableListItem;
@@ -115,11 +116,12 @@ public:
LLCOFWearables();
- virtual ~LLCOFWearables() {};
+ virtual ~LLCOFWearables();
/*virtual*/ BOOL postBuild();
LLUUID getSelectedUUID();
+ bool getSelectedUUIDs(uuid_vec_t& selected_ids);
void refresh();
void clear();
@@ -138,6 +140,8 @@ protected:
LLPanelBodyPartsListItem* buildBodypartListItem(LLViewerInventoryItem* item);
LLPanelDeletableWearableListItem* buildAttachemntListItem(LLViewerInventoryItem* item);
+ void onListRightClick(LLUICtrl* ctrl, S32 x, S32 y, LLListContextMenu* menu);
+
LLFlatListView* mAttachments;
LLFlatListView* mClothing;
LLFlatListView* mBodyParts;
@@ -146,6 +150,9 @@ protected:
LLCOFCallbacks mCOFCallbacks;
+ LLListContextMenu* mClothingMenu;
+ LLListContextMenu* mAttachmentMenu;
+ LLListContextMenu* mBodyPartMenu;
};
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 7625a7ab83..39fcf867b1 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -223,9 +223,7 @@ void LLInvFVBridge::cutToClipboard()
// *TODO: make sure this does the right thing
void LLInvFVBridge::showProperties()
{
- LLSD key;
- key["id"] = mUUID;
- LLSideTray::getInstance()->showPanel("sidepanel_inventory", key);
+ show_item_profile(mUUID);
// Disable old properties floater; this is replaced by the sidepanel.
/*
@@ -4423,7 +4421,7 @@ void wear_inventory_item_on_avatar( LLInventoryItem* item )
lldebugs << "wear_inventory_item_on_avatar( " << item->getName()
<< " )" << llendl;
- LLAppearanceMgr::instance().addCOFItemLink(item);
+ LLAppearanceMgr::getInstance()->wearItemOnAvatar(item->getUUID(), true, false);
}
}
@@ -4881,8 +4879,7 @@ void LLWearableBridge::onEditOnAvatar(void* user_data)
void LLWearableBridge::editOnAvatar()
{
- LLUUID linked_id = gInventory.getLinkedItemID(mUUID);
- const LLWearable* wearable = gAgentWearables.getWearableFromItemID(linked_id);
+ const LLWearable* wearable = gAgentWearables.getWearableFromItemID(mUUID);
if( wearable )
{
LLPanel * panel = LLSideTray::getInstance()->getPanel("sidepanel_appearance");
@@ -4966,7 +4963,7 @@ void LLWearableBridge::removeAllClothesFromAvatar()
gAgentWearables.getWearableInventoryItem((LLWearableType::EType)itype, index));
if (!item)
continue;
- const LLUUID &item_id = gInventory.getLinkedItemID(item->getUUID());
+ const LLUUID &item_id = item->getUUID();
const LLWearable *wearable = gAgentWearables.getWearableFromItemID(item_id);
if (!wearable)
continue;
diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp
index 2b4d9fb25c..c38d45f0f5 100644
--- a/indra/newview/llinventoryfunctions.cpp
+++ b/indra/newview/llinventoryfunctions.cpp
@@ -74,6 +74,7 @@
#include "llscrollbar.h"
#include "llscrollcontainer.h"
#include "llselectmgr.h"
+#include "llsidetray.h"
#include "lltabcontainer.h"
#include "lltooldraganddrop.h"
#include "lluictrlfactory.h"
@@ -160,6 +161,19 @@ BOOL get_is_item_worn(const LLUUID& id)
return FALSE;
}
+void show_item_profile(const LLUUID& item_uuid)
+{
+ LLUUID linked_uuid = gInventory.getLinkedItemID(item_uuid);
+ LLSideTray::getInstance()->showPanel("sidepanel_inventory", LLSD().with("id", linked_uuid));
+}
+
+void show_item_original(const LLUUID& item_uuid)
+{
+ LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel();
+ if (!active_panel) return;
+ active_panel->setSelection(gInventory.getLinkedItemID(item_uuid), TAKE_FOCUS_NO);
+}
+
///----------------------------------------------------------------------------
/// LLInventoryCollectFunctor implementations
///----------------------------------------------------------------------------
@@ -343,6 +357,21 @@ bool LLFindWearables::operator()(LLInventoryCategory* cat,
return FALSE;
}
+bool LLFindWearablesOfType::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+{
+ if (!item) return false;
+ if (item->getType() != LLAssetType::AT_CLOTHING &&
+ item->getType() != LLAssetType::AT_BODYPART)
+ {
+ return false;
+ }
+
+ LLViewerInventoryItem *vitem = dynamic_cast<LLViewerInventoryItem*>(item);
+ if (!vitem || vitem->getWearableType() != mWearableType) return false;
+
+ return true;
+}
+
///----------------------------------------------------------------------------
/// LLAssetIDMatches
///----------------------------------------------------------------------------
diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h
index 79b9b4a9cc..8b96ba29d9 100644
--- a/indra/newview/llinventoryfunctions.h
+++ b/indra/newview/llinventoryfunctions.h
@@ -46,6 +46,9 @@
// Is this item or its baseitem is worn, attached, etc...
BOOL get_is_item_worn(const LLUUID& id);
+void show_item_profile(const LLUUID& item_uuid);
+
+void show_item_original(const LLUUID& item_uuid);
void change_item_parent(LLInventoryModel* model,
LLViewerInventoryItem* item,
@@ -262,6 +265,17 @@ public:
LLInventoryItem* item);
};
+//Inventory collect functor collecting wearables of a specific wearable type
+class LLFindWearablesOfType : public LLInventoryCollectFunctor
+{
+public:
+ LLFindWearablesOfType(LLWearableType::EType type) : mWearableType(type) {}
+ virtual ~LLFindWearablesOfType() {}
+ virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
+
+ const LLWearableType::EType mWearableType;
+};
+
/** Inventory Collector Functions
** **
*******************************************************************************/
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index 864545f870..2e1c5238d3 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -566,6 +566,11 @@ const LLUUID& LLInventoryModel::getLinkedItemID(const LLUUID& object_id) const
return item->getLinkedUUID();
}
+LLViewerInventoryItem* LLInventoryModel::getLinkedItem(const LLUUID& object_id) const
+{
+ return object_id.notNull() ? getItem(getLinkedItemID(object_id)) : NULL;
+}
+
LLInventoryModel::item_array_t LLInventoryModel::collectLinkedItems(const LLUUID& id,
const LLUUID& start_folder_id)
{
diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h
index 1f7bd50328..7b56d0bdd1 100644
--- a/indra/newview/llinventorymodel.h
+++ b/indra/newview/llinventorymodel.h
@@ -258,6 +258,7 @@ public:
// Get the inventoryID or item that this item points to, else just return object_id
const LLUUID& getLinkedItemID(const LLUUID& object_id) const;
+ LLViewerInventoryItem* getLinkedItem(const LLUUID& object_id) const;
private:
mutable LLPointer<LLViewerInventoryItem> mLastItem; // cache recent lookups
diff --git a/indra/newview/lllistcontextmenu.cpp b/indra/newview/lllistcontextmenu.cpp
new file mode 100644
index 0000000000..50e969f6bc
--- /dev/null
+++ b/indra/newview/lllistcontextmenu.cpp
@@ -0,0 +1,125 @@
+/**
+ * @file lllistcontextmenu.cpp
+ * @brief Base class of misc lists' context menus
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ *
+ * Copyright (c) 2010, 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$
+ */
+
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lllistcontextmenu.h"
+
+// libs
+#include "llmenugl.h" // for LLContextMenu
+
+// newview
+#include "llviewermenu.h" // for LLViewerMenuHolderGL
+
+LLListContextMenu::LLListContextMenu()
+: mMenu(NULL)
+{
+}
+
+LLListContextMenu::~LLListContextMenu()
+{
+ // do not forget delete LLContextMenu* mMenu.
+ // It can have registered Enable callbacks which are called from the LLMenuHolderGL::draw()
+ // via selected item (menu_item_call) by calling LLMenuItemCallGL::buildDrawLabel.
+ // we can have a crash via using callbacks of deleted instance of ContextMenu. EXT-4725
+
+ // menu holder deletes its menus on viewer exit, so we have no way to determine if instance
+ // of mMenu has already been deleted except of using LLHandle. EXT-4762.
+ if (!mMenuHandle.isDead())
+ {
+ mMenu->die();
+ mMenu = NULL;
+ }
+}
+
+void LLListContextMenu::show(LLView* spawning_view, const uuid_vec_t& uuids, S32 x, S32 y)
+{
+ if (mMenu)
+ {
+ //preventing parent (menu holder) from deleting already "dead" context menus on exit
+ LLView* parent = mMenu->getParent();
+ if (parent)
+ {
+ parent->removeChild(mMenu);
+ }
+ delete mMenu;
+ mMenu = NULL;
+ mUUIDs.clear();
+ }
+
+ if ( uuids.empty() )
+ {
+ return;
+ }
+
+ mUUIDs.resize(uuids.size());
+ std::copy(uuids.begin(), uuids.end(), mUUIDs.begin());
+
+ mMenu = createMenu();
+ if (!mMenu)
+ {
+ llwarns << "Context menu creation failed" << llendl;
+ return;
+ }
+
+ mMenuHandle = mMenu->getHandle();
+ mMenu->show(x, y);
+ LLMenuGL::showPopup(spawning_view, mMenu, x, y);
+}
+
+void LLListContextMenu::hide()
+{
+ if(mMenu)
+ {
+ mMenu->hide();
+ }
+}
+
+// static
+void LLListContextMenu::handleMultiple(functor_t functor, const uuid_vec_t& ids)
+{
+ uuid_vec_t::const_iterator it;
+ for (it = ids.begin(); it != ids.end(); ++it)
+ {
+ functor(*it);
+ }
+}
+
+// static
+LLContextMenu* LLListContextMenu::createFromFile(const std::string& filename)
+{
+ return LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(
+ filename, LLContextMenu::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance());
+}
+
+// EOF
diff --git a/indra/newview/lllistcontextmenu.h b/indra/newview/lllistcontextmenu.h
new file mode 100644
index 0000000000..09540a833f
--- /dev/null
+++ b/indra/newview/lllistcontextmenu.h
@@ -0,0 +1,84 @@
+/**
+ * @file lllistcontextmenu.h
+ * @brief Base class of misc lists' context menus
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ *
+ * Copyright (c) 2010, 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_LLLISTCONTEXTMENU_H
+#define LL_LLLISTCONTEXTMENU_H
+
+#include "llhandle.h"
+#include "lluuid.h"
+#include "llview.h"
+
+class LLView;
+class LLContextMenu;
+
+/**
+ * Context menu for single or multiple list items.
+ *
+ * Derived classes must implement contextMenu().
+ *
+ * Typical usage:
+ * <code>
+ * my_context_menu->show(parent_view, selected_list_items_ids, x, y);
+ * </code>
+ */
+class LLListContextMenu
+{
+public:
+ LLListContextMenu();
+ virtual ~LLListContextMenu();
+
+ /**
+ * Show the menu at specified coordinates.
+ *
+ * @param spawning_view View to spawn at.
+ * @param uuids An array of list items ids.
+ * @param x Horizontal coordinate in the spawn_view's coordinate frame.
+ * @param y Vertical coordinate in the spawn_view's coordinate frame.
+ */
+ virtual void show(LLView* spawning_view, const uuid_vec_t& uuids, S32 x, S32 y);
+
+ virtual void hide();
+
+protected:
+ typedef boost::function<void (const LLUUID& id)> functor_t;
+
+ virtual LLContextMenu* createMenu() = 0;
+
+ static LLContextMenu* createFromFile(const std::string& filename);
+ static void handleMultiple(functor_t functor, const uuid_vec_t& ids);
+
+ uuid_vec_t mUUIDs;
+ LLContextMenu* mMenu;
+ LLHandle<LLView> mMenuHandle;
+};
+
+#endif // LL_LLLISTCONTEXTMENU_H
diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp
index 7ebbddca25..17a2db7a43 100644
--- a/indra/newview/lloutfitslist.cpp
+++ b/indra/newview/lloutfitslist.cpp
@@ -38,11 +38,44 @@
#include "llaccordionctrl.h"
#include "llaccordionctrltab.h"
+#include "llagentwearables.h"
#include "llappearancemgr.h"
#include "llinventoryfunctions.h"
#include "llinventorymodel.h"
+#include "lllistcontextmenu.h"
+#include "lltransutil.h"
+#include "llviewermenu.h"
+#include "llvoavatar.h"
+#include "llvoavatarself.h"
#include "llwearableitemslist.h"
+//////////////////////////////////////////////////////////////////////////
+
+class OutfitContextMenu : public LLListContextMenu
+{
+protected:
+ /* virtual */ LLContextMenu* createMenu()
+ {
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+ LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
+ LLUUID selected_id = mUUIDs.front();
+
+ registrar.add("Outfit.WearReplace",
+ boost::bind(&LLAppearanceMgr::replaceCurrentOutfit, &LLAppearanceMgr::instance(), selected_id));
+ registrar.add("Outfit.WearAdd",
+ boost::bind(&LLAppearanceMgr::addCategoryToCurrentOutfit, &LLAppearanceMgr::instance(), selected_id));
+ registrar.add("Outfit.TakeOff",
+ boost::bind(&LLAppearanceMgr::takeOffOutfit, &LLAppearanceMgr::instance(), selected_id));
+ // *TODO: implement this
+ // registrar.add("Outfit.Rename", boost::bind());
+ // registrar.add("Outfit.Delete", boost::bind());
+
+ return createFromFile("menu_outfit_tab.xml");
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////
+
static LLRegisterPanelClassWrapper<LLOutfitsList> t_outfits_list("outfits_list");
LLOutfitsList::LLOutfitsList()
@@ -55,10 +88,14 @@ LLOutfitsList::LLOutfitsList()
gInventory.addObserver(mCategoriesObserver);
gInventory.addObserver(this);
+
+ mOutfitMenu = new OutfitContextMenu();
}
LLOutfitsList::~LLOutfitsList()
{
+ delete mOutfitMenu;
+
if (gInventory.containsObserver(mCategoriesObserver))
{
gInventory.removeObserver(mCategoriesObserver);
@@ -140,6 +177,8 @@ void LLOutfitsList::refreshList(const LLUUID& category_id)
static LLXMLNodePtr accordionXmlNode = getAccordionTabXMLNode();
LLAccordionCtrlTab* tab = LLUICtrlFactory::defaultBuilder<LLAccordionCtrlTab>(accordionXmlNode, NULL, NULL);
+ tab->setRightMouseDownCallback(boost::bind(&LLOutfitsList::onAccordionTabRightClick, this,
+ _1, _2, _3, cat_id));
tab->setName(name);
tab->setTitle(name);
@@ -447,4 +486,19 @@ void LLOutfitsList::applyFilter(const std::string& new_filter_substring)
}
}
+void LLOutfitsList::onAccordionTabRightClick(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& cat_id)
+{
+ LLAccordionCtrlTab* tab = dynamic_cast<LLAccordionCtrlTab*>(ctrl);
+ if(mOutfitMenu && tab && tab->getHeaderVisible() && cat_id.notNull())
+ {
+ S32 header_bottom = tab->getLocalRect().getHeight() - tab->getHeaderHeight();
+ if(y >= header_bottom)
+ {
+ uuid_vec_t selected_uuids;
+ selected_uuids.push_back(cat_id);
+ mOutfitMenu->show(ctrl, selected_uuids, x, y);
+ }
+ }
+}
+
// EOF
diff --git a/indra/newview/lloutfitslist.h b/indra/newview/lloutfitslist.h
index 8eaa39e6f1..b6b3d6ae46 100644
--- a/indra/newview/lloutfitslist.h
+++ b/indra/newview/lloutfitslist.h
@@ -41,6 +41,7 @@
class LLAccordionCtrl;
class LLAccordionCtrlTab;
class LLWearableItemsList;
+class LLListContextMenu;
/**
* @class LLOutfitsList
@@ -105,6 +106,8 @@ private:
*/
void applyFilter(const std::string& new_filter_substring);
+ void onAccordionTabRightClick(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& cat_id);
+
LLInventoryCategoriesObserver* mCategoriesObserver;
LLAccordionCtrl* mAccordion;
@@ -118,6 +121,8 @@ private:
typedef std::map<LLUUID, LLAccordionCtrlTab*> outfits_map_t;
typedef outfits_map_t::value_type outfits_map_value_t;
outfits_map_t mOutfitsMap;
+
+ LLListContextMenu* mOutfitMenu;
};
#endif //LL_LLOUTFITSLIST_H
diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp
index 5ff51b8165..54d1b46016 100644
--- a/indra/newview/llpanelmaininventory.cpp
+++ b/indra/newview/llpanelmaininventory.cpp
@@ -1187,7 +1187,7 @@ void LLPanelMainInventory::setUploadCostIfNeeded()
LLMenuItemBranchGL* upload_menu = mMenuAdd->findChild<LLMenuItemBranchGL>("upload");
if(upload_menu)
{
- S32 upload_cost = -1;//LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
+ S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
std::string cost_str;
// getPriceUpload() returns -1 if no data available yet.
diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp
index 39ade40967..0d3beaa9a5 100644
--- a/indra/newview/llpanelobjectinventory.cpp
+++ b/indra/newview/llpanelobjectinventory.cpp
@@ -174,11 +174,7 @@ LLInventoryItem* LLTaskInvFVBridge::findItem() const
void LLTaskInvFVBridge::showProperties()
{
- LLSD key;
- key["object"] = mPanel->getTaskUUID();
- key["id"] = mUUID;
- LLSideTray::getInstance()->showPanel("sidepanel_inventory", key);
-
+ show_item_profile(mUUID);
// Disable old properties floater; this is replaced by the sidepanel.
/*
diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp
index ceb720908a..ae4b288588 100644
--- a/indra/newview/llpaneloutfitedit.cpp
+++ b/indra/newview/llpaneloutfitedit.cpp
@@ -212,6 +212,7 @@ LLPanelOutfitEdit::LLPanelOutfitEdit()
mCOFWearables(NULL),
mInventoryItemsPanel(NULL),
mCOFObserver(NULL),
+ mGearMenu(NULL),
mCOFDragAndDropObserver(NULL),
mInitialized(false)
{
@@ -254,6 +255,8 @@ BOOL LLPanelOutfitEdit::postBuild()
childSetCommitCallback("filter_button", boost::bind(&LLPanelOutfitEdit::showWearablesFilter, this), NULL);
childSetCommitCallback("folder_view_btn", boost::bind(&LLPanelOutfitEdit::showFilteredFolderWearablesPanel, this), NULL);
childSetCommitCallback("list_view_btn", boost::bind(&LLPanelOutfitEdit::showFilteredWearablesPanel, this), NULL);
+ childSetCommitCallback("gear_menu_btn", boost::bind(&LLPanelOutfitEdit::onGearButtonClick, this, _1), NULL);
+ childSetCommitCallback("wearables_gear_menu_btn", boost::bind(&LLPanelOutfitEdit::onGearButtonClick, this, _1), NULL);
mCOFWearables = getChild<LLCOFWearables>("cof_wearables_list");
mCOFWearables->setCommitCallback(boost::bind(&LLPanelOutfitEdit::onOutfitItemSelectionChange, this));
@@ -692,4 +695,31 @@ bool LLPanelOutfitEdit::switchPanels(LLPanel* switch_from_panel, LLPanel* switch
return false;
}
+void LLPanelOutfitEdit::onGearButtonClick(LLUICtrl* clicked_button)
+{
+ if(!mGearMenu)
+ {
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+
+ registrar.add("Gear.OnClick", boost::bind(&LLPanelOutfitEdit::onGearMenuItemClick, this, _2));
+
+ mGearMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>(
+ "menu_cof_gear.xml", LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance());
+ mGearMenu->buildDrawLabels();
+ mGearMenu->updateParent(LLMenuGL::sMenuContainer);
+ }
+
+ S32 menu_y = mGearMenu->getRect().getHeight() + clicked_button->getRect().getHeight();
+ LLMenuGL::showPopup(clicked_button, mGearMenu, 0, menu_y);
+}
+
+void LLPanelOutfitEdit::onGearMenuItemClick(const LLSD& data)
+{
+ std::string param = data.asString();
+ if("add" == param)
+ {
+ // TODO
+ }
+}
+
// EOF
diff --git a/indra/newview/llpaneloutfitedit.h b/indra/newview/llpaneloutfitedit.h
index 5ebe1e0406..1bf69c5606 100644
--- a/indra/newview/llpaneloutfitedit.h
+++ b/indra/newview/llpaneloutfitedit.h
@@ -58,6 +58,7 @@ class LLScrollListCtrl;
class LLToggleableMenu;
class LLFilterEditor;
class LLFilteredWearableListManager;
+class LLMenuGL;
class LLPanelOutfitEdit : public LLPanel
{
@@ -126,6 +127,8 @@ public:
private:
+ void onGearButtonClick(LLUICtrl* clicked_button);
+ void onGearMenuItemClick(const LLSD& data);
LLTextBox* mCurrentOutfitName;
@@ -149,6 +152,7 @@ private:
std::vector<LLLookItemType> mLookItemTypes;
LLCOFWearables* mCOFWearables;
+ LLMenuGL* mGearMenu;
bool mInitialized;
};
diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp
index 0760c57f8e..a7e8f497d9 100644
--- a/indra/newview/llpaneloutfitsinventory.cpp
+++ b/indra/newview/llpaneloutfitsinventory.cpp
@@ -383,11 +383,8 @@ void LLPanelOutfitsInventory::initListCommandsHandlers()
, _7 // EAcceptance* accept
));
- mCommitCallbackRegistrar.add("panel_outfits_inventory_gear_default.Custom.Action",
- boost::bind(&LLPanelOutfitsInventory::onCustomAction, this, _2));
- mEnableCallbackRegistrar.add("panel_outfits_inventory_gear_default.Enable",
- boost::bind(&LLPanelOutfitsInventory::isActionEnabled, this, _2));
- mMenuGearDefault = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("panel_outfits_inventory_gear_default.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+ mMenuGearDefault = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_outfit_gear.xml",
+ gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
}
void LLPanelOutfitsInventory::updateListCommands()
diff --git a/indra/newview/llpanelpeoplemenus.cpp b/indra/newview/llpanelpeoplemenus.cpp
index 862e32cca8..dc1c422ff0 100644
--- a/indra/newview/llpanelpeoplemenus.cpp
+++ b/indra/newview/llpanelpeoplemenus.cpp
@@ -42,6 +42,7 @@
#include "llagent.h"
#include "llagentdata.h" // for gAgentID
#include "llavataractions.h"
+#include "llcallingcard.h" // for LLAvatarTracker
#include "llviewermenu.h" // for gMenuHolder
namespace LLPanelPeopleMenus
@@ -49,64 +50,6 @@ namespace LLPanelPeopleMenus
NearbyMenu gNearbyMenu;
-//== ContextMenu ==============================================================
-
-ContextMenu::ContextMenu()
-: mMenu(NULL)
-{
-}
-
-ContextMenu::~ContextMenu()
-{
- // do not forget delete LLContextMenu* mMenu.
- // It can have registered Enable callbacks which are called from the LLMenuHolderGL::draw()
- // via selected item (menu_item_call) by calling LLMenuItemCallGL::buildDrawLabel.
- // we can have a crash via using callbacks of deleted instance of ContextMenu. EXT-4725
-
- // menu holder deletes its menus on viewer exit, so we have no way to determine if instance
- // of mMenu has already been deleted except of using LLHandle. EXT-4762.
- if (!mMenuHandle.isDead())
- {
- mMenu->die();
- mMenu = NULL;
- }
-}
-
-void ContextMenu::show(LLView* spawning_view, const uuid_vec_t& uuids, S32 x, S32 y)
-{
- if (mMenu)
- {
- //preventing parent (menu holder) from deleting already "dead" context menus on exit
- LLView* parent = mMenu->getParent();
- if (parent)
- {
- parent->removeChild(mMenu);
- }
- delete mMenu;
- mMenu = NULL;
- mUUIDs.clear();
- }
-
- if ( uuids.empty() )
- return;
-
- mUUIDs.resize(uuids.size());
- std::copy(uuids.begin(), uuids.end(), mUUIDs.begin());
-
- mMenu = createMenu();
- mMenuHandle = mMenu->getHandle();
- mMenu->show(x, y);
- LLMenuGL::showPopup(spawning_view, mMenu, x, y);
-}
-
-void ContextMenu::hide()
-{
- if(mMenu)
- {
- mMenu->hide();
- }
-}
-
//== NearbyMenu ===============================================================
LLContextMenu* NearbyMenu::createMenu()
@@ -135,8 +78,7 @@ LLContextMenu* NearbyMenu::createMenu()
enable_registrar.add("Avatar.CheckItem", boost::bind(&NearbyMenu::checkContextMenuItem, this, _2));
// create the context menu from the XUI
- return LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(
- "menu_people_nearby.xml", LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance());
+ return createFromFile("menu_people_nearby.xml");
}
else
{
@@ -151,9 +93,7 @@ LLContextMenu* NearbyMenu::createMenu()
enable_registrar.add("Avatar.EnableItem", boost::bind(&NearbyMenu::enableContextMenuItem, this, _2));
// create the context menu from the XUI
- return LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>
- ("menu_people_nearby_multiselect.xml", LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance());
-
+ return createFromFile("menu_people_nearby_multiselect.xml");
}
}
diff --git a/indra/newview/llpanelpeoplemenus.h b/indra/newview/llpanelpeoplemenus.h
index 8e12710afc..e1f8790135 100644
--- a/indra/newview/llpanelpeoplemenus.h
+++ b/indra/newview/llpanelpeoplemenus.h
@@ -33,42 +33,15 @@
#ifndef LL_LLPANELPEOPLEMENUS_H
#define LL_LLPANELPEOPLEMENUS_H
-#include "llavatarlistitem.h"
+#include "lllistcontextmenu.h"
namespace LLPanelPeopleMenus
{
/**
- * Base context menu.
- */
-class ContextMenu : public LLAvatarListItem::ContextMenu
-{
-public:
- ContextMenu();
- virtual ~ContextMenu();
-
- /**
- * Show the menu at specified coordinates.
- *
- * @param uuids - an array of avatar or group ids
- */
- /*virtual*/ void show(LLView* spawning_view, const uuid_vec_t& uuids, S32 x, S32 y);
-
- virtual void hide();
-
-protected:
-
- virtual LLContextMenu* createMenu() = 0;
-
- uuid_vec_t mUUIDs;
- LLContextMenu* mMenu;
- LLHandle<LLView> mMenuHandle;
-};
-
-/**
* Menu used in the nearby people list.
*/
-class NearbyMenu : public ContextMenu
+class NearbyMenu : public LLListContextMenu
{
public:
/*virtual*/ LLContextMenu* createMenu();
diff --git a/indra/newview/llpanelteleporthistory.h b/indra/newview/llpanelteleporthistory.h
index 1f2be63dc2..87e7044222 100644
--- a/indra/newview/llpanelteleporthistory.h
+++ b/indra/newview/llpanelteleporthistory.h
@@ -47,6 +47,7 @@ class LLFlatListView;
class LLTeleportHistoryPanel : public LLPanelPlacesTab
{
public:
+ // *TODO: derive from LLListContextMenu?
class ContextMenu
{
public:
diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp
index 1117ae05d7..b975536f8b 100644
--- a/indra/newview/llparticipantlist.cpp
+++ b/indra/newview/llparticipantlist.cpp
@@ -648,8 +648,7 @@ LLContextMenu* LLParticipantList::LLParticipantListMenu::createMenu()
enable_registrar.add("ParticipantList.CheckItem", boost::bind(&LLParticipantList::LLParticipantListMenu::checkContextMenuItem, this, _2));
// create the context menu from the XUI
- LLContextMenu* main_menu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(
- "menu_participant_list.xml", LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance());
+ LLContextMenu* main_menu = createFromFile("menu_participant_list.xml");
// Don't show sort options for P2P chat
bool is_sort_visible = (mParent.mAvatarList && mParent.mAvatarList->size() > 1);
@@ -666,10 +665,10 @@ LLContextMenu* LLParticipantList::LLParticipantListMenu::createMenu()
void LLParticipantList::LLParticipantListMenu::show(LLView* spawning_view, const uuid_vec_t& uuids, S32 x, S32 y)
{
- LLPanelPeopleMenus::ContextMenu::show(spawning_view, uuids, x, y);
-
if (uuids.size() == 0) return;
+ LLListContextMenu::show(spawning_view, uuids, x, y);
+
const LLUUID& speaker_id = mUUIDs.front();
BOOL is_muted = isMuted(speaker_id);
diff --git a/indra/newview/llparticipantlist.h b/indra/newview/llparticipantlist.h
index abaf503868..967c8b78cf 100644
--- a/indra/newview/llparticipantlist.h
+++ b/indra/newview/llparticipantlist.h
@@ -32,8 +32,8 @@
#include "llviewerprecompiledheaders.h"
#include "llevent.h"
-#include "llpanelpeoplemenus.h"
#include "llavatarlist.h" // for LLAvatarItemRecentSpeakerComparator
+#include "lllistcontextmenu.h"
class LLSpeakerMgr;
class LLAvatarList;
@@ -148,7 +148,7 @@ class LLParticipantList
/**
* Menu used in the participant list.
*/
- class LLParticipantListMenu : public LLPanelPeopleMenus::ContextMenu
+ class LLParticipantListMenu : public LLListContextMenu
{
public:
LLParticipantListMenu(LLParticipantList& parent):mParent(parent){};
diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp
index c8bb4aa983..9da3db3032 100644
--- a/indra/newview/llspeakers.cpp
+++ b/indra/newview/llspeakers.cpp
@@ -529,6 +529,7 @@ BOOL LLSpeakerMgr::isVoiceActive()
// LLIMSpeakerMgr
//
LLIMSpeakerMgr::LLIMSpeakerMgr(LLVoiceChannel* channel) : LLSpeakerMgr(channel)
+, mVoiceModerated(false)
{
}
@@ -783,21 +784,33 @@ void LLIMSpeakerMgr::moderateVoiceOtherParticipants(const LLUUID& excluded_avata
*/
mReverseVoiceModeratedAvatarID = excluded_avatar_id;
- moderateVoiceSession(getSessionID(), !unmute_everyone_else);
+
+
+ if (mVoiceModerated == !unmute_everyone_else)
+ {
+ // session already in requested state. Just force participants which do not match it.
+ forceVoiceModeratedMode(mVoiceModerated);
+ }
+ else
+ {
+ // otherwise set moderated mode for a whole session.
+ moderateVoiceSession(getSessionID(), !unmute_everyone_else);
+ }
}
void LLIMSpeakerMgr::processSessionUpdate(const LLSD& session_update)
{
- if (mReverseVoiceModeratedAvatarID.isNull()) return;
-
if (session_update.has("moderated_mode") &&
session_update["moderated_mode"].has("voice"))
{
- BOOL voice_moderated = session_update["moderated_mode"]["voice"];
+ mVoiceModerated = session_update["moderated_mode"]["voice"];
- moderateVoiceParticipant(mReverseVoiceModeratedAvatarID, voice_moderated);
+ if (mReverseVoiceModeratedAvatarID.notNull())
+ {
+ moderateVoiceParticipant(mReverseVoiceModeratedAvatarID, mVoiceModerated);
- mReverseVoiceModeratedAvatarID = LLUUID::null;
+ mReverseVoiceModeratedAvatarID = LLUUID::null;
+ }
}
}
@@ -817,6 +830,20 @@ void LLIMSpeakerMgr::moderateVoiceSession(const LLUUID& session_id, bool disallo
LLHTTPClient::post(url, data, new ModerationResponder(session_id));
}
+void LLIMSpeakerMgr::forceVoiceModeratedMode(bool should_be_muted)
+{
+ for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it)
+ {
+ LLUUID speaker_id = speaker_it->first;
+ LLSpeaker* speakerp = speaker_it->second;
+
+ // participant does not match requested state
+ if (should_be_muted != (bool)speakerp->mModeratorMutedVoice)
+ {
+ moderateVoiceParticipant(speaker_id, !should_be_muted);
+ }
+ }
+}
//
// LLActiveSpeakerMgr
diff --git a/indra/newview/llspeakers.h b/indra/newview/llspeakers.h
index 2bb160b7ce..b38acb7bc4 100644
--- a/indra/newview/llspeakers.h
+++ b/indra/newview/llspeakers.h
@@ -303,7 +303,15 @@ protected:
void moderateVoiceSession(const LLUUID& session_id, bool disallow_voice);
+ /**
+ * Process all participants to mute/unmute them according to passed voice session state.
+ */
+ void forceVoiceModeratedMode(bool should_be_muted);
+
+private:
LLUUID mReverseVoiceModeratedAvatarID;
+ bool mVoiceModerated;
+
};
class LLActiveSpeakerMgr : public LLSpeakerMgr, public LLSingleton<LLActiveSpeakerMgr>
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 10ceab2656..d0ac103f56 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -108,6 +108,7 @@
#include "lluilistener.h"
#include "llappearancemgr.h"
#include "lltrans.h"
+#include "lleconomy.h"
using namespace LLVOAvatarDefines;
@@ -7658,6 +7659,42 @@ class LLWorldToggleCameraControls : public view_listener_t
}
};
+class LLUploadCostCalculator : public view_listener_t
+{
+ std::string mCostStr;
+
+ bool handleEvent(const LLSD& userdata)
+ {
+ std::string menu_name = userdata.asString();
+ gMenuHolder->childSetLabelArg(menu_name, "[COST]", mCostStr);
+
+ return true;
+ }
+
+ void calculateCost();
+
+public:
+ LLUploadCostCalculator()
+ {
+ calculateCost();
+ }
+};
+
+void LLUploadCostCalculator::calculateCost()
+{
+ S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
+
+ // getPriceUpload() returns -1 if no data available yet.
+ if(upload_cost >= 0)
+ {
+ mCostStr = llformat("%d", upload_cost);
+ }
+ else
+ {
+ mCostStr = llformat("%d", gSavedSettings.getU32("DefaultUploadCost"));
+ }
+}
+
void show_navbar_context_menu(LLView* ctrl, S32 x, S32 y)
{
static LLMenuGL* show_navbar_context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_hide_navbar.xml",
@@ -7699,6 +7736,8 @@ void initialize_menus()
enable.add("IsGodCustomerService", boost::bind(&is_god_customer_service));
enable.add("IsGodCustomerService", boost::bind(&is_god_customer_service));
+ view_listener_t::addEnable(new LLUploadCostCalculator(), "Upload.CalculateCosts");
+
// Agent
commit.add("Agent.toggleFlying", boost::bind(&LLAgent::toggleFlying));
enable.add("Agent.enableFlying", boost::bind(&LLAgent::enableFlying));
diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp
index b209dfecce..fb7577c008 100644
--- a/indra/newview/llwearableitemslist.cpp
+++ b/indra/newview/llwearableitemslist.cpp
@@ -35,8 +35,11 @@
#include "lliconctrl.h"
+#include "llagentwearables.h"
+#include "llappearancemgr.h"
#include "llinventoryfunctions.h"
#include "llinventorymodel.h"
+#include "llmenugl.h" // for LLContextMenu
#include "lltransutil.h"
class LLFindOutfitItems : public LLInventoryCollectFunctor
@@ -377,12 +380,17 @@ static const LLWearableItemTypeNameComparator WEARABLE_TYPE_NAME_COMPARATOR;
static const LLDefaultChildRegistry::Register<LLWearableItemsList> r("wearable_items_list");
LLWearableItemsList::Params::Params()
+: use_internal_context_menu("use_internal_context_menu", true)
{}
LLWearableItemsList::LLWearableItemsList(const LLWearableItemsList::Params& p)
: LLInventoryItemsList(p)
{
setComparator(&WEARABLE_TYPE_NAME_COMPARATOR);
+ if (p.use_internal_context_menu)
+ {
+ setRightMouseDownCallback(boost::bind(&LLWearableItemsList::onRightClick, this, _2, _3));
+ }
}
// virtual
@@ -406,4 +414,140 @@ void LLWearableItemsList::updateList(const LLUUID& category_id)
refreshList(item_array);
}
+void LLWearableItemsList::onRightClick(S32 x, S32 y)
+{
+ uuid_vec_t selected_uuids;
+
+ getSelectedUUIDs(selected_uuids);
+ if (selected_uuids.empty())
+ {
+ return;
+ }
+
+ ContextMenu::instance().show(this, selected_uuids, x, y);
+}
+
+//////////////////////////////////////////////////////////////////////////
+/// ContextMenu
+//////////////////////////////////////////////////////////////////////////
+
+// virtual
+LLContextMenu* LLWearableItemsList::ContextMenu::createMenu()
+{
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+ const uuid_vec_t& ids = mUUIDs; // selected items IDs
+ LLUUID selected_id = ids.front(); // ID of the first selected item
+
+ functor_t wear = boost::bind(&LLAppearanceMgr::wearItemOnAvatar, LLAppearanceMgr::getInstance(), _1, true, false);
+ functor_t take_off = boost::bind(&LLAppearanceMgr::removeItemFromAvatar, LLAppearanceMgr::getInstance(), _1);
+
+ // Register handlers common for all wearable types.
+ registrar.add("Wearable.Wear", boost::bind(handleMultiple, wear, ids));
+ registrar.add("Wearable.Edit", boost::bind(handleMultiple, LLAgentWearables::editWearable, ids));
+ registrar.add("Wearable.ShowOriginal", boost::bind(show_item_original, selected_id));
+
+ // Register handlers for clothing.
+ registrar.add("Clothing.TakeOff", boost::bind(handleMultiple, take_off, ids));
+
+ // Register handlers for body parts.
+
+ // Register handlers for attachments.
+ registrar.add("Attachment.Detach", boost::bind(handleMultiple, take_off, ids));
+ registrar.add("Attachment.Profile", boost::bind(show_item_profile, selected_id));
+
+ // Create the menu.
+ LLContextMenu* menu = createFromFile("menu_wearable_list_item.xml");
+
+ // Determine which items should be visible/enabled.
+ updateItemsVisibility(menu);
+ return menu;
+}
+
+void LLWearableItemsList::ContextMenu::updateItemsVisibility(LLContextMenu* menu)
+{
+ if (!menu)
+ {
+ llwarns << "Invalid menu" << llendl;
+ return;
+ }
+
+ const uuid_vec_t& ids = mUUIDs; // selected items IDs
+ U32 mask = 0; // mask of selected items' types
+ U32 nitems = ids.size(); // number of selected items
+ U32 nworn = 0; // number of worn items among the selected ones
+ U32 nwornlinks = 0; // number of worn links among the selected items
+ U32 neditable = 0; // number of editable items among the selected ones
+
+ for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it)
+ {
+ LLUUID id = *it;
+ LLViewerInventoryItem* item = gInventory.getItem(id);
+
+ if (!item)
+ {
+ llwarns << "Invalid item" << llendl;
+ // *NOTE: the logic below may not work in this case
+ continue;
+ }
+
+ updateMask(mask, item->getType());
+
+ bool is_link = item->getIsLinkType();
+ bool is_worn = get_is_item_worn(id);
+ bool is_editable = gAgentWearables.isWearableModifiable(id);
+
+ if (is_worn)
+ {
+ ++nworn;
+
+ if (is_link)
+ {
+ ++nwornlinks;
+ }
+ }
+ if (is_editable)
+ {
+ ++neditable;
+ }
+ } // for
+
+ // *TODO: eliminate multiple traversals over the menu items
+ // *TODO: try disabling items rather than hiding them
+ // *FIX: we may hide *all* items and thus get an ugly empty menu
+ setMenuItemVisible(menu, "wear", nworn == 0);
+ setMenuItemVisible(menu, "edit", mask & (MASK_CLOTHING|MASK_BODYPART) && nitems == 1 && neditable == 1);
+ setMenuItemVisible(menu, "show_original", nitems == 1 && nwornlinks == nitems);
+ setMenuItemVisible(menu, "take_off", mask == MASK_CLOTHING && nworn == nitems); // selected only worn clothes
+ setMenuItemVisible(menu, "detach", mask == MASK_ATTACHMENT && nworn == nitems);
+ setMenuItemVisible(menu, "object_profile", mask == MASK_ATTACHMENT && nitems == 1);
+}
+
+// We need this method to convert non-zero BOOL values to exactly 1 (TRUE).
+// Otherwise code relying on a BOOL value being TRUE may fail
+// (I experienced a weird assert in LLView::drawChildren() because of that.
+void LLWearableItemsList::ContextMenu::setMenuItemVisible(LLContextMenu* menu, const std::string& name, bool val)
+{
+ menu->setItemVisible(name, val);
+}
+
+void LLWearableItemsList::ContextMenu::updateMask(U32& mask, LLAssetType::EType at)
+{
+ if (at == LLAssetType::AT_CLOTHING)
+ {
+ mask |= MASK_CLOTHING;
+ }
+ else if (at == LLAssetType::AT_BODYPART)
+ {
+ mask |= MASK_BODYPART;
+ }
+ else if (at == LLAssetType::AT_OBJECT)
+ {
+ mask |= MASK_ATTACHMENT;
+ }
+ else
+ {
+ llwarns << "Unsupported asset type: " << at << llendl;
+ }
+}
+
// EOF
diff --git a/indra/newview/llwearableitemslist.h b/indra/newview/llwearableitemslist.h
index 2cab5a07a2..7ad1b5a3ad 100644
--- a/indra/newview/llwearableitemslist.h
+++ b/indra/newview/llwearableitemslist.h
@@ -32,11 +32,14 @@
#ifndef LL_LLWEARABLEITEMSLIST_H
#define LL_LLWEARABLEITEMSLIST_H
+// libs
#include "llpanel.h"
+#include "llsingleton.h"
// newview
#include "llinventoryitemslist.h"
#include "llinventorymodel.h"
+#include "lllistcontextmenu.h"
#include "llwearabletype.h"
/**
@@ -274,8 +277,32 @@ private:
class LLWearableItemsList : public LLInventoryItemsList
{
public:
+ /**
+ * Context menu.
+ *
+ * This menu is likely to be used from outside
+ * (e.g. for items selected across multiple wearable lists),
+ * so making it a singleton.
+ */
+ class ContextMenu : public LLListContextMenu, public LLSingleton<ContextMenu>
+ {
+ protected:
+ enum {
+ MASK_CLOTHING = 0x01,
+ MASK_BODYPART = 0x02,
+ MASK_ATTACHMENT = 0x04,
+ };
+
+ /* virtual */ LLContextMenu* createMenu();
+ void updateItemsVisibility(LLContextMenu* menu);
+ void setMenuItemVisible(LLContextMenu* menu, const std::string& name, bool val);
+ void updateMask(U32& mask, LLAssetType::EType at);
+ };
+
struct Params : public LLInitParam::Block<Params, LLInventoryItemsList::Params>
{
+ Optional<bool> use_internal_context_menu;
+
Params();
};
@@ -286,6 +313,8 @@ public:
protected:
friend class LLUICtrlFactory;
LLWearableItemsList(const LLWearableItemsList::Params& p);
+
+ void onRightClick(S32 x, S32 y);
};
#endif //LL_LLWEARABLEITEMSLIST_H
diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp
index da61840761..bc7f8ec854 100644
--- a/indra/newview/llxmlrpctransaction.cpp
+++ b/indra/newview/llxmlrpctransaction.cpp
@@ -45,6 +45,7 @@
#include <xmlrpc-epi/xmlrpc.h>
#include "llappviewer.h"
+#include "lltrans.h"
// Static instance of LLXMLRPCListener declared here so that every time we
// bring in this code, we instantiate a listener. If we put the static
@@ -510,11 +511,7 @@ void LLXMLRPCTransaction::Impl::setStatus(EStatus status,
default:
// Usually this means that there's a problem with the login server,
// not with the client. Direct user to status page.
- mStatusMessage =
- "Despite our best efforts, something unexpected has gone wrong. \n"
- " \n"
- "Please check secondlife.com/status \n"
- "to see if there is a known problem with the service.";
+ mStatusMessage = LLTrans::getString("server_is_down");
mStatusURI = "http://secondlife.com/status/";
}
diff --git a/indra/newview/skins/default/xui/en/inspect_group.xml b/indra/newview/skins/default/xui/en/inspect_group.xml
index 37ae5a64d7..bcdb63228d 100644
--- a/indra/newview/skins/default/xui/en/inspect_group.xml
+++ b/indra/newview/skins/default/xui/en/inspect_group.xml
@@ -79,7 +79,7 @@ L$123 to join
height="23"
label="Join"
left="8"
- top="286"
+ top="125"
name="join_btn"
width="103"
commit_callback.function="InspectGroup.Join"/>
@@ -88,7 +88,7 @@ L$123 to join
height="23"
label="Leave"
left="8"
- top="286"
+ top="125"
name="leave_btn"
width="103"
commit_callback.function="InspectGroup.Leave"/>
@@ -97,7 +97,7 @@ L$123 to join
height="23"
label="View Profile"
name="view_profile_btn"
- top="286"
+ top="125"
left="117"
width="103"
commit_callback.function="InspectGroup.ViewProfile" />
diff --git a/indra/newview/skins/default/xui/en/menu_cof_attachment.xml b/indra/newview/skins/default/xui/en/menu_cof_attachment.xml
new file mode 100644
index 0000000000..b422d87938
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_cof_attachment.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<context_menu
+ layout="topleft"
+ name="COF Attachment">
+ <menu_item_call
+ label="Detach"
+ layout="topleft"
+ name="detach">
+ <on_click
+ function="Attachment.Detach"
+ parameter="detach"/>
+ </menu_item_call>
+ <context_menu
+ label="Attach to"
+ layout="topleft"
+ name="attach_to" />
+ <context_menu
+ label="Attach to HUD"
+ layout="topleft"
+ name="attach_to_hud" />
+</context_menu>
diff --git a/indra/newview/skins/default/xui/en/menu_cof_body_part.xml b/indra/newview/skins/default/xui/en/menu_cof_body_part.xml
new file mode 100644
index 0000000000..01008ef203
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_cof_body_part.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<context_menu
+ layout="topleft"
+ name="COF Body">
+ <menu_item_call
+ label="Replace"
+ layout="topleft"
+ name="replace">
+ <on_click
+ function="BodyPart.Replace"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Edit"
+ layout="topleft"
+ name="edit">
+ <on_click
+ function="BodyPart.Edit"/>
+ <on_enable
+ function="BodyPart.OnEnable"
+ parameter="edit" />
+ </menu_item_call>
+</context_menu>
diff --git a/indra/newview/skins/default/xui/en/menu_cof_clothing.xml b/indra/newview/skins/default/xui/en/menu_cof_clothing.xml
new file mode 100644
index 0000000000..f9cb29f0d7
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_cof_clothing.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<context_menu
+ layout="topleft"
+ name="COF Clothing">
+ <menu_item_call
+ label="Take Off"
+ layout="topleft"
+ name="take_off">
+ <on_click
+ function="Clothing.TakeOff" />
+ </menu_item_call>
+ <menu_item_call
+ label="Move Up a Layer"
+ layout="topleft"
+ name="move_up">
+ <on_click
+ function="Clothing.MoveUp" />
+ <on_enable
+ function="Clothing.OnEnable"
+ parameter="move_up" />
+ </menu_item_call>
+ <menu_item_call
+ label="Move Down a Layer"
+ layout="topleft"
+ name="move_down">
+ <on_click
+ function="Clothing.MoveDown" />
+ <on_enable
+ function="Clothing.OnEnable"
+ parameter="move_down" />
+ </menu_item_call>
+ <menu_item_call
+ label="Edit"
+ layout="topleft"
+ name="edit">
+ <on_click
+ function="Clothing.Edit" />
+ <on_enable
+ function="Clothing.OnEnable"
+ parameter="edit" />
+ </menu_item_call>
+</context_menu>
diff --git a/indra/newview/skins/default/xui/en/menu_cof_gear.xml b/indra/newview/skins/default/xui/en/menu_cof_gear.xml
new file mode 100644
index 0000000000..982d4f2015
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_cof_gear.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<menu
+ layout="topleft"
+ name="Gear COF">
+ <menu_item_call
+ label="Add To Outfit"
+ layout="topleft"
+ name="add">
+ <on_click
+ function="Gear.OnClick"
+ parameter="add"/>
+ <on_enable
+ function="Gear.OnEnable"
+ parameter="add" />
+ </menu_item_call>
+</menu>
diff --git a/indra/newview/skins/default/xui/en/menu_outfit_gear.xml b/indra/newview/skins/default/xui/en/menu_outfit_gear.xml
new file mode 100644
index 0000000000..dfc72b557c
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_outfit_gear.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<menu
+ layout="topleft"
+ name="Gear Outfit">
+ <menu_item_call
+ label="Wear - Replace Current Outfit"
+ layout="topleft"
+ name="wear">
+ <on_click
+ function="Gear.OnClick"
+ parameter="wear"/>
+ <on_enable
+ function="Gear.OnEnable"
+ parameter="wear" />
+ </menu_item_call>
+ <menu_item_call
+ label="Take Off - Remove Current Outfit"
+ layout="topleft"
+ name="take_off">
+ <on_click
+ function="Gear.OnClick"
+ parameter="take_off"/>
+ <on_enable
+ function="Gear.OnEnable"
+ parameter="take_off" />
+ </menu_item_call>
+ <menu_item_separator />
+ <menu_item_call
+ label="Rename"
+ layout="topleft"
+ name="rename">
+ <on_click
+ function="Gear.OnClick"
+ parameter="rename"/>
+ <on_enable
+ function="Gear.OnEnable"
+ parameter="rename" />
+ </menu_item_call>
+ <menu_item_call
+ label="Delete Outfit"
+ layout="topleft"
+ name="delete_outfit">
+ <on_click
+ function="Gear.OnClick"
+ parameter="delete_outfit"/>
+ <on_enable
+ function="Gear.OnEnable"
+ parameter="delete_outfit" />
+ </menu_item_call>
+</menu>
diff --git a/indra/newview/skins/default/xui/en/menu_outfit_tab.xml b/indra/newview/skins/default/xui/en/menu_outfit_tab.xml
new file mode 100644
index 0000000000..8f3e62157a
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_outfit_tab.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<context_menu
+ layout="topleft"
+ name="Outfit">
+ <menu_item_call
+ label="Wear - Replace Current Outfit"
+ layout="topleft"
+ name="wear_replace">
+ <on_click
+ function="Outfit.WearReplace" />
+ </menu_item_call>
+ <menu_item_call
+ label="Wear - Add to Current Outfit"
+ layout="topleft"
+ name="wear_add">
+ <on_click
+ function="Outfit.WearAdd" />
+ </menu_item_call>
+ <menu_item_call
+ label="Take Off - Remove Current Outfit"
+ layout="topleft"
+ name="take_off">
+ <on_click
+ function="Outfit.TakeOff" />
+ </menu_item_call>
+ <menu_item_separator />
+ <menu_item_call
+ label="Rename"
+ layout="topleft"
+ name="rename">
+ <on_click
+ function="Outfit.Rename" />
+ </menu_item_call>
+ <menu_item_call
+ label="Delete Outfit"
+ layout="topleft"
+ name="delete">
+ <on_click
+ function="Outfit.Delete" />
+ </menu_item_call>
+</context_menu>
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 16c2581d63..aff8f7ddf4 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -521,6 +521,52 @@
<menu_item_check.on_enable
function="Tools.EnableToolNotPie" />
</menu_item_check>
+ <menu
+ create_jump_keys="true"
+ label="Select Linked Parts"
+ name="Select Linked Parts"
+ tear_off="true">
+ <menu_item_call
+ label="Select Next Part"
+ name="Select Next Part"
+ shortcut="control|.">
+ <menu_item_call.on_click
+ function="Tools.SelectNextPart"
+ parameter="next" />
+ <menu_item_call.on_enable
+ function="Tools.EnableSelectNextPart" />
+ </menu_item_call>
+ <menu_item_call
+ label="Select Previous Part"
+ name="Select Previous Part"
+ shortcut="control|,">
+ <menu_item_call.on_click
+ function="Tools.SelectNextPart"
+ parameter="previous" />
+ <menu_item_call.on_enable
+ function="Tools.EnableSelectNextPart" />
+ </menu_item_call>
+ <menu_item_call
+ label="Include Next Part"
+ name="Include Next Part"
+ shortcut="control|shift|.">
+ <menu_item_call.on_click
+ function="Tools.SelectNextPart"
+ parameter="includenext" />
+ <menu_item_call.on_enable
+ function="Tools.EnableSelectNextPart" />
+ </menu_item_call>
+ <menu_item_call
+ label="Include Previous Part"
+ name="Include Previous Part"
+ shortcut="control|shift|,">
+ <menu_item_call.on_click
+ function="Tools.SelectNextPart"
+ parameter="includeprevious" />
+ <menu_item_call.on_enable
+ function="Tools.EnableSelectNextPart" />
+ </menu_item_call>
+ </menu>
<menu_item_separator/>
<menu_item_call
label="Focus on Selection"
@@ -764,49 +810,60 @@
</menu>
<menu
create_jump_keys="true"
- label="Select Linked Parts"
- name="Select Linked Parts"
+ label="Upload"
+ layout="topleft"
+ name="Upload"
tear_off="true">
<menu_item_call
- label="Select Next Part"
- name="Select Next Part"
- shortcut="control|.">
+ label="Image (L$[COST])..."
+ layout="topleft"
+ name="Upload Image"
+ shortcut="control|U">
<menu_item_call.on_click
- function="Tools.SelectNextPart"
- parameter="next" />
+ function="File.UploadImage"
+ parameter="" />
<menu_item_call.on_enable
- function="Tools.EnableSelectNextPart" />
+ function="File.EnableUpload" />
+ <menu_item_call.on_visible
+ function="Upload.CalculateCosts"
+ parameter="Upload Image" />
</menu_item_call>
<menu_item_call
- label="Select Previous Part"
- name="Select Previous Part"
- shortcut="control|,">
+ label="Sound (L$[COST])..."
+ layout="topleft"
+ name="Upload Sound">
<menu_item_call.on_click
- function="Tools.SelectNextPart"
- parameter="previous" />
+ function="File.UploadSound"
+ parameter="" />
<menu_item_call.on_enable
- function="Tools.EnableSelectNextPart" />
+ function="File.EnableUpload" />
+ <menu_item_call.on_visible
+ function="Upload.CalculateCosts"
+ parameter="Upload Sound" />
</menu_item_call>
<menu_item_call
- label="Include Next Part"
- name="Include Next Part"
- shortcut="control|shift|.">
+ label="Animation (L$[COST])..."
+ layout="topleft"
+ name="Upload Animation">
<menu_item_call.on_click
- function="Tools.SelectNextPart"
- parameter="includenext" />
+ function="File.UploadAnim"
+ parameter="" />
<menu_item_call.on_enable
- function="Tools.EnableSelectNextPart" />
+ function="File.EnableUpload" />
+ <menu_item_call.on_visible
+ function="Upload.CalculateCosts"
+ parameter="Upload Animation" />
</menu_item_call>
<menu_item_call
- label="Include Previous Part"
- name="Include Previous Part"
- shortcut="control|shift|,">
+ label="Bulk (L$[COST] per file)..."
+ layout="topleft"
+ name="Bulk Upload">
<menu_item_call.on_click
- function="Tools.SelectNextPart"
- parameter="includeprevious" />
- <menu_item_call.on_enable
- function="Tools.EnableSelectNextPart" />
+ function="File.UploadBulk"
+ parameter="" />
</menu_item_call>
+ <menu_item_separator
+ layout="topleft" />
</menu>
</menu>
<menu
@@ -3210,4 +3267,4 @@
</menu>
</menu>
</menu>
-</menu_bar>
+</menu_bar> \ No newline at end of file
diff --git a/indra/newview/skins/default/xui/en/menu_wearable_list_item.xml b/indra/newview/skins/default/xui/en/menu_wearable_list_item.xml
new file mode 100644
index 0000000000..7ea7eaade5
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_wearable_list_item.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<context_menu
+ name="Outfit Wearable Context Menu">
+ <menu_item_call
+ label="Wear"
+ layout="topleft"
+ name="wear">
+ <on_click
+ function="Wearable.Wear" />
+ </menu_item_call>
+ <menu_item_call
+ label="Detach"
+ layout="topleft"
+ name="detach">
+ <on_click
+ function="Attachment.Detach" />
+ </menu_item_call>
+<!-- *TODO: implement the submenus
+ <menu
+ label="Attach to"
+ layout="topleft"
+ name="attach_to" />
+ <menu
+ label="Attach to HUD"
+ layout="topleft"
+ name="attach_to_hud" />
+-->
+ <menu_item_call
+ label="Object Profile"
+ layout="topleft"
+ name="object_profile">
+ <on_click
+ function="Attachment.Profile" />
+ </menu_item_call>
+ <menu_item_call
+ label="Take Off"
+ layout="topleft"
+ name="take_off">
+ <on_click
+ function="Clothing.TakeOff"
+ parameter="take_off"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Edit"
+ layout="topleft"
+ name="edit">
+ <on_click
+ function="Wearable.Edit"
+ parameter="edit"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Show Original"
+ layout="topleft"
+ name="show_original">
+ <on_click
+ function="Wearable.ShowOriginal" />
+ </menu_item_call>
+</context_menu>
diff --git a/indra/newview/skins/default/xui/en/outfit_accordion_tab.xml b/indra/newview/skins/default/xui/en/outfit_accordion_tab.xml
index 5fcc9b012b..066992b25d 100644
--- a/indra/newview/skins/default/xui/en/outfit_accordion_tab.xml
+++ b/indra/newview/skins/default/xui/en/outfit_accordion_tab.xml
@@ -15,6 +15,7 @@
allow_select="true"
follows="all"
keep_one_selected="true"
+ multi_select="true"
name="wearable_items_list"
translate="false"
/>
diff --git a/indra/newview/skins/default/xui/en/panel_bottomtray.xml b/indra/newview/skins/default/xui/en/panel_bottomtray.xml
index bfc40a8638..ac61c7da5d 100644
--- a/indra/newview/skins/default/xui/en/panel_bottomtray.xml
+++ b/indra/newview/skins/default/xui/en/panel_bottomtray.xml
@@ -67,7 +67,7 @@
mouse_opaque="false"
name="speak_panel"
top_delta="0"
- user_resize="false"
+ user_resize="true"
width="110">
<talk_button
follows="left|right"
@@ -193,6 +193,7 @@
min_width="40"
mouse_opaque="false"
name="snapshot_panel"
+ user_resize="false"
width="39">
<button
follows="left|right"
diff --git a/indra/newview/skins/default/xui/en/panel_cof_wearables.xml b/indra/newview/skins/default/xui/en/panel_cof_wearables.xml
index 86b9ea6e14..cf84c31078 100644
--- a/indra/newview/skins/default/xui/en/panel_cof_wearables.xml
+++ b/indra/newview/skins/default/xui/en/panel_cof_wearables.xml
@@ -29,6 +29,7 @@
height="10"
layout="topleft"
left="0"
+ multi_select="true"
name="list_attachments"
top="0"
width="311" />
@@ -63,6 +64,7 @@
height="10"
layout="topleft"
left="0"
+ multi_select="true"
name="list_clothing"
top_pad="0"
width="311" />
@@ -98,6 +100,7 @@
height="10"
layout="topleft"
left="0"
+ multi_select="true"
name="list_body_parts"
top_pad="0"
width="311" />
diff --git a/indra/newview/skins/default/xui/en/panel_edit_shape.xml b/indra/newview/skins/default/xui/en/panel_edit_shape.xml
index 76842e5279..cf15fb0455 100644
--- a/indra/newview/skins/default/xui/en/panel_edit_shape.xml
+++ b/indra/newview/skins/default/xui/en/panel_edit_shape.xml
@@ -43,7 +43,7 @@
name="wearable_accordion"
top="0"
single_expansion="true"
- fit_parent="false"
+ fit_parent="true"
width="313">
<accordion_tab
layout="topleft"
diff --git a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml
index b473a7a282..895cc4e3cc 100644
--- a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml
+++ b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml
@@ -336,6 +336,7 @@
allow_select="true"
layout="topleft"
follows="all"
+ multi_select="true"
width="310"
height="140"
left="0"
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index f8bb36b88a..e57b30185d 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -3176,5 +3176,11 @@ Abuse Report</string>
<!-- language specific white-space characters, delimiters, spacers, item separation symbols -->
<string name="sentences_separator" value=" "></string>
+
+ <string name="server_is_down">
+ Despite our best efforts, something unexpected has gone wrong.
+
+ Please check secondlife.com/status to see if there is a known problem with the service.
+ </string>
</strings>