summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Savchuk <vsavchuk@productengine.com>2010-08-06 17:14:01 +0300
committerVadim Savchuk <vsavchuk@productengine.com>2010-08-06 17:14:01 +0300
commit5f9f6dcdec0de015e3c9a88afad6f4761c3c6527 (patch)
treeb85dd598af4740146853076e19cc326ac391def8
parent593a6f0bbf25877f7e3b7482d8644cf34840f923 (diff)
EXT-8577 WIP Context menu items for multi-attachments.
Done: - 1. Dropped the obsolete "MultipleAttachments" setting. - 2. Added an "Add" item to the following attachment-related context menus: * My Appearance (ex-My Outfits) context menu. * Edit Outfit -> Add More context menu. * Object in-world context menu. * Inventory context menu. * Object inspector gear menu. - 3. Modified "Attach To / Attach To HUD" to perform the "add" instead of "replace" action. TODO: - Ability to attach multiple objects at once from the Add More panel (bulk attach). - Make sure there's no memleak when you click Wear/Attach in the in-world object context menu and the callback isn't invoked (because e.g. avatar fails to get close enough to the object). Issues: 0. I'm not sure whether LLAgentWearables::userAttachMultipleAttachments() should replace attachments or add them. Assumed the former. 1. I couldn't verify that adding objects from the object inspector menu works because I either could wear an object or see its inspector, not both. 2. > 1. Right-click on an object in your inventory and select "Wear". > VERIFY: Attaches the object and replaces whatever's there; asks for > confirmation before replacing an existing object. I think this is impossible to implement because we don't know in advance what point the object will be attached to, so we can't display a confirmation dialog. Reviewed by Seraph at https://codereview.productengine.com/secondlife/r/843/ --HG-- branch : product-engine
-rw-r--r--indra/newview/app_settings/settings.xml11
-rw-r--r--indra/newview/llagentwearables.cpp5
-rw-r--r--indra/newview/llappearancemgr.cpp2
-rw-r--r--indra/newview/llinventorybridge.cpp19
-rw-r--r--indra/newview/llinventorybridge.h3
-rw-r--r--indra/newview/llselectmgr.cpp9
-rw-r--r--indra/newview/llselectmgr.h2
-rw-r--r--indra/newview/llviewerattachmenu.cpp2
-rw-r--r--indra/newview/llviewermenu.cpp36
-rw-r--r--indra/newview/llwearableitemslist.cpp18
-rw-r--r--indra/newview/skins/default/xui/en/menu_inspect_object_gear.xml9
-rw-r--r--indra/newview/skins/default/xui/en/menu_object.xml9
-rw-r--r--indra/newview/skins/default/xui/en/menu_wearable_list_item.xml2
13 files changed, 89 insertions, 38 deletions
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index bbf3f4fc75..4c52fb015f 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -5373,17 +5373,6 @@
<key>Value</key>
<integer>0</integer>
</map>
- <key>MultipleAttachments</key>
- <map>
- <key>Comment</key>
- <string>Allow multiple objects to be attached to a single attachment point.</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>Boolean</string>
- <key>Value</key>
- <integer>0</integer>
- </map>
<key>MuteAmbient</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp
index 6e3e54df2c..337878cf96 100644
--- a/indra/newview/llagentwearables.cpp
+++ b/indra/newview/llagentwearables.cpp
@@ -1885,10 +1885,7 @@ void LLAgentWearables::userAttachMultipleAttachments(LLInventoryModel::item_arra
msg->nextBlockFast(_PREHASH_ObjectData );
msg->addUUIDFast(_PREHASH_ItemID, item->getLinkedUUID());
msg->addUUIDFast(_PREHASH_OwnerID, item->getPermissions().getOwner());
- if (gSavedSettings.getBOOL("MultipleAttachments"))
- msg->addU8Fast(_PREHASH_AttachmentPt, 0 | ATTACHMENT_ADD );
- else
- msg->addU8Fast(_PREHASH_AttachmentPt, 0 ); // Wear at the previous or default attachment point
+ msg->addU8Fast(_PREHASH_AttachmentPt, 0 ); // Wear at the previous or default attachment point
pack_permissions_slam(msg, item->getFlags(), item->getPermissions());
msg->addStringFast(_PREHASH_Name, item->getName());
msg->addStringFast(_PREHASH_Description, item->getDescription());
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index 296a580106..cecb2ee6ad 100644
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -1012,7 +1012,7 @@ bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_up
addCOFItemLink(item_to_wear, do_update, cb);
break;
case LLAssetType::AT_OBJECT:
- rez_attachment(item_to_wear, NULL);
+ rez_attachment(item_to_wear, NULL, replace);
break;
default: return false;;
}
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 38f3521b2d..7e710ce8e1 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -3936,7 +3936,7 @@ void LLObjectBridge::performAction(LLInventoryModel* model, std::string action)
item = (LLViewerInventoryItem*)gInventory.getItem(object_id);
if(item && gInventory.isObjectDescendentOf(object_id, gInventory.getRootFolderID()))
{
- rez_attachment(item, NULL);
+ rez_attachment(item, NULL, true); // Replace if "Wear"ing.
}
else if(item && item->isFinished())
{
@@ -3952,6 +3952,10 @@ void LLObjectBridge::performAction(LLInventoryModel* model, std::string action)
}
gFocusMgr.setKeyboardFocus(NULL);
}
+ else if ("wear_add" == action)
+ {
+ LLAppearanceMgr::instance().wearItemOnAvatar(mUUID, true, false); // Don't replace if adding.
+ }
else if (isRemoveAction(action))
{
LLInventoryItem* item = gInventory.getItem(mUUID);
@@ -3998,7 +4002,7 @@ std::string LLObjectBridge::getLabelSuffix() const
}
}
-void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment)
+void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment, bool replace)
{
LLSD payload;
payload["item_id"] = item->getLinkedUUID(); // Wear the base object in case this is a link.
@@ -4017,9 +4021,14 @@ void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attach
}
}
+ if (!replace)
+ {
+ attach_pt |= ATTACHMENT_ADD;
+ }
+
payload["attachment_point"] = attach_pt;
- if (!gSavedSettings.getBOOL("MultipleAttachments") &&
+ if (replace &&
(attachment && attachment->getNumObjects() > 0))
{
LLNotificationsUtil::add("ReplaceAttachment", LLSD(), payload, confirm_replace_attachment_rez);
@@ -4048,8 +4057,6 @@ bool confirm_replace_attachment_rez(const LLSD& notification, const LLSD& respon
if (itemp)
{
U8 attachment_pt = notification["payload"]["attachment_point"].asInteger();
- if (gSavedSettings.getBOOL("MultipleAttachments"))
- attachment_pt |= ATTACHMENT_ADD;
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_RezSingleAttachmentFromInv);
@@ -4106,6 +4113,7 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
{
items.push_back(std::string("Wearable And Object Separator"));
items.push_back(std::string("Wearable And Object Wear"));
+ items.push_back(std::string("Wearable Add"));
items.push_back(std::string("Attach To"));
items.push_back(std::string("Attach To HUD"));
// commented out for DEV-32347
@@ -4114,6 +4122,7 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
if (!gAgentAvatarp->canAttachMoreObjects())
{
disabled_items.push_back(std::string("Wearable And Object Wear"));
+ disabled_items.push_back(std::string("Wearable Add"));
disabled_items.push_back(std::string("Attach To"));
disabled_items.push_back(std::string("Attach To HUD"));
}
diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h
index 91055eb906..00e8b0fb08 100644
--- a/indra/newview/llinventorybridge.h
+++ b/indra/newview/llinventorybridge.h
@@ -594,7 +594,8 @@ public:
};
void rez_attachment(LLViewerInventoryItem* item,
- LLViewerJointAttachment* attachment);
+ LLViewerJointAttachment* attachment,
+ bool replace = false);
// Move items from an in-world object's "Contents" folder to a specified
// folder in agent inventory.
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
index 050b87bbe0..21f8485e90 100644
--- a/indra/newview/llselectmgr.cpp
+++ b/indra/newview/llselectmgr.cpp
@@ -3615,7 +3615,7 @@ void LLSelectMgr::selectionSetObjectSaleInfo(const LLSaleInfo& sale_info)
// Attachments
//----------------------------------------------------------------------
-void LLSelectMgr::sendAttach(U8 attachment_point)
+void LLSelectMgr::sendAttach(U8 attachment_point, bool replace)
{
LLViewerObject* attach_object = mSelectedObjects->getFirstRootObject();
@@ -3629,9 +3629,12 @@ void LLSelectMgr::sendAttach(U8 attachment_point)
if (0 == attachment_point ||
get_if_there(gAgentAvatarp->mAttachmentPoints, (S32)attachment_point, (LLViewerJointAttachment*)NULL))
{
-
- if (gSavedSettings.getBOOL("MultipleAttachments"))
+ if (!replace || attachment_point != 0)
+ {
+ // If we know the attachment point then we got here by clicking an
+ // "Attach to..." context menu item, so we should add, not replace.
attachment_point |= ATTACHMENT_ADD;
+ }
sendListToRegions(
"ObjectAttach",
diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h
index 668c04cf15..4c64c77b83 100644
--- a/indra/newview/llselectmgr.h
+++ b/indra/newview/llselectmgr.h
@@ -612,7 +612,7 @@ public:
// verification only, if it doesn't match region info then sale is
// canceled
void sendBuy(const LLUUID& buyer_id, const LLUUID& category_id, const LLSaleInfo sale_info);
- void sendAttach(U8 attachment_point);
+ void sendAttach(U8 attachment_point, bool replace);
void sendDetach();
void sendDropAttachment();
void sendLink();
diff --git a/indra/newview/llviewerattachmenu.cpp b/indra/newview/llviewerattachmenu.cpp
index f683bd8674..5b3c4ea773 100644
--- a/indra/newview/llviewerattachmenu.cpp
+++ b/indra/newview/llviewerattachmenu.cpp
@@ -122,7 +122,7 @@ void LLViewerAttachMenu::attachObjects(const uuid_vec_t& items, const std::strin
LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getLinkedItem(id);
if(item && gInventory.isObjectDescendentOf(id, gInventory.getRootFolderID()))
{
- rez_attachment(item, attachmentp);
+ rez_attachment(item, attachmentp); // don't replace if called from an "Attach To..." menu
}
else if(item && item->isFinished())
{
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index a83980dc23..92195f0a4d 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -5852,6 +5852,7 @@ void handle_buy_land()
class LLObjectAttachToAvatar : public view_listener_t
{
public:
+ LLObjectAttachToAvatar(bool replace) : mReplace(replace) {}
static void setObjectSelection(LLObjectSelectionHandle selection) { sObjectSelection = selection; }
private:
@@ -5865,22 +5866,38 @@ private:
LLViewerJointAttachment* attachment_point = NULL;
if (index > 0)
attachment_point = get_if_there(gAgentAvatarp->mAttachmentPoints, index, (LLViewerJointAttachment*)NULL);
- confirm_replace_attachment(0, attachment_point);
+ confirmReplaceAttachment(0, attachment_point);
}
return true;
}
+ static void onNearAttachObject(BOOL success, void *user_data);
+ void confirmReplaceAttachment(S32 option, LLViewerJointAttachment* attachment_point);
+
+ struct CallbackData
+ {
+ CallbackData(LLViewerJointAttachment* point, bool replace) : mAttachmentPoint(point), mReplace(replace) {}
+
+ LLViewerJointAttachment* mAttachmentPoint;
+ bool mReplace;
+ };
+
protected:
static LLObjectSelectionHandle sObjectSelection;
+ bool mReplace;
};
LLObjectSelectionHandle LLObjectAttachToAvatar::sObjectSelection;
-void near_attach_object(BOOL success, void *user_data)
+// static
+void LLObjectAttachToAvatar::onNearAttachObject(BOOL success, void *user_data)
{
+ if (!user_data) return;
+ CallbackData* cb_data = static_cast<CallbackData*>(user_data);
+
if (success)
{
- const LLViewerJointAttachment *attachment = (LLViewerJointAttachment *)user_data;
+ const LLViewerJointAttachment *attachment = cb_data->mAttachmentPoint;
U8 attachment_id = 0;
if (attachment)
@@ -5900,12 +5917,15 @@ void near_attach_object(BOOL success, void *user_data)
// interpret 0 as "default location"
attachment_id = 0;
}
- LLSelectMgr::getInstance()->sendAttach(attachment_id);
+ LLSelectMgr::getInstance()->sendAttach(attachment_id, cb_data->mReplace);
}
LLObjectAttachToAvatar::setObjectSelection(NULL);
+
+ delete cb_data;
}
-void confirm_replace_attachment(S32 option, void* user_data)
+// static
+void LLObjectAttachToAvatar::confirmReplaceAttachment(S32 option, LLViewerJointAttachment* attachment_point)
{
if (option == 0/*YES*/)
{
@@ -5930,7 +5950,8 @@ void confirm_replace_attachment(S32 option, void* user_data)
delta = delta * 0.5f;
walkToSpot -= delta;
- gAgent.startAutoPilotGlobal(gAgent.getPosGlobalFromAgent(walkToSpot), "Attach", NULL, near_attach_object, user_data, stop_distance);
+ CallbackData* user_data = new CallbackData(attachment_point, mReplace); // *TODO: leak if the callback isn't called?
+ gAgent.startAutoPilotGlobal(gAgent.getPosGlobalFromAgent(walkToSpot), "Attach", NULL, onNearAttachObject, user_data, stop_distance);
gAgentCamera.clearFocusObject();
}
}
@@ -8115,7 +8136,8 @@ void initialize_menus()
commit.add("Object.Touch", boost::bind(&handle_object_touch));
commit.add("Object.SitOrStand", boost::bind(&handle_object_sit_or_stand));
commit.add("Object.Delete", boost::bind(&handle_object_delete));
- view_listener_t::addMenu(new LLObjectAttachToAvatar(), "Object.AttachToAvatar");
+ view_listener_t::addMenu(new LLObjectAttachToAvatar(true), "Object.AttachToAvatar");
+ view_listener_t::addMenu(new LLObjectAttachToAvatar(false), "Object.AttachAddToAvatar");
view_listener_t::addMenu(new LLObjectReturn(), "Object.Return");
view_listener_t::addMenu(new LLObjectReportAbuse(), "Object.ReportAbuse");
view_listener_t::addMenu(new LLObjectMute(), "Object.Mute");
diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp
index c9130b56b4..194213f880 100644
--- a/indra/newview/llwearableitemslist.cpp
+++ b/indra/newview/llwearableitemslist.cpp
@@ -863,12 +863,13 @@ void LLWearableItemsList::ContextMenu::updateItemsVisibility(LLContextMenu* menu
} // for
bool standalone = mParent ? mParent->isStandalone() : false;
+ bool wear_add_visible = mask & (MASK_CLOTHING|MASK_ATTACHMENT) && n_worn == 0 && can_be_worn && (n_already_worn != 0 || mask & MASK_ATTACHMENT);
// *TODO: eliminate multiple traversals over the menu items
setMenuItemVisible(menu, "wear_wear", n_already_worn == 0 && n_worn == 0 && can_be_worn);
setMenuItemEnabled(menu, "wear_wear", n_already_worn == 0 && n_worn == 0);
- setMenuItemVisible(menu, "wear_add", mask == MASK_CLOTHING && n_worn == 0 && n_already_worn != 0 && can_be_worn);
- setMenuItemEnabled(menu, "wear_add", n_items == 1 && canAddWearable(ids.front()) && n_already_worn != 0);
+ setMenuItemVisible(menu, "wear_add", wear_add_visible);
+ setMenuItemEnabled(menu, "wear_add", n_items == 1 && canAddWearable(ids.front()));
setMenuItemVisible(menu, "wear_replace", n_worn == 0 && n_already_worn != 0 && can_be_worn);
//visible only when one item selected and this item is worn
setMenuItemVisible(menu, "edit", !standalone && mask & (MASK_CLOTHING|MASK_BODYPART) && n_worn == n_items && n_worn == 1);
@@ -984,7 +985,18 @@ bool LLWearableItemsList::ContextMenu::canAddWearable(const LLUUID& item_id)
// TODO: investigate wearables may not be loaded at this point EXT-8231
LLViewerInventoryItem* item = gInventory.getItem(item_id);
- if (!item || item->getType() != LLAssetType::AT_CLOTHING)
+ if (!item)
+ {
+ return false;
+ }
+
+ if (item->getType() == LLAssetType::AT_OBJECT)
+ {
+ // *TODO: is this the right check?
+ return isAgentAvatarValid() && gAgentAvatarp->canAttachMoreObjects();
+ }
+
+ if (item->getType() != LLAssetType::AT_CLOTHING)
{
return false;
}
diff --git a/indra/newview/skins/default/xui/en/menu_inspect_object_gear.xml b/indra/newview/skins/default/xui/en/menu_inspect_object_gear.xml
index 8ec7689819..76f68c6d4b 100644
--- a/indra/newview/skins/default/xui/en/menu_inspect_object_gear.xml
+++ b/indra/newview/skins/default/xui/en/menu_inspect_object_gear.xml
@@ -89,6 +89,15 @@
function="Object.EnableWear" />
</menu_item_call>
<menu_item_call
+ label="Add"
+ layout="topleft"
+ name="add">
+ <menu_item_call.on_click
+ function="Object.AttachAddToAvatar" />
+ <menu_item_call.on_visible
+ function="Object.EnableWear" />
+ </menu_item_call>
+ <menu_item_call
label="Report"
layout="topleft"
name="report">
diff --git a/indra/newview/skins/default/xui/en/menu_object.xml b/indra/newview/skins/default/xui/en/menu_object.xml
index b6cc222e96..31f70d99ca 100644
--- a/indra/newview/skins/default/xui/en/menu_object.xml
+++ b/indra/newview/skins/default/xui/en/menu_object.xml
@@ -83,6 +83,15 @@
<menu_item_call.on_enable
function="Object.EnableWear" />
</menu_item_call>
+ <menu_item_call
+ enabled="false"
+ label="Add"
+ name="Add">
+ <menu_item_call.on_click
+ function="Object.AttachAddToAvatar" />
+ <menu_item_call.on_enable
+ function="Object.EnableWear" />
+ </menu_item_call>
<context_menu
label="Attach ▶"
name="Object Attach" />
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
index 8af2e1eaca..5feac53c33 100644
--- a/indra/newview/skins/default/xui/en/menu_wearable_list_item.xml
+++ b/indra/newview/skins/default/xui/en/menu_wearable_list_item.xml
@@ -13,7 +13,7 @@
layout="topleft"
name="wear_wear">
<on_click
- function="Wearable.Add" />
+ function="Wearable.Wear" />
</menu_item_call>
<menu_item_call
label="Add"