From ec55705bdd3ff63a69056a0771f7cc5e3a2530d2 Mon Sep 17 00:00:00 2001 From: Beeks Date: Wed, 22 Sep 2010 06:48:48 -0400 Subject: [PATCH] THE BIGGIE. Holy hell, this was an interesting one to implement. I wish I understood it better. Unfortunately I think this marks the end of trying to re-implement the Local Inventory for Temp and Local textures. It's just not feasible now that the entire inventory system has been whipped into a code shitstorm. Signed-off-by: Beeks --- indra/llcommon/indra_constants.h | 4 + indra/llcommon/llassettype.cpp | 26 +- indra/llcommon/llassettype.h | 11 +- indra/llcommon/stdenums.h | 3 +- indra/llinventory/llinventory.cpp | 32 + indra/llinventory/llinventory.h | 30 +- indra/llinventory/llinventorytype.cpp | 4 + indra/llmessage/lltransfersourceasset.cpp | 2 + indra/llmessage/message_prehash.cpp | 1 + indra/llmessage/message_prehash.h | 1 + indra/newview/app_settings/settings.xml | 38 + indra/newview/ascentprefsvan.cpp | 9 + indra/newview/llagent.cpp | 195 +- indra/newview/llbuildnewviewsscheduler.cpp | 8 +- indra/newview/llfloaterproperties.cpp | 4 +- indra/newview/llfolderview.cpp | 12 +- indra/newview/llfolderview.h | 15 +- indra/newview/llgesturemgr.cpp | 61 +- indra/newview/llinventoryactions.cpp | 2 - indra/newview/llinventorybridge.cpp | 815 +- indra/newview/llinventorybridge.h | 69 +- indra/newview/llinventorymodel.cpp | 184 +- indra/newview/llinventorymodel.h | 30 +- indra/newview/llinventoryview.cpp | 13 +- indra/newview/llinventoryview.h | 2 - indra/newview/lllocalinventory.cpp | 3 +- indra/newview/llpanelinventory.cpp | 5 + indra/newview/llselectmgr.cpp | 13045 ++++++++-------- indra/newview/lltooldraganddrop.cpp | 11 + indra/newview/llviewerinventory.cpp | 208 +- indra/newview/llviewerinventory.h | 27 +- indra/newview/llviewerjointattachment.cpp | 232 +- indra/newview/llviewerjointattachment.h | 21 +- indra/newview/llviewermenu.cpp | 124 +- indra/newview/llviewerobject.cpp | 46 +- indra/newview/llviewerobject.h | 8 +- indra/newview/llviewerobjectlist.cpp | 22 +- indra/newview/llvoavatar.cpp | 268 +- indra/newview/llvoavatar.h | 6 +- indra/newview/llvovolume.cpp | 6 +- indra/newview/llvovolume.h | 2 +- indra/newview/pipeline.cpp | 61 +- indra/newview/scriptcounter.cpp | 13 +- .../xui/en-us/floater_new_outfit_dialog.xml | 4 + .../default/xui/en-us/menu_inventory.xml | 20 +- .../skins/default/xui/en-us/notifications.xml | 8 + .../en-us/panel_preferences_ascent_vanity.xml | 4 + scripts/messages/message_template.msg | 21 + 48 files changed, 8559 insertions(+), 7177 deletions(-) diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h index ae7863d10..6cb2985e0 100644 --- a/indra/llcommon/indra_constants.h +++ b/indra/llcommon/indra_constants.h @@ -253,6 +253,10 @@ const U8 SIM_ACCESS_MAX = SIM_ACCESS_ADULT; // group constants const S32 MAX_AGENT_GROUPS = 25; +// attachment constants +const S32 MAX_AGENT_ATTACHMENTS = 38; +const U8 ATTACHMENT_ADD = 0x80; + // god levels const U8 GOD_MAINTENANCE = 250; const U8 GOD_FULL = 200; diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp index cf3bf89b4..5929afa9a 100644 --- a/indra/llcommon/llassettype.cpp +++ b/indra/llcommon/llassettype.cpp @@ -70,6 +70,8 @@ asset_info_t asset_types[] = { LLAssetType::AT_ANIMATION, "ANIMATION" }, { LLAssetType::AT_GESTURE, "GESTURE" }, { LLAssetType::AT_SIMSTATE, "SIMSTATE" }, + { LLAssetType::AT_LINK, "LINK" }, + { LLAssetType::AT_LINK_FOLDER, "FOLDER_LINK" }, { LLAssetType::AT_NONE, "NONE" }, }; @@ -129,7 +131,10 @@ const char* LLAssetType::mAssetTypeNames[LLAssetType::AT_COUNT] = "jpeg", "animatn", "gesture", - "simstate" + "simstate", + "", + "link", + "link_f" }; // This table is meant for decoding to human readable form. Put any @@ -159,7 +164,10 @@ const char* LLAssetType::mAssetTypeHumanNames[LLAssetType::AT_COUNT] = "jpeg image", "animation", "gesture", - "simstate" + "simstate", + "", + "symbolic link", + "symbolic folder link" }; ///---------------------------------------------------------------------------- @@ -248,6 +256,7 @@ EDragAndDropType LLAssetType::lookupDragAndDropType( EType asset ) case AT_BODYPART: return DAD_BODYPART; case AT_ANIMATION: return DAD_ANIMATION; case AT_GESTURE: return DAD_GESTURE; + case AT_LINK: return DAD_LINK; default: return DAD_NONE; }; } @@ -265,3 +274,16 @@ void LLAssetType::generateDescriptionFor(LLAssetType::EType type, desc.assign(time_str); desc.append(LLAssetType::lookupHumanReadable(type)); } +// static +bool LLAssetType::lookupCanLink(EType asset_type) +{ + return (asset_type == AT_CLOTHING || asset_type == AT_OBJECT || asset_type == AT_CATEGORY || + asset_type == AT_BODYPART || asset_type == AT_GESTURE); +} + +// static +// Not adding this to dictionary since we probably will only have these two types +bool LLAssetType::lookupIsLinkType(EType asset_type) +{ + return (asset_type == AT_LINK || asset_type == AT_LINK_FOLDER); +} \ No newline at end of file diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h index 4077b8d2c..f7c657870 100644 --- a/indra/llcommon/llassettype.h +++ b/indra/llcommon/llassettype.h @@ -131,6 +131,12 @@ public: // simstate file AT_SIMSTATE = 22, + // Inventory symbolic link + AT_LINK = 24, + + // Inventory folder link + AT_LINK_FOLDER = 25, + // +*********************************************+ // | TO ADD AN ELEMENT TO THIS ENUM: | // +*********************************************+ @@ -140,7 +146,7 @@ public: // | 4. ADD TO LLAssetType::mAssetTypeHumanNames | // +*********************************************+ - AT_COUNT = 23, + AT_COUNT = 26, AT_NONE = -1 }; @@ -164,6 +170,9 @@ public: static EType getType(const std::string& sin); static std::string getDesc(EType type); + + static bool lookupCanLink(EType asset_type); + static bool lookupIsLinkType(EType asset_type); private: // don't instantiate or derive one of these objects diff --git a/indra/llcommon/stdenums.h b/indra/llcommon/stdenums.h index 41da51fce..1a5678dde 100644 --- a/indra/llcommon/stdenums.h +++ b/indra/llcommon/stdenums.h @@ -54,7 +54,8 @@ enum EDragAndDropType DAD_BODYPART = 11, DAD_ANIMATION = 12, DAD_GESTURE = 13, - DAD_COUNT = 14, // number of types in this enum + DAD_LINK = 14, + DAD_COUNT = 15, // number of types in this enum }; // Reasons for drags to be denied. diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp index b1cfd6866..bb82966fd 100644 --- a/indra/llinventory/llinventory.cpp +++ b/indra/llinventory/llinventory.cpp @@ -126,6 +126,25 @@ const std::string& LLInventoryObject::getName() const return mName; } +// To bypass linked items, since llviewerinventory's getType +// will return the linked-to item's type instead of this object's type. +LLAssetType::EType LLInventoryObject::getActualType() const +{ + return mType; +} + +BOOL LLInventoryObject::getIsLinkType() const +{ + return LLAssetType::lookupIsLinkType(mType); +} + +// See LLInventoryItem override. +// virtual +const LLUUID& LLInventoryObject::getLinkedUUID() const +{ + return mUUID; +} + LLAssetType::EType LLInventoryObject::getType() const { return mType; @@ -342,6 +361,19 @@ void LLInventoryItem::cloneItem(LLPointer& newitem) const newitem->mUUID.generate(); } +// If this is a linked item, then the UUID of the base object is +// this item's assetID. +// virtual +const LLUUID& LLInventoryItem::getLinkedUUID() const +{ + if (LLAssetType::lookupIsLinkType(getActualType())) + { + return mAssetUUID; + } + + return LLInventoryObject::getLinkedUUID(); +} + const LLPermissions& LLInventoryItem::getPermissions() const { return mPermissions; diff --git a/indra/llinventory/llinventory.h b/indra/llinventory/llinventory.h index 937e12dbc..78824c555 100644 --- a/indra/llinventory/llinventory.h +++ b/indra/llinventory/llinventory.h @@ -89,14 +89,17 @@ public: void copyObject(const LLInventoryObject* other); // LLRefCount requires custom copy // accessors - const LLUUID& getUUID() const; + virtual const LLUUID& getUUID() const; const LLUUID& getParentUUID() const; - const std::string& getName() const; - LLAssetType::EType getType() const; + virtual const LLUUID& getLinkedUUID() const; // get the inventoryID that this item points to, else this item's inventoryID + virtual const std::string& getName() const; + virtual LLAssetType::EType getType() const; + LLAssetType::EType getActualType() const; // bypasses indirection for linked items + BOOL getIsLinkType() const; // mutators - will not call updateServer(); void setUUID(const LLUUID& new_uuid); - void rename(const std::string& new_name); + virtual void rename(const std::string& new_name); void setParent(const LLUUID& new_parent); void setType(LLAssetType::EType type); @@ -231,15 +234,16 @@ public: virtual void cloneItem(LLPointer& newitem) const; // accessors - const LLPermissions& getPermissions() const; - const LLUUID& getCreatorUUID() const; - const LLUUID& getAssetUUID() const; - const std::string& getDescription() const; - const LLSaleInfo& getSaleInfo() const; - LLInventoryType::EType getInventoryType() const; - U32 getFlags() const; - time_t getCreationDate() const; - U32 getCRC32() const; // really more of a checksum. + virtual const LLUUID& getLinkedUUID() const; + virtual const LLPermissions& getPermissions() const; + virtual const LLUUID& getCreatorUUID() const; + virtual const LLUUID& getAssetUUID() const; + virtual const std::string& getDescription() const; + virtual const LLSaleInfo& getSaleInfo() const; + virtual LLInventoryType::EType getInventoryType() const; + virtual U32 getFlags() const; + virtual time_t getCreationDate() const; + virtual U32 getCRC32() const; // really more of a checksum. // mutators - will not call updateServer(), and will never fail // (though it may correct to sane values) diff --git a/indra/llinventory/llinventorytype.cpp b/indra/llinventory/llinventorytype.cpp index a161a0ee0..fb7fd7b77 100644 --- a/indra/llinventory/llinventorytype.cpp +++ b/indra/llinventory/llinventorytype.cpp @@ -120,6 +120,10 @@ DEFAULT_ASSET_FOR_INV_TYPE[LLAssetType::AT_COUNT] = LLInventoryType::IT_NONE, // AT_IMAGE_JPEG LLInventoryType::IT_ANIMATION, // AT_ANIMATION LLInventoryType::IT_GESTURE, // AT_GESTURE + LLInventoryType::IT_NONE, // AT_SIMSTATE + LLInventoryType::IT_NONE, // (null entry) + LLInventoryType::IT_NONE, // AT_LINK + LLInventoryType::IT_NONE, // AT_LINK_FOLDER }; static const int MAX_POSSIBLE_ASSET_TYPES = 2; diff --git a/indra/llmessage/lltransfersourceasset.cpp b/indra/llmessage/lltransfersourceasset.cpp index a4d59275b..d1bfeb4fc 100644 --- a/indra/llmessage/lltransfersourceasset.cpp +++ b/indra/llmessage/lltransfersourceasset.cpp @@ -292,6 +292,8 @@ bool is_asset_id_knowable(LLAssetType::EType type) case LLAssetType::AT_BODYPART: case LLAssetType::AT_ANIMATION: case LLAssetType::AT_GESTURE: + case LLAssetType::AT_LINK: + case LLAssetType::AT_LINK_FOLDER: rv = true; break; default: diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp index 9f10153e5..892547cab 100644 --- a/indra/llmessage/message_prehash.cpp +++ b/indra/llmessage/message_prehash.cpp @@ -174,6 +174,7 @@ char* _PREHASH_UpdateInventoryItem = LLMessageStringTable::getInstance()->getStr char* _PREHASH_UpdateCreateInventoryItem = LLMessageStringTable::getInstance()->getString("UpdateCreateInventoryItem"); char* _PREHASH_MoveInventoryItem = LLMessageStringTable::getInstance()->getString("MoveInventoryItem"); char* _PREHASH_CopyInventoryItem = LLMessageStringTable::getInstance()->getString("CopyInventoryItem"); +char* _PREHASH_LinkInventoryItem = LLMessageStringTable::getInstance()->getString("LinkInventoryItem"); char* _PREHASH_RemoveInventoryItem = LLMessageStringTable::getInstance()->getString("RemoveInventoryItem"); char* _PREHASH_CreateInventoryItem = LLMessageStringTable::getInstance()->getString("CreateInventoryItem"); char* _PREHASH_PathTwistBegin = LLMessageStringTable::getInstance()->getString("PathTwistBegin"); diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h index 8d1708fad..4bf89e513 100644 --- a/indra/llmessage/message_prehash.h +++ b/indra/llmessage/message_prehash.h @@ -174,6 +174,7 @@ extern char * _PREHASH_UpdateInventoryItem; extern char * _PREHASH_UpdateCreateInventoryItem; extern char * _PREHASH_MoveInventoryItem; extern char * _PREHASH_CopyInventoryItem; +extern char * _PREHASH_LinkInventoryItem; extern char * _PREHASH_RemoveInventoryItem; extern char * _PREHASH_CreateInventoryItem; extern char * _PREHASH_PathTwistBegin; diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 610b2b92d..91b89e669 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2,6 +2,44 @@ + BeauchampUseInventoryLinks + + Comment + When making a new outfit, use links for no-copy items + Persist + 1 + Type + Boolean + Value + 0 + + MoyFastMiniMap + + Comment + When making a new outfit, use links for no-copy items + Persist + 1 + Type + Boolean + Value + 0 + + MoyMiniMapCustomColor + + Comment + Custom minimap color you wish to have. + Persist + 1 + Type + Color4 + Value + + 0.375 + 1.0 + 1.0 + 1.0 + + ShyotlRenderUseStreamVBO Comment diff --git a/indra/newview/ascentprefsvan.cpp b/indra/newview/ascentprefsvan.cpp index fa5fd6ca4..67e14ef8a 100644 --- a/indra/newview/ascentprefsvan.cpp +++ b/indra/newview/ascentprefsvan.cpp @@ -79,6 +79,7 @@ private: LLColor4 mLindenColor; LLColor4 mMutedColor; LLColor4 mEMColor; + LLColor4 mCustomColor; U32 mSelectedClient; }; @@ -213,6 +214,7 @@ void LLPrefsAscentVanImpl::refreshValues() mLindenColor = LLSavedSettingsGlue::getCOAColor4("AscentLindenColor"); mMutedColor = LLSavedSettingsGlue::getCOAColor4("AscentMutedColor"); mEMColor = LLSavedSettingsGlue::getCOAColor4("AscentEstateOwnerColor"); + mCustomColor = LLSavedSettingsGlue::getCOAColor4("MoyMiniMapCustomColor"); } void LLPrefsAscentVanImpl::refresh() @@ -236,6 +238,7 @@ void LLPrefsAscentVanImpl::refresh() getChild("linden_color_swatch")->set(mLindenColor); getChild("muted_color_swatch")->set(mMutedColor); getChild("em_color_swatch")->set(mEMColor); + getChild("custom_color_swatch")->set(mCustomColor); LLSavedSettingsGlue::setCOAColor4("EffectColor", LLColor4::white); LLSavedSettingsGlue::setCOAColor4("EffectColor", mEffectColor); @@ -250,6 +253,9 @@ void LLPrefsAscentVanImpl::refresh() LLSavedSettingsGlue::setCOAColor4("AscentEstateOwnerColor", LLColor4::white); LLSavedSettingsGlue::setCOAColor4("AscentEstateOwnerColor", mEMColor); + + LLSavedSettingsGlue::setCOAColor4("MoyMiniMapCustomColor", LLColor4::white); + LLSavedSettingsGlue::setCOAColor4("MoyMiniMapCustomColor", mCustomColor); gAgent.resetClientTag(); } @@ -268,6 +274,8 @@ void LLPrefsAscentVanImpl::cancel() LLSavedSettingsGlue::setCOAColor4("AscentMutedColor", mMutedColor); LLSavedSettingsGlue::setCOAColor4("AscentEstateOwnerColor", LLColor4::yellow); LLSavedSettingsGlue::setCOAColor4("AscentEstateOwnerColor", mEMColor); + LLSavedSettingsGlue::setCOAColor4("MoyMiniMapCustomColor", LLColor4::yellow); + LLSavedSettingsGlue::setCOAColor4("MoyMiniMapCustomColor", mCustomColor); } void LLPrefsAscentVanImpl::apply() @@ -304,6 +312,7 @@ void LLPrefsAscentVanImpl::apply() LLSavedSettingsGlue::setCOAColor4("AscentLindenColor", childGetValue("linden_color_swatch")); LLSavedSettingsGlue::setCOAColor4("AscentMutedColor", childGetValue("muted_color_swatch")); LLSavedSettingsGlue::setCOAColor4("AscentEstateOwnerColor", childGetValue("em_color_swatch")); + LLSavedSettingsGlue::setCOAColor4("MoyMiniMapCustomColor", childGetValue("custom_color_swatch")); LLSavedSettingsGlue::setCOABOOL("AscentUseCustomTag", childGetValue("customize_own_tag_check")); LLSavedSettingsGlue::setCOAString("AscentCustomTagLabel", childGetValue("custom_tag_label_box")); LLSavedSettingsGlue::setCOAColor4("AscentCustomTagColor", childGetValue("custom_tag_color_swatch")); diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 0b1ac3ddc..eb3d75275 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -3463,13 +3463,18 @@ void LLAgent::updateCamera() { LLVOAvatar::attachment_map_t::iterator curiter = iter++; LLViewerJointAttachment* attachment = curiter->second; - LLViewerObject *attached_object = attachment->getObject(); - if (attached_object && !attached_object->isDead() && attached_object->mDrawable.notNull()) + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) { - // clear any existing "early" movements of attachment - attached_object->mDrawable->clearState(LLDrawable::EARLY_MOVE); - gPipeline.updateMoveNormalAsync(attached_object->mDrawable); - attached_object->updateText(); + LLViewerObject *attached_object = (*attachment_iter); + if (attached_object && !attached_object->isDead() && attached_object->mDrawable.notNull()) + { + // clear any existing "early" movements of attachment + attached_object->mDrawable->clearState(LLDrawable::EARLY_MOVE); + gPipeline.updateMoveNormalAsync(attached_object->mDrawable); + attached_object->updateText(); + } } } @@ -6633,7 +6638,15 @@ void LLAgent::sendAgentWearablesUpdate() if( wearable ) { LL_DEBUGS("Wearables") << "Sending wearable " << wearable->getName() << " mItemID = " << mWearableEntry[ i ].mItemID << LL_ENDL; - gMessageSystem->addUUIDFast(_PREHASH_ItemID, mWearableEntry[ i ].mItemID ); + LLUUID item_id = mWearableEntry[i].mItemID; + const LLViewerInventoryItem *item = gInventory.getItem(item_id); + if (item && item->getIsLinkType()) + { + // Get the itemID that this item points to. i.e. make sure + // we are storing baseitems, not their links, in the database. + item_id = item->getLinkedUUID(); + } + gMessageSystem->addUUIDFast(_PREHASH_ItemID, item_id); } else { @@ -6942,7 +6955,8 @@ BOOL LLAgent::selfHasWearable( void* userdata ) BOOL LLAgent::isWearingItem( const LLUUID& item_id ) { - return (getWearableFromWearableItem( item_id ) != NULL); + const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); + return (getWearableFromWearableItem(base_item_id) != NULL); } // static @@ -7246,6 +7260,7 @@ void LLAgent::makeNewOutfit( new_folder_name); bool found_first_item = false; + BOOL no_link = !gSavedSettings.getBOOL("BeauchampUseInventoryLinks"); /////////////////// // Wearables @@ -7255,7 +7270,6 @@ void LLAgent::makeNewOutfit( // Then, iterate though each of the wearables and save copies of them in the folder. S32 i; S32 count = wearables_to_include.count(); - LLDynamicArray delete_items; LLPointer cbdone = NULL; for( i = 0; i < count; ++i ) { @@ -7263,53 +7277,69 @@ void LLAgent::makeNewOutfit( LLWearable* old_wearable = mWearableEntry[ index ].mWearable; if( old_wearable ) { - std::string new_name; - LLWearable* new_wearable; - new_wearable = gWearableList.createCopy(old_wearable); + LLViewerInventoryItem* item = gInventory.getItem(mWearableEntry[index].mItemID); + std::string new_name = item->getName(); if (rename_clothing) { new_name = new_folder_name; new_name.append(" "); new_name.append(old_wearable->getTypeLabel()); LLStringUtil::truncate(new_name, DB_INV_ITEM_NAME_STR_LEN); - new_wearable->setName(new_name); } - LLViewerInventoryItem* item = gInventory.getItem(mWearableEntry[index].mItemID); - S32 todo = addWearableToAgentInventoryCallback::CALL_NONE; - if (!found_first_item) + if (no_link || isWearableCopyable((EWearableType)index)) { - found_first_item = true; - /* set the focus to the first item */ - todo |= addWearableToAgentInventoryCallback::CALL_MAKENEWOUTFITDONE; - /* send the agent wearables update when done */ - cbdone = new sendAgentWearablesUpdateCallback; - } - LLPointer cb = - new addWearableToAgentInventoryCallback( - cbdone, - index, - new_wearable, - todo); - if (isWearableCopyable((EWearableType)index)) - { - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - folder_id, - new_name, - cb); + LLWearable* new_wearable = gWearableList.createCopy(old_wearable); + if (rename_clothing) + { + new_wearable->setName(new_name); + } + + S32 todo = addWearableToAgentInventoryCallback::CALL_NONE; + if (!found_first_item) + { + found_first_item = true; + /* set the focus to the first item */ + todo |= addWearableToAgentInventoryCallback::CALL_MAKENEWOUTFITDONE; + /* send the agent wearables update when done */ + cbdone = new sendAgentWearablesUpdateCallback; + } + LLPointer cb = + new addWearableToAgentInventoryCallback( + cbdone, + index, + new_wearable, + todo); + if (isWearableCopyable((EWearableType)index)) + { + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getLinkedUUID(), + folder_id, + new_name, + cb); + } + else + { + move_inventory_item( + gAgent.getID(), + gAgent.getSessionID(), + item->getLinkedUUID(), + folder_id, + new_name, + cb); + } } else { - move_inventory_item( + link_inventory_item( gAgent.getID(), - gAgent.getSessionID(), - item->getUUID(), + item->getLinkedUUID(), folder_id, - new_name, - cb); + item->getName(), // Apparently, links cannot have arbitrary names... + LLAssetType::AT_LINK, + LLPointer(NULL)); } } } @@ -7322,39 +7352,55 @@ void LLAgent::makeNewOutfit( if( attachments_to_include.count() ) { - BOOL msg_started = FALSE; - LLMessageSystem* msg = gMessageSystem; for( S32 i = 0; i < attachments_to_include.count(); i++ ) { S32 attachment_pt = attachments_to_include[i]; LLViewerJointAttachment* attachment = get_if_there(mAvatarObject->mAttachmentPoints, attachment_pt, (LLViewerJointAttachment*)NULL ); if(!attachment) continue; - LLViewerObject* attached_object = attachment->getObject(); - if(!attached_object) continue; - const LLUUID& item_id = attachment->getItemID(); - if(item_id.isNull()) continue; - LLInventoryItem* item = gInventory.getItem(item_id); - if(!item) continue; - if(!msg_started) - { - msg_started = TRUE; - msg->newMessage("CreateNewOutfitAttachments"); - msg->nextBlock("AgentData"); - msg->addUUID("AgentID", getID()); - msg->addUUID("SessionID", getSessionID()); - msg->nextBlock("HeaderData"); - msg->addUUID("NewFolderID", folder_id); + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) + { + LLViewerObject *attached_object = (*attachment_iter); + if (!attached_object) continue; + const LLUUID& item_id = attached_object->getAttachmentItemID(); + if (item_id.isNull()) continue; + LLInventoryItem* item = gInventory.getItem(item_id); + if (!item) continue; + if (no_link || item->getPermissions().allowCopyBy(gAgent.getID())) + { + const LLUUID& old_folder_id = item->getParentUUID(); + move_inventory_item( + gAgent.getID(), + gAgent.getSessionID(), + item->getLinkedUUID(), + folder_id, + item->getName(), + LLPointer(NULL)); + + if (item->getPermissions().allowCopyBy(gAgent.getID())) + { + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getLinkedUUID(), + old_folder_id, + item->getName(), + LLPointer(NULL)); + } + } + else + { + link_inventory_item( + gAgent.getID(), + item->getLinkedUUID(), + folder_id, + item->getName(), + LLAssetType::AT_LINK, + LLPointer(NULL)); + } } - msg->nextBlock("ObjectData"); - msg->addUUID("OldItemID", item_id); - msg->addUUID("OldFolderID", item->getParentUUID()); } - - if( msg_started ) - { - sendReliableMessage(); - } - } } @@ -8006,11 +8052,16 @@ void LLAgent::userRemoveAllAttachments( void* userdata ) { LLVOAvatar::attachment_map_t::iterator curiter = iter++; LLViewerJointAttachment* attachment = curiter->second; - LLViewerObject* objectp = attachment->getObject(); - if (objectp) + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) { - gMessageSystem->nextBlockFast(_PREHASH_ObjectData); - gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, objectp->getLocalID()); + LLViewerObject *attached_object = (*attachment_iter); + if (attached_object) + { + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, attached_object->getLocalID()); + } } } gMessageSystem->sendReliable( gAgent.getRegionHost() ); diff --git a/indra/newview/llbuildnewviewsscheduler.cpp b/indra/newview/llbuildnewviewsscheduler.cpp index d7f1cd871..c5741edf1 100644 --- a/indra/newview/llbuildnewviewsscheduler.cpp +++ b/indra/newview/llbuildnewviewsscheduler.cpp @@ -59,9 +59,12 @@ void LLBuildNewViewsScheduler::buildNewViews(LLInventoryPanel* panelp, LLInvento << ((S32) objectp->getType()) << " (shouldn't happen)" << llendl; } - else if (objectp->getType() == LLAssetType::AT_CATEGORY) // build new view for category + else if ((objectp->getType() == LLAssetType::AT_CATEGORY) && + (objectp->getActualType() != LLAssetType::AT_LINK_FOLDER)) // build new view for category { - LLInvFVBridge* new_listener = LLInvFVBridge::createBridge(objectp->getType(), + LLInvFVBridge* new_listener = LLInvFVBridge::createBridge( + objectp->getType(), + objectp->getType(), LLInventoryType::IT_CATEGORY, panelp, objectp->getUUID()); @@ -82,6 +85,7 @@ void LLBuildNewViewsScheduler::buildNewViews(LLInventoryPanel* panelp, LLInvento LLInventoryItem* item = (LLInventoryItem*)objectp; LLInvFVBridge* new_listener = LLInvFVBridge::createBridge( item->getType(), + item->getActualType(), item->getInventoryType(), panelp, item->getUUID(), diff --git a/indra/newview/llfloaterproperties.cpp b/indra/newview/llfloaterproperties.cpp index bb7cf85a7..2447f3624 100644 --- a/indra/newview/llfloaterproperties.cpp +++ b/indra/newview/llfloaterproperties.cpp @@ -329,6 +329,8 @@ void LLFloaterProperties::refreshFromItem(LLInventoryItem* item) BOOL can_agent_sell = gAgent.allowOperation(PERM_OWNER, perm, GP_OBJECT_SET_SALE); + BOOL is_link = i->getIsLinkType(); + // You need permission to modify the object to modify an inventory // item in it. LLViewerObject* object = NULL; @@ -509,7 +511,7 @@ void LLFloaterProperties::refreshFromItem(LLInventoryItem* item) ///////////// // Check for ability to change values. - if (is_obj_modify && can_agent_manipulate) + if (!is_link && is_obj_modify && can_agent_manipulate) { childSetEnabled("CheckShareWithGroup",TRUE); childSetEnabled("CheckEveryoneCopy",(owner_mask & PERM_COPY) && (owner_mask & PERM_TRANSFER)); diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index 1956a09e3..b35efe1f0 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -4672,6 +4672,15 @@ LLInventoryFilter::~LLInventoryFilter() BOOL LLInventoryFilter::check(LLFolderViewItem* item) { + LLFolderViewEventListener* listener = item->getListener(); + const LLUUID& item_id = listener->getUUID(); + const LLInventoryObject *obj = gInventory.getObject(item_id); + if (isActive() && obj && obj->getIsLinkType()) + { + // When filtering is active, omit links. + return FALSE; + } + time_t earliest; earliest = time_corrected() - mFilterOps.mHoursAgo * 3600; @@ -4683,8 +4692,7 @@ BOOL LLInventoryFilter::check(LLFolderViewItem* item) { earliest = 0; } - LLFolderViewEventListener* listener = item->getListener(); - const LLUUID& item_id = listener->getUUID(); + //When searching for all labels, we need to explode the filter string //Into an array, and then compare each string to the label seperately diff --git a/indra/newview/llfolderview.h b/indra/newview/llfolderview.h index bff8aa282..2f29ca8e1 100644 --- a/indra/newview/llfolderview.h +++ b/indra/newview/llfolderview.h @@ -100,6 +100,7 @@ public: virtual void cutToClipboard() = 0; virtual BOOL isClipboardPasteable() const = 0; virtual void pasteFromClipboard() = 0; + virtual void pasteLinkFromClipboard() = 0; virtual void buildContextMenu(LLMenuGL& menu, U32 flags) = 0; virtual BOOL isUpToDate() const = 0; virtual BOOL hasChildren() const = 0; @@ -382,13 +383,6 @@ protected: BOOL mIsLoading; LLTimer mTimeSinceRequestStart; - // This function clears the currently selected item, and records - // the specified selected item appropriately for display and use - // in the UI. If open is TRUE, then folders are opened up along - // the way to the selection. - void setSelectionFromRoot(LLFolderViewItem* selection, BOOL openitem, - BOOL take_keyboard_focus = TRUE); - // helper function to change the selection from the root. void changeSelectionFromRoot(LLFolderViewItem* selection, BOOL selected); @@ -401,6 +395,13 @@ protected: virtual BOOL addFolder(LLFolderViewFolder*) { return FALSE; } public: + // This function clears the currently selected item, and records + // the specified selected item appropriately for display and use + // in the UI. If open is TRUE, then folders are opened up along + // the way to the selection. + void setSelectionFromRoot(LLFolderViewItem* selection, BOOL openitem, + BOOL take_keyboard_focus = TRUE); + static void initClass(); static void cleanupClass(); diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp index 476c5240a..9518c3f17 100644 --- a/indra/newview/llgesturemgr.cpp +++ b/indra/newview/llgesturemgr.cpp @@ -65,6 +65,9 @@ LLGestureManager gGestureManager; // Longest time, in seconds, to wait for all animations to stop playing const F32 MAX_WAIT_ANIM_SECS = 30.f; +// If this gesture is a link, get the base gesture that this link points to, +// otherwise just return this id. +static const LLUUID& get_linked_uuid(const LLUUID& item_id); // Lightweight constructor. // init() does the heavy lifting. @@ -212,15 +215,17 @@ void LLGestureManager::activateGestureWithAsset(const LLUUID& item_id, BOOL inform_server, BOOL deactivate_similar) { + const LLUUID& base_item_id = get_linked_uuid(item_id); + if( !gAssetStorage ) { llwarns << "LLGestureManager::activateGestureWithAsset without valid gAssetStorage" << llendl; return; } // If gesture is already active, nothing to do. - if (isGestureActive(item_id)) + if (isGestureActive(base_item_id)) { - llwarns << "Tried to loadGesture twice " << item_id << llendl; + llwarns << "Tried to loadGesture twice " << base_item_id << llendl; return; } @@ -232,13 +237,13 @@ void LLGestureManager::activateGestureWithAsset(const LLUUID& item_id, // For now, put NULL into the item map. We'll build a gesture // class object when the asset data arrives. - mActive[item_id] = NULL; + mActive[base_item_id] = NULL; // Copy the UUID if (asset_id.notNull()) { LLLoadInfo* info = new LLLoadInfo; - info->mItemID = item_id; + info->mItemID = base_item_id; info->mInformServer = inform_server; info->mDeactivateSimilar = deactivate_similar; @@ -258,10 +263,11 @@ void LLGestureManager::activateGestureWithAsset(const LLUUID& item_id, void LLGestureManager::deactivateGesture(const LLUUID& item_id) { - item_map_t::iterator it = mActive.find(item_id); + const LLUUID& base_item_id = get_linked_uuid(item_id); + item_map_t::iterator it = mActive.find(base_item_id); if (it == mActive.end()) { - llwarns << "deactivateGesture for inactive gesture " << item_id << llendl; + llwarns << "deactivateGesture for inactive gesture " << base_item_id << llendl; return; } @@ -278,7 +284,7 @@ void LLGestureManager::deactivateGesture(const LLUUID& item_id) } mActive.erase(it); - gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id); + gInventory.addChangedMask(LLInventoryObserver::LABEL, base_item_id); // Inform the database of this change LLMessageSystem* msg = gMessageSystem; @@ -289,7 +295,7 @@ void LLGestureManager::deactivateGesture(const LLUUID& item_id) msg->addU32("Flags", 0x0); msg->nextBlock("Data"); - msg->addUUID("ItemID", item_id); + msg->addUUID("ItemID", base_item_id); msg->addU32("GestureFlags", 0x0); gAgent.sendReliableMessage(); @@ -300,6 +306,7 @@ void LLGestureManager::deactivateGesture(const LLUUID& item_id) void LLGestureManager::deactivateSimilarGestures(LLMultiGesture* in, const LLUUID& in_item_id) { + const LLUUID& base_in_item_id = get_linked_uuid(in_item_id); std::vector gest_item_ids; // Deactivate all gestures that match @@ -311,7 +318,7 @@ void LLGestureManager::deactivateSimilarGestures(LLMultiGesture* in, const LLUUI // Don't deactivate the gesture we are looking for duplicates of // (for replaceGesture) - if (!gest || item_id == in_item_id) + if (!gest || item_id == base_in_item_id) { // legal, can have null pointers in list ++it; @@ -386,14 +393,16 @@ void LLGestureManager::deactivateSimilarGestures(LLMultiGesture* in, const LLUUI BOOL LLGestureManager::isGestureActive(const LLUUID& item_id) { - item_map_t::iterator it = mActive.find(item_id); + const LLUUID& base_item_id = get_linked_uuid(item_id); + item_map_t::iterator it = mActive.find(base_item_id); return (it != mActive.end()); } BOOL LLGestureManager::isGesturePlaying(const LLUUID& item_id) { - item_map_t::iterator it = mActive.find(item_id); + const LLUUID& base_item_id = get_linked_uuid(item_id); + item_map_t::iterator it = mActive.find(base_item_id); if (it == mActive.end()) return FALSE; LLMultiGesture* gesture = (*it).second; @@ -404,19 +413,20 @@ BOOL LLGestureManager::isGesturePlaying(const LLUUID& item_id) void LLGestureManager::replaceGesture(const LLUUID& item_id, LLMultiGesture* new_gesture, const LLUUID& asset_id) { - item_map_t::iterator it = mActive.find(item_id); + const LLUUID& base_item_id = get_linked_uuid(item_id); + item_map_t::iterator it = mActive.find(base_item_id); if (it == mActive.end()) { - llwarns << "replaceGesture for inactive gesture " << item_id << llendl; + llwarns << "replaceGesture for inactive gesture " << base_item_id << llendl; return; } LLMultiGesture* old_gesture = (*it).second; stopGesture(old_gesture); - mActive.erase(item_id); + mActive.erase(base_item_id); - mActive[item_id] = new_gesture; + mActive[base_item_id] = new_gesture; delete old_gesture; old_gesture = NULL; @@ -427,7 +437,7 @@ void LLGestureManager::replaceGesture(const LLUUID& item_id, LLMultiGesture* new mDeactivateSimilarNames.clear(); LLLoadInfo* info = new LLLoadInfo; - info->mItemID = item_id; + info->mItemID = base_item_id; info->mInformServer = TRUE; info->mDeactivateSimilar = FALSE; @@ -444,7 +454,8 @@ void LLGestureManager::replaceGesture(const LLUUID& item_id, LLMultiGesture* new void LLGestureManager::replaceGesture(const LLUUID& item_id, const LLUUID& new_asset_id) { - item_map_t::iterator it = gGestureManager.mActive.find(item_id); + const LLUUID& base_item_id = get_linked_uuid(item_id); + item_map_t::iterator it = gGestureManager.mActive.find(base_item_id); if (it == mActive.end()) { llwarns << "replaceGesture for inactive gesture " << item_id << llendl; @@ -453,7 +464,7 @@ void LLGestureManager::replaceGesture(const LLUUID& item_id, const LLUUID& new_a // mActive owns this gesture pointer, so clean up memory. LLMultiGesture* gesture = (*it).second; - gGestureManager.replaceGesture(item_id, gesture, new_asset_id); + gGestureManager.replaceGesture(base_item_id, gesture, new_asset_id); } void LLGestureManager::playGesture(LLMultiGesture* gesture) @@ -477,7 +488,8 @@ void LLGestureManager::playGesture(LLMultiGesture* gesture) // Convenience function that looks up the item_id for you. void LLGestureManager::playGesture(const LLUUID& item_id) { - item_map_t::iterator it = mActive.find(item_id); + const LLUUID& base_item_id = get_linked_uuid(item_id); + item_map_t::iterator it = mActive.find(base_item_id); if (it == mActive.end()) return; LLMultiGesture* gesture = (*it).second; @@ -1144,3 +1156,14 @@ void LLGestureManager::getItemIDs(std::vector* ids) ids->push_back(it->first); } } + +// static +const LLUUID& get_linked_uuid(const LLUUID &item_id) +{ + LLViewerInventoryItem* item = gInventory.getItem(item_id); + if (item && item->getIsLinkType()) + { + return item->getLinkedUUID(); + } + return item_id; +} \ No newline at end of file diff --git a/indra/newview/llinventoryactions.cpp b/indra/newview/llinventoryactions.cpp index b48740f14..b6c21ea09 100644 --- a/indra/newview/llinventoryactions.cpp +++ b/indra/newview/llinventoryactions.cpp @@ -784,8 +784,6 @@ class LLBeginIMSession : public inventory_panel_listener_t } }; -void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment); - class LLAttachObject : public inventory_panel_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 036817791..8e2f775a5 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -174,6 +174,9 @@ std::string ICON_NAME[ICON_NAME_COUNT] = "inv_item_animation.tga", "inv_item_gesture.tga", + + "inv_link_item.tga", + "inv_link_folder.tga" }; struct LLWearInfo @@ -218,7 +221,7 @@ time_t LLInvFVBridge::getCreationDate() const return 0; } -// Can be destoryed (or moved to trash) +// Can be destroyed (or moved to trash) BOOL LLInvFVBridge::isItemRemovable() { // derf @@ -229,7 +232,15 @@ BOOL LLInvFVBridge::isItemRemovable() } // LLInventoryModel* model = mInventoryPanel->getModel(); - if(!model) return FALSE; + if (!model) + { + return FALSE; + } + const LLInventoryObject *obj = model->getItem(mUUID); + if (obj && obj->getIsLinkType()) + { + return TRUE; + } if(model->isObjectDescendentOf(mUUID, gAgent.getInventoryRootID())) { return TRUE; @@ -486,14 +497,75 @@ BOOL LLInvFVBridge::isClipboardPasteable() const } // LLInventoryModel* model = mInventoryPanel->getModel(); - if(!model) return FALSE; - BOOL is_agent_inventory = model->isObjectDescendentOf(mUUID, gAgent.getInventoryRootID()); - - if(LLInventoryClipboard::instance().hasContents() && is_agent_inventory) + if (!model) { - return TRUE; + return FALSE; } - return FALSE; + BOOL is_agent_inventory = model->isObjectDescendentOf(mUUID, gAgent.getInventoryRootID()); + if (!LLInventoryClipboard::instance().hasContents() || !is_agent_inventory) + { + return FALSE; + } + + const LLUUID &agent_id = gAgent.getID(); + LLDynamicArray objects; + LLInventoryClipboard::instance().retrieve(objects); + S32 count = objects.count(); + for(S32 i = 0; i < count; i++) + { + const LLUUID &item_id = objects.get(i); + + // Can't paste folders + const LLInventoryCategory *cat = model->getCategory(item_id); + if (cat) + { + return FALSE; + } + + const LLInventoryItem *item = model->getItem(item_id); + if (item) + { + if (!item->getPermissions().allowCopyBy(agent_id)) + { + return FALSE; + } + } + } + return TRUE; +} + +BOOL LLInvFVBridge::isClipboardPasteableAsLink() const +{ + if (!LLInventoryClipboard::instance().hasContents() || !isAgentInventory()) + { + return FALSE; + } + const LLInventoryModel* model = mInventoryPanel->getModel(); + if (!model) + { + return FALSE; + } + + LLDynamicArray objects; + LLInventoryClipboard::instance().retrieve(objects); + S32 count = objects.count(); + for(S32 i = 0; i < count; i++) + { + const LLInventoryItem *item = model->getItem(objects.get(i)); + if (item) + { + if (!LLAssetType::lookupCanLink(item->getActualType())) + { + return FALSE; + } + } + const LLViewerInventoryCategory *cat = model->getCategory(objects.get(i)); + if (cat && LLAssetType::AT_NONE == cat->getPreferredType()) + { + return FALSE; + } + } + return TRUE; } void hideContextEntries(LLMenuGL& menu, @@ -545,37 +617,44 @@ void hideContextEntries(LLMenuGL& menu, void LLInvFVBridge::getClipboardEntries(bool show_asset_id, std::vector &items, std::vector &disabled_items, U32 flags) { - // *TODO: Translate - items.push_back(std::string("Rename")); - if (!isItemRenameable() || (flags & FIRST_SELECTED_ITEM) == 0) - { - disabled_items.push_back(std::string("Rename")); - } + const LLInventoryObject *obj = getInventoryObject(); - if (show_asset_id) + if (obj) { - items.push_back(std::string("Copy Asset UUID")); - - if ( (! ( isItemPermissive() || gAgent.isGodlike() ) ) - || (flags & FIRST_SELECTED_ITEM) == 0) + if (obj->getIsLinkType()) { - disabled_items.push_back(std::string("Copy Asset UUID")); + items.push_back(std::string("Find Original")); + if (isLinkedObjectMissing()) + { + disabled_items.push_back(std::string("Find Original")); + } } - } - + else + { + items.push_back(std::string("Rename")); + if (!isItemRenameable() || (flags & FIRST_SELECTED_ITEM) == 0) + { + disabled_items.push_back(std::string("Rename")); + } + if (show_asset_id) + { + items.push_back(std::string("Copy Asset UUID")); + if ((!( isItemPermissive() || gAgent.isGodlike())) + || (flags & FIRST_SELECTED_ITEM) == 0) + { + disabled_items.push_back(std::string("Copy Asset UUID")); + } + } + items.push_back(std::string("Copy Separator")); - - - - - items.push_back(std::string("Copy Separator")); - - items.push_back(std::string("Copy")); - if (!isItemCopyable()) - { - disabled_items.push_back(std::string("Copy")); + items.push_back(std::string("Copy")); + if (!isItemCopyable()) + { + disabled_items.push_back(std::string("Copy")); + } + } } items.push_back(std::string("Paste")); @@ -584,6 +663,12 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, std::vector disabled_items; if(isInTrash()) { + const LLInventoryObject *obj = getInventoryObject(); + if (obj && obj->getIsLinkType()) + { + items.push_back(std::string("Find Original")); + if (isLinkedObjectMissing()) + { + disabled_items.push_back(std::string("Find Original")); + } + } items.push_back(std::string("Purge Item")); if (!isItemRemovable()) { @@ -618,19 +712,34 @@ void LLInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags) hideContextEntries(menu, items, disabled_items); } +BOOL LLInvFVBridge::isLinkedObjectInTrash() const +{ + if (isInTrash()) return TRUE; + const LLInventoryObject *obj = getInventoryObject(); + if (obj && obj->getIsLinkType()) + { + LLInventoryModel* model = mInventoryPanel->getModel(); + if(!model) return FALSE; + const LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH); + return model->isObjectDescendentOf(obj->getLinkedUUID(), trash_id); + } + return FALSE; +} - - - - - - - - - - - +BOOL LLInvFVBridge::isLinkedObjectMissing() const +{ + const LLInventoryObject *obj = getInventoryObject(); + if (!obj) + { + return TRUE; + } + if (obj->getIsLinkType() && LLAssetType::lookupIsLinkType(obj->getType())) + { + return TRUE; + } + return FALSE; +} // *TODO: remove this BOOL LLInvFVBridge::startDrag(EDragAndDropType* type, LLUUID* id) const @@ -689,7 +798,10 @@ BOOL LLInvFVBridge::isInTrash() const } // LLInventoryModel* model = mInventoryPanel->getModel(); - if(!model) return FALSE; + if (!model) + { + return FALSE; + } LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH); return model->isObjectDescendentOf(mUUID, trash_id); } @@ -779,10 +891,11 @@ const char* safe_inv_type_lookup(LLInventoryType::EType inv_type) } LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, - LLInventoryType::EType inv_type, - LLInventoryPanel* inventory, - const LLUUID& uuid, - U32 flags) + LLAssetType::EType actual_asset_type, + LLInventoryType::EType inv_type, + LLInventoryPanel* inventory, + const LLUUID& uuid, + U32 flags) { LLInvFVBridge* new_listener = NULL; switch(asset_type) @@ -878,8 +991,20 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, case LLAssetType::AT_CATEGORY: case LLAssetType::AT_ROOT_CATEGORY: + if (actual_asset_type == LLAssetType::AT_LINK_FOLDER) + { + // Create a link folder handler instead. + new_listener = new LLLinkFolderBridge(inventory, uuid); + break; + } new_listener = new LLFolderBridge(inventory, uuid); break; + + case LLAssetType::AT_LINK: + case LLAssetType::AT_LINK_FOLDER: + // Only should happen for broken links. + new_listener = new LLLinkItemBridge(inventory, uuid); + break; default: llinfos << "Unhandled asset type (llassetstorage.h): " @@ -902,7 +1027,11 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, void LLItemBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) { - if ("open" == action) + if ("goto" == action) + { + gotoItem(folder); + } + else if ("open" == action) { openItem(); } @@ -961,6 +1090,18 @@ void LLItemBridge::performAction(LLFolderView* folder, LLInventoryModel* model, folder_view_itemp->getListener()->pasteFromClipboard(); return; } + else if ("paste_link" == action) + { + // Single item only + LLInventoryItem* itemp = model->getItem(mUUID); + if (!itemp) return; + + LLFolderViewItem* folder_view_itemp = folder->getItemByID(itemp->getParentUUID()); + if (!folder_view_itemp) return; + + folder_view_itemp->getListener()->pasteLinkFromClipboard(); + return; + } else if("reupload" == action) { LLInventoryItem* item = model->getItem(mUUID); @@ -988,6 +1129,19 @@ void LLItemBridge::performAction(LLFolderView* folder, LLInventoryModel* model, // } +void LLItemBridge::gotoItem(LLFolderView *folder) +{ + LLInventoryObject *obj = getInventoryObject(); + if (obj && obj->getIsLinkType()) + { + LLInventoryView *view = LLInventoryView::getActiveInventory(); + if (view) + { + view->getPanel()->setSelection(obj->getLinkedUUID(), TAKE_FOCUS_NO); + } + } +} + void LLItemBridge::selectItem() { LLViewerInventoryItem* item = (LLViewerInventoryItem*)getItem(); @@ -1120,6 +1274,11 @@ std::string LLItemBridge::getLabelSuffix() const BOOL xfer = perm.allowOperationBy(PERM_TRANSFER, gAgent.getID()); // *TODO: Translate + static std::string LINK = " (link)"; + static std::string BROKEN_LINK = " (broken link)"; + if (LLAssetType::lookupIsLinkType(item->getType())) return BROKEN_LINK; + if (item->getIsLinkType()) return LINK; + const char* EMPTY = ""; const char* NO_COPY = " (no copy)"; const char* NO_MOD = " (no modify)"; @@ -1245,12 +1404,17 @@ BOOL LLItemBridge::removeItem() BOOL LLItemBridge::isItemCopyable() const { LLViewerInventoryItem* item = getItem(); - if (item) + if (item && !item->getIsLinkType()) { - return (item->getPermissions().allowCopyBy(gAgent.getID())); + // All items can be copied since you can + // at least paste-as-link the item, though you + // still may not be able paste the item. + return TRUE; + //return (item->getPermissions().allowCopyBy(gAgent.getID())); } return FALSE; } + BOOL LLItemBridge::copyToClipboard() const { if(isItemCopyable()) @@ -1378,16 +1542,15 @@ BOOL LLFolderBridge::isItemRemovable() for( i = 0; i < descendent_items.count(); i++ ) { LLInventoryItem* item = descendent_items[i]; - if( (item->getType() == LLAssetType::AT_CLOTHING) || - (item->getType() == LLAssetType::AT_BODYPART) ) + if ((item->getType() == LLAssetType::AT_CLOTHING || + item->getType() == LLAssetType::AT_BODYPART) && !item->getIsLinkType()) { if( gAgent.isWearingItem( item->getUUID() ) ) { return FALSE; } } - else - if( item->getType() == LLAssetType::AT_OBJECT ) + else if (item->getType() == LLAssetType::AT_OBJECT && !item->getIsLinkType()) { if( avatar->isWearingAttachment( item->getUUID() ) ) { @@ -1402,6 +1565,63 @@ BOOL LLFolderBridge::isItemRemovable() return TRUE; } +BOOL LLFolderBridge::isClipboardPasteableAsLink() const +{ + // Check normal paste-as-link permissions + if (!LLInvFVBridge::isClipboardPasteableAsLink()) + { + return FALSE; + } + + LLInventoryModel* model = mInventoryPanel->getModel(); + if (!model) + { + return FALSE; + } + + const LLViewerInventoryCategory *current_cat = getCategory(); + if (current_cat) + { +/* TODO + const BOOL is_in_friend_folder = LLFriendCardsManager::instance().isCategoryInFriendFolder(current_cat); +*/ + const LLUUID ¤t_cat_id = current_cat->getUUID(); + LLDynamicArray objects; + LLInventoryClipboard::instance().retrieve(objects); + S32 count = objects.count(); + for (S32 i = 0; i < count; i++) + { + const LLUUID &obj_id = objects.get(i); + const LLInventoryCategory *cat = model->getCategory(obj_id); + if (cat) + { + const LLUUID &cat_id = cat->getUUID(); + // Don't allow recursive pasting + if ((cat_id == current_cat_id) || + model->isObjectDescendentOf(current_cat_id, cat_id)) + { + return FALSE; + } + } +/* TODO + // Don't allow pasting duplicates to the Calling Card/Friends subfolders, see bug EXT-1599 + if (is_in_friend_folder) + { + // If object is direct descendent of current Friends subfolder than return false. + // Note: We can't use 'const LLInventoryCategory *cat', because it may be null + // in case type of obj_id is LLInventoryItem. + if (LLFriendCardsManager::instance().isObjDirectDescendentOfCategory(model->getObject(obj_id), current_cat)) + { + return FALSE; + } + } +*/ + } + } + return TRUE; + +} + BOOL LLFolderBridge::isUpToDate() const { // derf @@ -1495,7 +1715,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, if( (item->getType() == LLAssetType::AT_CLOTHING) || (item->getType() == LLAssetType::AT_BODYPART) ) { - if( gAgent.isWearingItem( item->getUUID() ) ) + if (gAgent.isWearingItem(item->getLinkedUUID())) { is_movable = FALSE; // It's generally movable, but not into the trash! break; @@ -1504,7 +1724,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, else if( item->getType() == LLAssetType::AT_OBJECT ) { - if( avatar->isWearingAttachment( item->getUUID() ) ) + if (avatar->isWearingAttachment(item->getLinkedUUID())) { is_movable = FALSE; // It's generally movable, but not into the trash! break; @@ -1874,6 +2094,10 @@ void LLFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model { pasteFromClipboard(); } + else if ("paste_link" == action) + { + pasteLinkFromClipboard(); + } else if ("properties" == action) { showProperties(); @@ -1923,6 +2147,34 @@ void LLFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model } } +void LLFolderBridge::pasteLinkFromClipboard() +{ + const LLInventoryModel* model = mInventoryPanel->getModel(); + if(model) + { + const LLUUID parent_id(mUUID); + + LLDynamicArray objects; + LLInventoryClipboard::instance().retrieve(objects); + for (LLDynamicArray::const_iterator iter = objects.begin(); + iter != objects.end(); + ++iter) + { + const LLUUID &object_id = (*iter); + if (LLInventoryItem *item = model->getItem(object_id)) + { + link_inventory_item( + gAgent.getID(), + item->getLinkedUUID(), + parent_id, + item->getName(), + LLAssetType::AT_LINK, + LLPointer(NULL)); + } + } + } +} + void LLFolderBridge::openItem() { lldebugs << "LLFolderBridge::openItem()" << llendl; @@ -2308,6 +2560,15 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { // This is a folder in the trash. mItems.clear(); // clear any items that used to exist + const LLInventoryObject *obj = getInventoryObject(); + if (obj && obj->getIsLinkType()) + { + mItems.push_back(std::string("Find Original")); + if (isLinkedObjectMissing()) + { + mDisabledItems.push_back(std::string("Find Original")); + } + } mItems.push_back(std::string("Purge Item")); if (!isItemRemovable()) { @@ -2437,6 +2698,7 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop, case DAD_BODYPART: case DAD_ANIMATION: case DAD_GESTURE: + case DAD_LINK: accept = dragItemIntoFolder((LLInventoryItem*)cargo_data, drop); break; @@ -2685,7 +2947,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, { BOOL is_movable = TRUE; - switch( inv_item->getType() ) + switch (inv_item->getActualType()) { case LLAssetType::AT_ROOT_CATEGORY: is_movable = FALSE; @@ -2702,18 +2964,25 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id); if(is_movable && move_is_into_trash) { - switch(inv_item->getType()) + if (inv_item->getActualType()) { - case LLAssetType::AT_CLOTHING: - case LLAssetType::AT_BODYPART: - is_movable = !gAgent.isWearingItem(inv_item->getUUID()); - break; + is_movable = TRUE; + } + else + { + switch (inv_item->getType()) + { + case LLAssetType::AT_CLOTHING: + case LLAssetType::AT_BODYPART: + is_movable = !gAgent.isWearingItem(inv_item->getUUID()); + break; - case LLAssetType::AT_OBJECT: - is_movable = !avatar->isWearingAttachment(inv_item->getUUID()); - break; - default: - break; + case LLAssetType::AT_OBJECT: + is_movable = !avatar->isWearingAttachment(inv_item->getUUID()); + break; + default: + break; + } } } @@ -3450,14 +3719,24 @@ LLUIImagePtr LLGestureBridge::getIcon() const LLFontGL::StyleFlags LLGestureBridge::getLabelStyle() const { - if( gGestureManager.isGestureActive(mUUID) ) + U8 font = LLFontGL::NORMAL; + + if (gGestureManager.isGestureActive(mUUID)) { - return LLFontGL::BOLD; + font |= LLFontGL::BOLD; } - else + + const LLViewerInventoryItem* item = getItem(); + if (item) { - return LLFontGL::NORMAL; + LLPermissions perm = getItem()->getPermissions(); + if (item->getIsLinkType() || (perm.getGroup() == gAgent.getID())) + { + font |= LLFontGL::ITALIC; + } } + + return (LLFontGL::StyleFlags)font; } std::string LLGestureBridge::getLabelSuffix() const @@ -3538,6 +3817,15 @@ void LLGestureBridge::buildContextMenu(LLMenuGL& menu, U32 flags) std::vector disabled_items; if(isInTrash()) { + const LLInventoryObject *obj = getInventoryObject(); + if (obj && obj->getIsLinkType()) + { + items.push_back(std::string("Find Original")); + if (isLinkedObjectMissing()) + { + disabled_items.push_back(std::string("Find Original")); + } + } items.push_back(std::string("Purge Item")); if (!isItemRemovable()) { @@ -3689,6 +3977,16 @@ LLUUID LLObjectBridge::sContextMenuItemID; BOOL LLObjectBridge::isItemRemovable() { + LLInventoryModel* model = mInventoryPanel->getModel(); + if (!model) + { + return FALSE; + } + const LLInventoryObject *obj = model->getItem(mUUID); + if (obj && obj->getIsLinkType()) + { + return TRUE; + } // //LLVOAvatar* avatar = gAgent.getAvatarObject(); //if(!avatar) return FALSE; @@ -3702,31 +4000,23 @@ LLUIImagePtr LLObjectBridge::getIcon() const return get_item_icon(LLAssetType::AT_OBJECT, mInvType, mAttachPt, mIsMultiObject ); } -void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment); - // virtual void LLObjectBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) { - if ("attach" == action) + if ("attach" == action || "wear_add" == action) { + bool replace = ("attach" == action); // Replace if "Wear"ing. LLUUID object_id = mUUID; LLViewerInventoryItem* item; item = (LLViewerInventoryItem*)gInventory.getItem(object_id); if(item && gInventory.isObjectDescendentOf(object_id, gAgent.getInventoryRootID())) { - - - - - - - - rez_attachment(item, NULL); + rez_attachment(item, NULL, replace); } else if(item && item->isComplete()) { // must be in library. copy it to our inventory and put it on. - LLPointer cb = new RezAttachmentCallback(0); + LLPointer cb = new RezAttachmentCallback(0, replace); copy_inventory_item( gAgent.getID(), item->getPermissions().getOwner(), @@ -3754,7 +4044,7 @@ void LLObjectBridge::performAction(LLFolderView* folder, LLInventoryModel* model gMessageSystem->newMessageFast(_PREHASH_DetachAttachmentIntoInv); gMessageSystem->nextBlockFast(_PREHASH_ObjectData ); gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); - gMessageSystem->addUUIDFast(_PREHASH_ItemID, item->getUUID() ); + gMessageSystem->addUUIDFast(_PREHASH_ItemID, item->getLinkedUUID()); gMessageSystem->sendReliable( gAgent.getRegion()->getHost() ); } @@ -3770,55 +4060,6 @@ void LLObjectBridge::performAction(LLFolderView* folder, LLInventoryModel* model llwarns << "object not found - ignoring" << llendl; } } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - else LLItemBridge::performAction(folder, model, action); } @@ -3859,15 +4100,21 @@ void LLObjectBridge::openItem() LLFontGL::StyleFlags LLObjectBridge::getLabelStyle() const { - LLVOAvatar* avatar = gAgent.getAvatarObject(); - if( avatar && avatar->isWearingAttachment( mUUID ) ) - { - return LLFontGL::BOLD; - } - else - { - return LLFontGL::NORMAL; - } + U8 font = LLFontGL::NORMAL; + + LLVOAvatar* avatar = gAgent.getAvatarObject(); + if (avatar && avatar->isWearingAttachment(mUUID)) + { + font |= LLFontGL::BOLD; + } + + const LLViewerInventoryItem* item = getItem(); + if (item && item->getIsLinkType()) + { + font |= LLFontGL::ITALIC; + } + + return (LLFontGL::StyleFlags)font; } std::string LLObjectBridge::getLabelSuffix() const @@ -3895,10 +4142,10 @@ 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->getUUID(); + payload["item_id"] = item->getLinkedUUID(); // Wear the base object in case this is a link. S32 attach_pt = 0; if (gAgent.getAvatarObject() && attachment) @@ -3914,42 +4161,31 @@ void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attach } } + if (!replace) + { + attach_pt |= ATTACHMENT_ADD; + } payload["attachment_point"] = attach_pt; - if (attachment && attachment->getObject()) + if (replace && attachment && attachment->getNumObjects() > 0) { - - - - - - - LLNotifications::instance().add("ReplaceAttachment", LLSD(), payload, confirm_replace_attachment_rez); } else { - - - - - - - - - - - - - - - LLNotifications::instance().forceResponse(LLNotification::Params("ReplaceAttachment").payload(payload), 0/*YES*/); } } bool confirm_replace_attachment_rez(const LLSD& notification, const LLSD& response) { + if (!gAgent.getAvatarObject()->canAttachMoreObjects()) + { + LLSD args; + args["MAX_ATTACHMENTS"] = llformat("%d", MAX_AGENT_ATTACHMENTS); + LLNotifications::instance().add("MaxAttachmentsOnOutfit", args); + return false; + } S32 option = LLNotification::getSelectedOption(notification, response); if (option == 0/*YES*/) { @@ -4033,24 +4269,16 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { items.push_back(std::string("Attach Separator")); items.push_back(std::string("Object Wear")); + items.push_back(std::string("Object Add")); + if (!avatarp->canAttachMoreObjects()) + { + disabled_items.push_back(std::string("Object Add")); + } items.push_back(std::string("Attach To")); items.push_back(std::string("Attach To HUD")); // commented out for DEV-32347 - AND Commented back in for non-morons. -HgB items.push_back(std::string("Restore to Last Position")); - - - - - - - - - - - - - LLMenuGL* attach_menu = menu.getChildMenuByName("Attach To", TRUE); LLMenuGL* attach_hud_menu = menu.getChildMenuByName("Attach To HUD", TRUE); LLVOAvatar *avatarp = gAgent.getAvatarObject(); @@ -4230,7 +4458,7 @@ void wear_inventory_item_on_avatar( LLInventoryItem* item ) item->getName(), item->getType(), LLWearableBridge::onWearOnAvatarArrived, - new LLUUID(item->getUUID())); + new LLUUID(item->getLinkedUUID())); } } @@ -4705,7 +4933,7 @@ void wear_inventory_category_on_avatar_step2( BOOL proceed, void* userdata ) LLDynamicArray found_container; for(i = 0; i < wearable_count; ++i) { - found = new LLFoundData(item_array.get(i)->getUUID(), + found = new LLFoundData(item_array.get(i)->getLinkedUUID(), item_array.get(i)->getAssetUUID(), item_array.get(i)->getName(), item_array.get(i)->getType()); @@ -4775,9 +5003,9 @@ void wear_inventory_category_on_avatar_step2( BOOL proceed, void* userdata ) LLInventoryItem* item = obj_item_array.get(i); msg->nextBlockFast(_PREHASH_ObjectData ); - msg->addUUIDFast(_PREHASH_ItemID, item->getUUID() ); + msg->addUUIDFast(_PREHASH_ItemID, item->getLinkedUUID()); msg->addUUIDFast(_PREHASH_OwnerID, item->getPermissions().getOwner()); - msg->addU8Fast(_PREHASH_AttachmentPt, 0 ); // Wear at the previous or default attachment point + msg->addU8Fast(_PREHASH_AttachmentPt, 0 | ATTACHMENT_ADD); // 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()); @@ -5041,6 +5269,14 @@ void remove_inventory_category_from_avatar( LLInventoryCategory* category ) } } +struct OnRemoveStruct +{ + LLUUID mUUID; + OnRemoveStruct(const LLUUID& uuid): + mUUID(uuid) + { + } +}; void remove_inventory_category_from_avatar_step2( BOOL proceed, void* userdata) { @@ -5091,9 +5327,9 @@ void remove_inventory_category_from_avatar_step2( BOOL proceed, void* userdata) { gWearableList.getAsset(item_array.get(i)->getAssetUUID(), item_array.get(i)->getName(), - item_array.get(i)->getType(), - LLWearableBridge::onRemoveFromAvatarArrived, - new LLUUID(item_array.get(i)->getUUID())); + item_array.get(i)->getType(), + LLWearableBridge::onRemoveFromAvatarArrived, + new OnRemoveStruct(item_array.get(i)->getLinkedUUID())); } } @@ -5122,12 +5358,12 @@ void remove_inventory_category_from_avatar_step2( BOOL proceed, void* userdata) gMessageSystem->newMessageFast(_PREHASH_DetachAttachmentIntoInv); gMessageSystem->nextBlockFast(_PREHASH_ObjectData ); gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); - gMessageSystem->addUUIDFast(_PREHASH_ItemID, obj_item_array.get(i)->getUUID() ); + gMessageSystem->addUUIDFast(_PREHASH_ItemID, obj_item_array.get(i)->getLinkedUUID()); gMessageSystem->sendReliable( gAgent.getRegion()->getHost() ); // this object might have been selected, so let the selection manager know it's gone now - LLViewerObject *found_obj = gObjectList.findObject( obj_item_array.get(i)->getUUID()); + LLViewerObject *found_obj = gObjectList.findObject( obj_item_array.get(i)->getLinkedUUID()); if (found_obj) { LLSelectMgr::getInstance()->remove(found_obj); @@ -5143,9 +5379,9 @@ void remove_inventory_category_from_avatar_step2( BOOL proceed, void* userdata) { for(i = 0; i < gest_count; ++i) { - if ( gGestureManager.isGestureActive( gest_item_array.get(i)->getUUID()) ) + if ( gGestureManager.isGestureActive( gest_item_array.get(i)->getLinkedUUID()) ) { - gGestureManager.deactivateGesture( gest_item_array.get(i)->getUUID() ); + gGestureManager.deactivateGesture( gest_item_array.get(i)->getLinkedUUID()); gInventory.updateItem( gest_item_array.get(i) ); gInventory.notifyObservers(); } @@ -5168,33 +5404,38 @@ BOOL LLWearableBridge::renameItem(const std::string& new_name) BOOL LLWearableBridge::isItemRemovable() { + LLInventoryModel* model = mInventoryPanel->getModel(); + if (!model) + { + return FALSE; + } + const LLInventoryObject *obj = model->getItem(mUUID); + if (obj && obj->getIsLinkType()) + { + return TRUE; + } // //if(gAgent.isWearingItem(mUUID)) return FALSE; // return LLInvFVBridge::isItemRemovable(); } - - - - - - - - - - LLFontGL::StyleFlags LLWearableBridge::getLabelStyle() const { - if( gAgent.isWearingItem( mUUID ) ) - { - // llinfos << "BOLD" << llendl; - return LLFontGL::BOLD; - } - else - { - return LLFontGL::NORMAL; - } + U8 font = LLFontGL::NORMAL; + + if (gAgent.isWearingItem(mUUID)) + { + font |= LLFontGL::BOLD; + } + + const LLViewerInventoryItem* item = getItem(); + if (item && item->getIsLinkType()) + { + font |= LLFontGL::ITALIC; + } + + return (LLFontGL::StyleFlags)font; } std::string LLWearableBridge::getLabelSuffix() const @@ -5237,7 +5478,7 @@ void LLWearableBridge::performAction(LLFolderView* folder, LLInventoryModel* mod item->getName(), item->getType(), LLWearableBridge::onRemoveFromAvatarArrived, - new LLUUID(mUUID)); + new OnRemoveStruct(item->getLinkedUUID())); } } } @@ -5469,7 +5710,8 @@ void LLWearableBridge::onEditOnAvatar(void* user_data) void LLWearableBridge::editOnAvatar() { - LLWearable* wearable = gAgent.getWearableFromWearableItem(mUUID); + LLUUID linked_id = gInventory.getLinkedItemID(mUUID); + LLWearable* wearable = gAgent.getWearableFromWearableItem(linked_id); if( wearable ) { // Set the tab to the right wearable. @@ -5508,7 +5750,7 @@ void LLWearableBridge::onRemoveFromAvatar(void* user_data) item->getName(), item->getType(), onRemoveFromAvatarArrived, - new LLUUID(self->mUUID)); + new OnRemoveStruct(LLUUID(self->mUUID))); } } } @@ -5517,10 +5759,11 @@ void LLWearableBridge::onRemoveFromAvatar(void* user_data) void LLWearableBridge::onRemoveFromAvatarArrived(LLWearable* wearable, void* userdata) { - LLUUID* item_id = (LLUUID*) userdata; + OnRemoveStruct *on_remove_struct = (OnRemoveStruct*) userdata; + const LLUUID &item_id = gInventory.getLinkedItemID(on_remove_struct->mUUID); if(wearable) { - if( gAgent.isWearingItem( *item_id ) ) + if( gAgent.isWearingItem(item_id)) { EWearableType type = wearable->getType(); @@ -5531,5 +5774,143 @@ void LLWearableBridge::onRemoveFromAvatarArrived(LLWearable* wearable, } } } - delete item_id; + delete on_remove_struct; } + + +// +=================================================+ +// | LLLinkItemBridge | +// +=================================================+ +// For broken links + +std::string LLLinkItemBridge::sPrefix("Link: "); + + +LLUIImagePtr LLLinkItemBridge::getIcon() const +{ + if (LLViewerInventoryItem *item = getItem()) + { + U32 attachment_point = (item->getFlags() & 0xff); // low byte of inventory flags + bool is_multi = LLInventoryItem::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS & item->getFlags(); + + return get_item_icon(item->getActualType(), item->getInventoryType(), attachment_point, is_multi); + } + return get_item_icon(LLAssetType::AT_LINK, LLInventoryType::IT_NONE, 0, FALSE); +} + +void LLLinkItemBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + // *TODO: Translate + lldebugs << "LLLink::buildContextMenu()" << llendl; + std::vector items; + std::vector disabled_items; + + items.push_back(std::string("Find Original")); + disabled_items.push_back(std::string("Find Original")); + + if (isInTrash()) + { + disabled_items.push_back(std::string("Find Original")); + if (isLinkedObjectMissing()) + { + disabled_items.push_back(std::string("Find Original")); + } + items.push_back(std::string("Purge Item")); + items.push_back(std::string("Restore Item")); + } + else + { + items.push_back(std::string("Properties")); + items.push_back(std::string("Find Original")); + if (isLinkedObjectMissing()) + { + disabled_items.push_back(std::string("Find Original")); + } + items.push_back(std::string("Delete")); + } + hideContextEntries(menu, items, disabled_items); +} + + +// +=================================================+ +// | LLLinkBridge | +// +=================================================+ +// For broken links. + +std::string LLLinkFolderBridge::sPrefix("Link: "); + + +LLUIImagePtr LLLinkFolderBridge::getIcon() const +{ + return LLUI::getUIImage("inv_link_folder.tga"); +} + +void LLLinkFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + // *TODO: Translate + lldebugs << "LLLink::buildContextMenu()" << llendl; + std::vector items; + std::vector disabled_items; + + if (isInTrash()) + { + items.push_back(std::string("Find Original")); + if (isLinkedObjectMissing()) + { + disabled_items.push_back(std::string("Find Original")); + } + items.push_back(std::string("Purge Item")); + items.push_back(std::string("Restore Item")); + } + else + { + items.push_back(std::string("Find Original")); + if (isLinkedObjectMissing()) + { + disabled_items.push_back(std::string("Find Original")); + } + items.push_back(std::string("Delete")); + } + hideContextEntries(menu, items, disabled_items); +} + +void LLLinkFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) +{ + if ("goto" == action) + { + gotoItem(folder); + return; + } + LLItemBridge::performAction(folder,model,action); +} + +void LLLinkFolderBridge::gotoItem(LLFolderView *folder) +{ + const LLUUID &cat_uuid = getFolderID(); + if (!cat_uuid.isNull()) + { + if (LLFolderViewItem *base_folder = folder->getItemByID(cat_uuid)) + { + if (LLInventoryModel* model = mInventoryPanel->getModel()) + { + model->fetchDescendentsOf(cat_uuid); + } + base_folder->setOpen(TRUE); + folder->setSelectionFromRoot(base_folder,TRUE); + folder->scrollToShowSelection(); + } + } +} + +const LLUUID &LLLinkFolderBridge::getFolderID() const +{ + if (LLViewerInventoryItem *link_item = getItem()) + { + if (const LLViewerInventoryCategory *cat = link_item->getLinkedCategory()) + { + const LLUUID& cat_uuid = cat->getUUID(); + return cat_uuid; + } + } + return LLUUID::null; +} \ No newline at end of file diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index beeb19144..e789a3b7b 100755 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -30,7 +30,13 @@ * $/LicenseInfo$ */ +#ifndef LL_LLINVENTORYBRIDGE_H +#define LL_LLINVENTORYBRIDGE_H + #include "llfloaterproperties.h" +#include "llfolderview.h" +#include "llinventorymodel.h" +#include "llinventoryview.h" #include "llwearable.h" #include "llviewercontrol.h" #include "llcallingcard.h" @@ -70,6 +76,9 @@ enum EInventoryIcon ANIMATION_ICON_NAME, GESTURE_ICON_NAME, + LINKITEM_ICON_NAME, + LINKFOLDER_ICON_NAME, + ICON_NAME_COUNT }; @@ -187,10 +196,11 @@ public: // This method is a convenience function which creates the correct // type of bridge based on some basic information static LLInvFVBridge* createBridge(LLAssetType::EType asset_type, - LLInventoryType::EType inv_type, - LLInventoryPanel* inventory, - const LLUUID& uuid, - U32 flags = 0x00); + LLAssetType::EType actual_asset_type, + LLInventoryType::EType inv_type, + LLInventoryPanel* inventory, + const LLUUID& uuid, + U32 flags = 0x00); virtual ~LLInvFVBridge() {} virtual const LLUUID& getUUID() const { return mUUID; } @@ -223,7 +233,9 @@ public: virtual BOOL copyToClipboard() const { return FALSE; } virtual void cutToClipboard() {} virtual BOOL isClipboardPasteable() const; + virtual BOOL isClipboardPasteableAsLink() const; virtual void pasteFromClipboard() {} + virtual void pasteLinkFromClipboard() {} void getClipboardEntries(bool show_asset_id, std::vector &items, std::vector &disabled_items, U32 flags); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); @@ -242,6 +254,8 @@ protected: LLInventoryObject* getInventoryObject() const; BOOL isInTrash() const; + BOOL isLinkedObjectInTrash() const; // Is this obj or its baseobj in the trash? + BOOL isLinkedObjectMissing() const; // Is this a linked obj whose baseobj is not in inventory? // return true if the item is in agent inventory. if false, it // must be lost or in the inventory library. BOOL isAgentInventory() const; @@ -275,6 +289,7 @@ public: virtual void restoreItem(); virtual void restoreToWorld(); + virtual void gotoItem(LLFolderView *folder); virtual LLUIImagePtr getIcon() const; virtual const std::string& getDisplayName() const; virtual std::string getLabelSuffix() const; @@ -320,7 +335,9 @@ public: virtual BOOL renameItem(const std::string& new_name); virtual BOOL removeItem(); virtual BOOL isClipboardPasteable() const; + virtual BOOL isClipboardPasteableAsLink() const; virtual void pasteFromClipboard(); + virtual void pasteLinkFromClipboard(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); virtual BOOL hasChildren() const; virtual BOOL dragOrDrop(MASK mask, BOOL drop, @@ -647,3 +664,47 @@ protected: LLInventoryType::EType mInvType; EWearableType mWearableType; }; + +class LLLinkItemBridge : public LLItemBridge +{ + friend class LLInvFVBridge; +public: + virtual const std::string& getPrefix() { return sPrefix; } + + virtual LLUIImagePtr getIcon() const; + virtual void buildContextMenu(LLMenuGL& menu, U32 flags); + +protected: + LLLinkItemBridge(LLInventoryPanel* inventory, const LLUUID& uuid) : + LLItemBridge(inventory, uuid) {} + +protected: + static std::string sPrefix; +}; + + +class LLLinkFolderBridge : public LLItemBridge +{ + friend class LLInvFVBridge; +public: + virtual const std::string& getPrefix() { return sPrefix; } + + virtual LLUIImagePtr getIcon() const; + virtual void buildContextMenu(LLMenuGL& menu, U32 flags); + virtual void performAction(LLFolderView* folder, LLInventoryModel* model, std::string action); + virtual void gotoItem(LLFolderView *folder); + +protected: + LLLinkFolderBridge(LLInventoryPanel* inventory, const LLUUID& uuid) : + LLItemBridge(inventory, uuid) {} + const LLUUID &getFolderID() const; + +protected: + static std::string sPrefix; +}; + +void rez_attachment(LLViewerInventoryItem* item, + LLViewerJointAttachment* attachment, + bool replace = false); + +#endif // LL_LLINVENTORYBRIDGE_H \ No newline at end of file diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 54826d9a3..693960765 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -83,6 +83,10 @@ S16 LLInventoryModel::sBulkFetchCount = 0; // RN: for some reason, using std::queue in the header file confuses the compiler which things it's an xmlrpc_queue static std::deque sFetchQueue; +// Increment this if the inventory contents change in a non-backwards-compatible way. +// For viewers with link items support, former caches are incorrect. +const S32 LLInventoryModel::sCurrentInvCacheVersion = 2; + ///---------------------------------------------------------------------------- /// Local function declarations, constants, enums, and typedefs ///---------------------------------------------------------------------------- @@ -568,6 +572,54 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id, } } +void LLInventoryModel::addChangedMaskForLinks(const LLUUID& object_id, U32 mask) +{ + const LLInventoryObject *obj = getObject(object_id); + if (!obj || obj->getIsLinkType()) + return; + + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + LLLinkedItemIDMatches is_linked_item_match(object_id); + collectDescendentsIf(gAgent.getInventoryRootID(), + cat_array, + item_array, + LLInventoryModel::INCLUDE_TRASH, + is_linked_item_match); + if (cat_array.empty() && item_array.empty()) + { + return; + } + for (LLInventoryModel::cat_array_t::iterator cat_iter = cat_array.begin(); + cat_iter != cat_array.end(); + cat_iter++) + { + LLViewerInventoryCategory *linked_cat = (*cat_iter); + addChangedMask(mask, linked_cat->getUUID()); + }; + +for (LLInventoryModel::item_array_t::iterator iter = item_array.begin(); + iter != item_array.end(); + iter++) + { + LLViewerInventoryItem *linked_item = (*iter); + addChangedMask(mask, linked_item->getUUID()); + }; +} + +const LLUUID& LLInventoryModel::getLinkedItemID(const LLUUID& object_id) const +{ + const LLInventoryItem *item = gInventory.getItem(object_id); + if (!item) + { + return object_id; + } + + // Find the base item in case this a link (if it's not a link, + // this will just be inv_item_id) + return item->getLinkedUUID(); +} + // Generates a string containing the path to the item specified by // item_id. void LLInventoryModel::appendPath(const LLUUID& id, std::string& path) @@ -1099,6 +1151,12 @@ void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent) { mChangedItemIDs.insert(referent); } + // Update all linked items. Starting with just LABEL because I'm + // not sure what else might need to be accounted for this. + if (mModifyMask & LLInventoryObserver::LABEL) + { + addChangedMaskForLinks(referent, LLInventoryObserver::LABEL); + } } // This method to prepares a set of mock inventory which provides @@ -2062,6 +2120,7 @@ bool LLInventoryModel::loadSkeleton( { cat_array_t categories; item_array_t items; + cat_set_t invalid_categories; // Used to mark categories that weren't successfully loaded. std::string owner_id_str; owner_id.toString(owner_id_str); std::string path(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, owner_id_str)); @@ -2088,7 +2147,8 @@ bool LLInventoryModel::loadSkeleton( llinfos << "Unable to gunzip " << gzip_filename << llendl; } } - if (loadFromFile(inventory_filename, categories, items)) + bool is_cache_obsolete = false; + if (loadFromFile(inventory_filename, categories, items, is_cache_obsolete)) { // We were able to find a cache of files. So, use what we // found to generate a set of categories we should add. We @@ -2146,6 +2206,7 @@ bool LLInventoryModel::loadSkeleton( // Add all the items loaded which are parented to a // category with a correctly cached parent count = items.count(); + S32 bad_link_count = 0; cat_map_t::iterator unparented = mCategoryMap.end(); for (int i = 0; i < count; ++i) { @@ -2156,12 +2217,29 @@ bool LLInventoryModel::loadSkeleton( LLViewerInventoryCategory* cat = cit->second; if (cat->getVersion() != NO_VERSION) { + // This can happen if the linked object's baseobj is removed from the cache but the linked object is still in the cache. + if (items[i]->getIsBrokenLink()) + { + bad_link_count++; + lldebugs << "Attempted to add cached link item without baseobj present ( name: " + << items[i]->getName() << " itemID: " << items[i]->getUUID() + << " assetID: " << items[i]->getAssetUUID() + << " ). Ignoring and invalidating " << cat->getName() << " . " << llendl; + invalid_categories.insert(cit->second); + continue; + } addItem(items[i]); cached_item_count += 1; ++child_counts[cat->getUUID()]; } } } + if (bad_link_count > 0) + { + llinfos << "Attempted to add " << bad_link_count + << " cached link items without baseobj present. " + << "The corresponding categories were invalidated." << llendl; + } } else { @@ -2175,6 +2253,17 @@ bool LLInventoryModel::loadSkeleton( } } + // Invalidate all categories that failed fetching descendents for whatever + // reason (e.g. one of the descendents was a broken link). + for (cat_set_t::iterator invalid_cat_it = invalid_categories.begin(); + invalid_cat_it != invalid_categories.end(); + invalid_cat_it++) + { + LLViewerInventoryCategory* cat = (*invalid_cat_it).get(); + cat->setVersion(NO_VERSION); + llinfos << "Invalidating category name: " << cat->getName() << " UUID: " << cat->getUUID() << " due to invalid descendents cache" << llendl; + } + // At this point, we need to set the known descendents for each // category which successfully cached so that we do not // needlessly fetch descendents for categories which we have. @@ -2202,6 +2291,12 @@ bool LLInventoryModel::loadSkeleton( // clean up the gunzipped file. LLFile::remove(inventory_filename); } + if (is_cache_obsolete) + { + // If out of date, remove the gzipped file too. + llwarns << "Inv cache out of date, removing" << llendl; + LLFile::remove(gzip_filename); + } categories.clear(); // will unref and delete entries } @@ -2273,6 +2368,7 @@ bool LLInventoryModel::loadSkeleton( cat_array_t categories; item_array_t items; std::string owner_id_str; + cat_set_t invalid_categories; // Used to mark categories that weren't successfully loaded. owner_id.toString(owner_id_str); std::string path(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, owner_id_str)); std::string inventory_filename; @@ -2298,7 +2394,8 @@ bool LLInventoryModel::loadSkeleton( llinfos << "Unable to gunzip " << gzip_filename << llendl; } } - if(loadFromFile(inventory_filename, categories, items)) + bool is_cache_obsolete = false; + if (loadFromFile(inventory_filename, categories, items, is_cache_obsolete)) { // We were able to find a cache of files. So, use what we // found to generate a set of categories we should add. We @@ -2356,6 +2453,7 @@ bool LLInventoryModel::loadSkeleton( // Add all the items loaded which are parented to a // category with a correctly cached parent count = items.count(); + S32 bad_link_count = 0; cat_map_t::iterator unparented = mCategoryMap.end(); for(int i = 0; i < count; ++i) { @@ -2366,12 +2464,29 @@ bool LLInventoryModel::loadSkeleton( LLViewerInventoryCategory* cat = cit->second; if(cat->getVersion() != NO_VERSION) { + // This can happen if the linked object's baseobj is removed from the cache but the linked object is still in the cache. + if (items[i]->getIsBrokenLink()) + { + bad_link_count++; + lldebugs << "Attempted to add cached link item without baseobj present ( name: " + << items[i]->getName() << " itemID: " << items[i]->getUUID() + << " assetID: " << items[i]->getAssetUUID() + << " ). Ignoring and invalidating " << cat->getName() << " . " << llendl; + invalid_categories.insert(cit->second); + continue; + } addItem(items[i]); cached_item_count += 1; ++child_counts[cat->getUUID()]; } } } + if (bad_link_count > 0) + { + llinfos << "Attempted to add " << bad_link_count + << " cached link items without baseobj present. " + << "The corresponding categories were invalidated." << llendl; + } } else { @@ -2384,6 +2499,16 @@ bool LLInventoryModel::loadSkeleton( addCategory(*it); } } + // Invalidate all categories that failed fetching descendents for whatever + // reason (e.g. one of the descendents was a broken link). + for (cat_set_t::iterator invalid_cat_it = invalid_categories.begin(); + invalid_cat_it != invalid_categories.end(); + invalid_cat_it++) + { + LLViewerInventoryCategory* cat = (*invalid_cat_it).get(); + cat->setVersion(NO_VERSION); + llinfos << "Invalidating category name: " << cat->getName() << " UUID: " << cat->getUUID() << " due to invalid descendents cache" << llendl; + } // At this point, we need to set the known descendents for each // category which successfully cached so that we do not @@ -2412,6 +2537,12 @@ bool LLInventoryModel::loadSkeleton( // clean up the gunzipped file. LLFile::remove(inventory_filename); } + if (is_cache_obsolete) + { + // If out of date, remove the gzipped file too. + llwarns << "Inv cache out of date, removing" << llendl; + LLFile::remove(gzip_filename); + } categories.clear(); // will unref and delete entries } @@ -2809,7 +2940,8 @@ bool LLUUIDAndName::operator>(const LLUUIDAndName& rhs) const // static bool LLInventoryModel::loadFromFile(const std::string& filename, LLInventoryModel::cat_array_t& categories, - LLInventoryModel::item_array_t& items) + LLInventoryModel::item_array_t& items, + bool &is_cache_obsolete) { if(filename.empty()) { @@ -2826,11 +2958,32 @@ bool LLInventoryModel::loadFromFile(const std::string& filename, // *NOTE: This buffer size is hard coded into scanf() below. char buffer[MAX_STRING]; /*Flawfinder: ignore*/ char keyword[MAX_STRING]; /*Flawfinder: ignore*/ + char value[MAX_STRING]; /*Flawfinder: ignore*/ + is_cache_obsolete = true; // Obsolete until proven current while(!feof(file) && fgets(buffer, MAX_STRING, file)) { - sscanf(buffer, " %254s", keyword); /* Flawfinder: ignore */ - if(0 == strcmp("inv_category", keyword)) + sscanf(buffer, " %126s %126s", keyword, value); /* Flawfinder: ignore */ + if (0 == strcmp("inv_cache_version", keyword)) { + S32 version; + int succ = sscanf(value,"%d",&version); + if ((1 == succ) && (version == sCurrentInvCacheVersion)) + { + // Cache is up to date + is_cache_obsolete = false; + continue; + } + else + { + // Cache is out of date + break; + } + } + else if(0 == strcmp("inv_category", keyword)) + { + if (is_cache_obsolete) + break; + LLPointer inv_cat = new LLViewerInventoryCategory(LLUUID::null); if(inv_cat->importFileLocal(file)) { @@ -2875,6 +3028,10 @@ bool LLInventoryModel::loadFromFile(const std::string& filename, } } fclose(file); + + if (is_cache_obsolete) + return false; + return true; } @@ -2896,6 +3053,7 @@ bool LLInventoryModel::saveToFile(const std::string& filename, return false; } + fprintf(file, "\tinv_cache_version\t%d\n", sCurrentInvCacheVersion); S32 count = categories.count(); S32 i; for(i = 0; i < count; ++i) @@ -3455,6 +3613,12 @@ void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**) for(i = 0; i < count; ++i) { titem->unpackMessage(msg, _PREHASH_ItemData, i); + // If the item has already been added (e.g. from link prefetch), then it doesn't need to be re-added. + if (gInventory.getItem(titem->getUUID())) + { + lldebugs << "Skipping prefetched item [ Name: " << titem->getName() << " | Type: " << titem->getActualType() << " | ItemUUID: " << titem->getUUID() << " ] " << llendl; + continue; + } gInventory.updateItem(titem); } @@ -4261,6 +4425,16 @@ bool LLAssetIDMatches ::operator()(LLInventoryCategory* cat, LLInventoryItem* it } +///---------------------------------------------------------------------------- +/// LLLinkedItemIDMatches +///---------------------------------------------------------------------------- +bool LLLinkedItemIDMatches::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + return (item && + (item->getIsLinkType()) && + (item->getLinkedUUID() == mBaseItemID)); // A linked item's assetID will be the compared-to item's itemID. +} + ///---------------------------------------------------------------------------- /// Local function definitions ///---------------------------------------------------------------------------- diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index 187ac795d..40d070d36 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -185,6 +185,10 @@ public: BOOL include_trash, LLInventoryCollectFunctor& add); + // Get the inventoryID that this item points to, else just return item_id + const LLUUID& getLinkedItemID(const LLUUID& object_id) const; + + // This method will return false if this inventory model is in an usabel state. // The inventory model usage is sensitive to the initial construction of the // model. @@ -415,7 +419,9 @@ public: // static bool loadFromFile(const std::string& filename, cat_array_t& categories, - item_array_t& items); + item_array_t& items, + bool& is_cache_obsolete); + static bool saveToFile(const std::string& filename, const cat_array_t& categories, const item_array_t& items); @@ -439,6 +445,8 @@ protected: static void processFetchInventoryReply(LLMessageSystem* msg, void**); bool messageUpdateCore(LLMessageSystem* msg, bool do_accounting); + // Updates all linked items pointing to this id. + void addChangedMaskForLinks(const LLUUID& object_id, U32 mask); protected: cat_array_t* getUnlockedCatArray(const LLUUID& id); @@ -488,6 +496,9 @@ protected: // This flag is used to handle an invalid inventory state. bool mIsAgentInvUsable; +private: + const static S32 sCurrentInvCacheVersion; // expected inventory cache version + public: // *NOTE: DEBUG functionality void dumpInventory(); @@ -540,6 +551,23 @@ protected: }; +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLLinkedItemIDMatches +// +// This functor finds inventory items linked to the specific inventory id. +// Assumes the inventory id is itself not a linked item. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLLinkedItemIDMatches : public LLInventoryCollectFunctor +{ +public: + LLLinkedItemIDMatches(const LLUUID& item_id) : mBaseItemID(item_id) {} + virtual ~LLLinkedItemIDMatches() {} + bool operator()(LLInventoryCategory* cat, LLInventoryItem* item); + +protected: + LLUUID mBaseItemID; +}; + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLIsType // diff --git a/indra/newview/llinventoryview.cpp b/indra/newview/llinventoryview.cpp index 48d861c02..041e1ea37 100644 --- a/indra/newview/llinventoryview.cpp +++ b/indra/newview/llinventoryview.cpp @@ -1937,9 +1937,11 @@ void LLInventoryPanel::buildNewViews(const LLUUID& id) << " for object " << objectp->getName() << " (shouldn't happen)" << llendl; } - else if (objectp->getType() == LLAssetType::AT_CATEGORY) // build new view for category + else if ((objectp->getType() == LLAssetType::AT_CATEGORY) && + (objectp->getActualType() != LLAssetType::AT_LINK_FOLDER)) // build new view for category { LLInvFVBridge* new_listener = LLInvFVBridge::createBridge(objectp->getType(), + objectp->getType(), LLInventoryType::IT_CATEGORY, this, objectp->getUUID()); @@ -1960,6 +1962,7 @@ void LLInventoryPanel::buildNewViews(const LLUUID& id) LLInventoryItem* item = (LLInventoryItem*)objectp; LLInvFVBridge* new_listener = LLInvFVBridge::createBridge( item->getType(), + item->getActualType(), item->getInventoryType(), this, item->getUUID(), @@ -2043,6 +2046,7 @@ void LLInventoryPanel::buildNewViews(const LLInventoryObject* objectp) else if (objectp->getType() == LLAssetType::AT_CATEGORY) // build new view for category { LLInvFVBridge* new_listener = LLInvFVBridge::createBridge(objectp->getType(), + objectp->getType(), LLInventoryType::IT_CATEGORY, this, objectp->getUUID()); @@ -2063,9 +2067,10 @@ void LLInventoryPanel::buildNewViews(const LLInventoryObject* objectp) LLInventoryItem* item = (LLInventoryItem*)objectp; LLInvFVBridge* new_listener = LLInvFVBridge::createBridge( item->getType(), - item->getInventoryType(), - this, - item->getUUID(), + item->getActualType(), + item->getInventoryType(), + this, + item->getUUID(), item->getFlags()); if (new_listener) { diff --git a/indra/newview/llinventoryview.h b/indra/newview/llinventoryview.h index 5d9936476..7ae905cb9 100644 --- a/indra/newview/llinventoryview.h +++ b/indra/newview/llinventoryview.h @@ -419,8 +419,6 @@ BOOL move_inv_category_world_to_agent(const LLUUID& object_id, const BOOL TAKE_FOCUS_YES = TRUE; const BOOL TAKE_FOCUS_NO = FALSE; -void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment); - #endif // LL_LLINVENTORYVIEW_H diff --git a/indra/newview/lllocalinventory.cpp b/indra/newview/lllocalinventory.cpp index 777e70b63..b969f72f2 100644 --- a/indra/newview/lllocalinventory.cpp +++ b/indra/newview/lllocalinventory.cpp @@ -176,7 +176,8 @@ void LLLocalInventory::loadInvCache(std::string filename) LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; - if(LLInventoryModel::loadFromFile(inv_filename, cats, items)) + bool is_cache_obsolete = false; + if(LLInventoryModel::loadFromFile(inv_filename, cats, items, is_cache_obsolete)) { // create a container category for everything LLViewerInventoryCategory* container = new LLViewerInventoryCategory(gAgent.getID()); diff --git a/indra/newview/llpanelinventory.cpp b/indra/newview/llpanelinventory.cpp index ae458bc26..1da05bc42 100644 --- a/indra/newview/llpanelinventory.cpp +++ b/indra/newview/llpanelinventory.cpp @@ -142,6 +142,7 @@ public: virtual void cutToClipboard(); virtual BOOL isClipboardPasteable() const; virtual void pasteFromClipboard(); + virtual void pasteLinkFromClipboard(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); virtual void performAction(LLFolderView* folder, LLInventoryModel* model, std::string action); virtual BOOL isUpToDate() const { return TRUE; } @@ -550,6 +551,10 @@ void LLTaskInvFVBridge::pasteFromClipboard() { } +void LLTaskInvFVBridge::pasteLinkFromClipboard() +{ +} + BOOL LLTaskInvFVBridge::startDrag(EDragAndDropType* type, LLUUID* id) const { //llinfos << "LLTaskInvFVBridge::startDrag()" << llendl; diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 24d9e2d96..bab1291ca 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -1,6519 +1,6526 @@ -/** - * @file llselectmgr.cpp - * @brief A manager for selected objects and faces. - * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -// file include -#include "llselectmgr.h" - -// library includes -#include "llcachename.h" -#include "lldbstrings.h" -#include "lleconomy.h" -#include "llgl.h" -#include "llrender.h" -#include "llpermissions.h" -#include "llpermissionsflags.h" -#include "llundo.h" -#include "lluuid.h" -#include "llvolume.h" -#include "message.h" -#include "object_flags.h" -#include "llquaternion.h" - -// viewer includes -#include "llagent.h" -#include "llviewerwindow.h" -#include "lldrawable.h" -#include "llfloaterinspect.h" -#include "llfloaterproperties.h" -#include "llfloaterreporter.h" -#include "llfloatertools.h" -#include "llframetimer.h" -#include "llfocusmgr.h" -#include "llhudeffecttrail.h" -#include "llhudmanager.h" -#include "llinventorymodel.h" -#include "llmenugl.h" -#include "llmutelist.h" -#include "llstatusbar.h" -#include "llsurface.h" -#include "lltool.h" -#include "lltooldraganddrop.h" -#include "lltoolmgr.h" -#include "lltoolpie.h" -#include "llui.h" -#include "llviewercamera.h" -#include "llviewercontrol.h" -#include "llviewerimagelist.h" -#include "llviewermedia.h" -#include "llviewermediafocus.h" -#include "llviewermenu.h" -#include "llviewerobject.h" -#include "llviewerobjectlist.h" -#include "llviewerregion.h" -#include "llviewerstats.h" -#include "llvoavatar.h" -#include "llvovolume.h" -#include "pipeline.h" -// -#include "llfloaterexport.h" -// - -#include "llglheaders.h" - -LLViewerObject* getSelectedParentObject(LLViewerObject *object) ; -// -// Consts -// - -const S32 NUM_SELECTION_UNDO_ENTRIES = 200; -const F32 SILHOUETTE_UPDATE_THRESHOLD_SQUARED = 0.02f; -const S32 OWNERSHIP_COST_PER_OBJECT = 10; // Must be the same as economy_constants.price_object_claim in the database. -const S32 MAX_ACTION_QUEUE_SIZE = 20; -const S32 MAX_SILS_PER_FRAME = 50; -const S32 MAX_OBJECTS_PER_PACKET = 254; - -// -// Globals -// - -BOOL gDebugSelectMgr = FALSE; - -BOOL gHideSelectedObjects = FALSE; -BOOL gAllowSelectAvatar = FALSE; - -BOOL LLSelectMgr::sRectSelectInclusive = TRUE; -BOOL LLSelectMgr::sRenderSelectionHighlights = TRUE; -BOOL LLSelectMgr::sRenderHiddenSelections = TRUE; -BOOL LLSelectMgr::sRenderLightRadius = FALSE; -F32 LLSelectMgr::sHighlightThickness = 0.f; -F32 LLSelectMgr::sHighlightUScale = 0.f; -F32 LLSelectMgr::sHighlightVScale = 0.f; -F32 LLSelectMgr::sHighlightAlpha = 0.f; -F32 LLSelectMgr::sHighlightAlphaTest = 0.f; -F32 LLSelectMgr::sHighlightUAnim = 0.f; -F32 LLSelectMgr::sHighlightVAnim = 0.f; -LLColor4 LLSelectMgr::sSilhouetteParentColor; -LLColor4 LLSelectMgr::sSilhouetteChildColor; -LLColor4 LLSelectMgr::sHighlightInspectColor; -LLColor4 LLSelectMgr::sHighlightParentColor; -LLColor4 LLSelectMgr::sHighlightChildColor; -LLColor4 LLSelectMgr::sContextSilhouetteColor; -std::set LLSelectMgr::sObjectPropertiesFamilyRequests; - -static LLObjectSelection *get_null_object_selection(); -template<> - const LLSafeHandle::NullFunc - LLSafeHandle::sNullFunc = get_null_object_selection; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// struct LLDeRezInfo -// -// Used to keep track of important derez info. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -struct LLDeRezInfo -{ - EDeRezDestination mDestination; - LLUUID mDestinationID; - LLDeRezInfo(EDeRezDestination dest, const LLUUID& dest_id) : - mDestination(dest), mDestinationID(dest_id) {} -}; - -// -// Imports -// - - -static LLPointer sNullSelection; - -// -// Functions -// - -void LLSelectMgr::cleanupGlobals() -{ - sNullSelection = NULL; - LLSelectMgr::getInstance()->clearSelections(); -} - -LLObjectSelection *get_null_object_selection() -{ - if (sNullSelection.isNull()) - { - sNullSelection = new LLObjectSelection; - } - return sNullSelection; -} - - -//----------------------------------------------------------------------------- -// LLSelectMgr() -//----------------------------------------------------------------------------- -LLSelectMgr::LLSelectMgr() -{ - mTEMode = FALSE; - mLastCameraPos.clearVec(); - - sHighlightThickness = gSavedSettings.getF32("SelectionHighlightThickness"); - sHighlightUScale = gSavedSettings.getF32("SelectionHighlightUScale"); - sHighlightVScale = gSavedSettings.getF32("SelectionHighlightVScale"); - sHighlightAlpha = gSavedSettings.getF32("SelectionHighlightAlpha"); - sHighlightAlphaTest = gSavedSettings.getF32("SelectionHighlightAlphaTest"); - sHighlightUAnim = gSavedSettings.getF32("SelectionHighlightUAnim"); - sHighlightVAnim = gSavedSettings.getF32("SelectionHighlightVAnim"); - - sSilhouetteParentColor = gColors.getColor("SilhouetteParentColor"); - sSilhouetteChildColor = gColors.getColor("SilhouetteChildColor"); - sHighlightParentColor = gColors.getColor("HighlightParentColor"); - sHighlightChildColor = gColors.getColor("HighlightChildColor"); - sHighlightInspectColor = gColors.getColor("HighlightInspectColor"); - sContextSilhouetteColor = gColors.getColor("ContextSilhouetteColor")*0.5f; - - sRenderLightRadius = gSavedSettings.getBOOL("RenderLightRadius"); - - mRenderSilhouettes = TRUE; - - mGridMode = GRID_MODE_WORLD; - gSavedSettings.setS32("GridMode", (S32)GRID_MODE_WORLD); - mGridValid = FALSE; - - mSelectedObjects = new LLObjectSelection(); - mHoverObjects = new LLObjectSelection(); - mHighlightedObjects = new LLObjectSelection(); -} - - -//----------------------------------------------------------------------------- -// ~LLSelectMgr() -//----------------------------------------------------------------------------- -LLSelectMgr::~LLSelectMgr() -{ - clearSelections(); -} - -void LLSelectMgr::clearSelections() -{ - mHoverObjects->deleteAllNodes(); - mSelectedObjects->deleteAllNodes(); - mHighlightedObjects->deleteAllNodes(); - mRectSelectedObjects.clear(); - mGridObjects.deleteAllNodes(); -} - -void LLSelectMgr::update() -{ - mSelectedObjects->cleanupNodes(); -} - -void LLSelectMgr::updateEffects() -{ - //keep reference grid objects active - struct f : public LLSelectedObjectFunctor - { - virtual bool apply(LLViewerObject* object) - { - LLDrawable* drawable = object->mDrawable; - if (drawable) - { - gPipeline.markMoved(drawable); - } - return true; - } - } func; - mGridObjects.applyToObjects(&func); - - if (mEffectsTimer.getElapsedTimeF32() > 1.f) - { - mSelectedObjects->updateEffects(); - mEffectsTimer.reset(); - } -} - -void LLSelectMgr::overrideObjectUpdates() -{ - //override any position updates from simulator on objects being edited - struct f : public LLSelectedNodeFunctor - { - virtual bool apply(LLSelectNode* selectNode) - { - LLViewerObject* object = selectNode->getObject(); - if (object && object->permMove()) - { - if (!selectNode->mLastPositionLocal.isExactlyZero()) - { - object->setPosition(selectNode->mLastPositionLocal); - } - if (selectNode->mLastRotation != LLQuaternion()) - { - object->setRotation(selectNode->mLastRotation); - } - if (!selectNode->mLastScale.isExactlyZero()) - { - object->setScale(selectNode->mLastScale); - } - } - return true; - } - } func; - getSelection()->applyToNodes(&func); -} - -//----------------------------------------------------------------------------- -// Select just the object, not any other group members. -//----------------------------------------------------------------------------- -LLObjectSelectionHandle LLSelectMgr::selectObjectOnly(LLViewerObject* object, S32 face) -{ - llassert( object ); - - //remember primary object - mSelectedObjects->mPrimaryObject = object; - - // Don't add an object that is already in the list - if (object->isSelected() ) { - // make sure point at position is updated - updatePointAt(); - gEditMenuHandler = this; - return NULL; - } - - if (!canSelectObject(object)) - { - //make_ui_sound("UISndInvalidOp"); - return NULL; - } - - // llinfos << "Adding object to selected object list" << llendl; - - // Place it in the list and tag it. - // This will refresh dialogs. - addAsIndividual(object, face); - - // Stop the object from moving (this anticipates changes on the - // simulator in LLTask::userSelect) - // *FIX: shouldn't zero out these either - object->setVelocity(LLVector3::zero); - object->setAcceleration(LLVector3::zero); - //object->setAngularVelocity(LLVector3::zero); - object->resetRot(); - - // Always send to simulator, so you get a copy of the - // permissions structure back. - gMessageSystem->newMessageFast(_PREHASH_ObjectSelect); - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - gMessageSystem->nextBlockFast(_PREHASH_ObjectData); - gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID() ); - LLViewerRegion* regionp = object->getRegion(); - gMessageSystem->sendReliable( regionp->getHost()); - - updatePointAt(); - updateSelectionCenter(); - saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); - - // have selection manager handle edit menu immediately after - // user selects an object - if (mSelectedObjects->getObjectCount()) - { - gEditMenuHandler = this; - } - - return mSelectedObjects; -} - -//----------------------------------------------------------------------------- -// Select the object, parents and children. -//----------------------------------------------------------------------------- -LLObjectSelectionHandle LLSelectMgr::selectObjectAndFamily(LLViewerObject* obj, BOOL add_to_end) -{ - llassert( obj ); - - //remember primary object - mSelectedObjects->mPrimaryObject = obj; - - // This may be incorrect if things weren't family selected before... - djs 07/08/02 - // Don't add an object that is already in the list - if (obj->isSelected() ) - { - // make sure pointat position is updated - updatePointAt(); - gEditMenuHandler = this; - return NULL; - } - - if (!canSelectObject(obj)) - { - //make_ui_sound("UISndInvalidOp"); - return NULL; - } - - // Since we're selecting a family, start at the root, but - // don't include an avatar. - LLViewerObject* root = obj; - - while(!root->isAvatar() && root->getParent() && !root->isJointChild()) - { - LLViewerObject* parent = (LLViewerObject*)root->getParent(); - if (parent->isAvatar()) - { - break; - } - root = parent; - } - - // Collect all of the objects - LLDynamicArray objects; - - root->addThisAndNonJointChildren(objects); - addAsFamily(objects, add_to_end); - - updateSelectionCenter(); - saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); - updatePointAt(); - - dialog_refresh_all(); - - // Always send to simulator, so you get a copy of the permissions - // structure back. - sendSelect(); - - // Stop the object from moving (this anticipates changes on the - // simulator in LLTask::userSelect) - root->setVelocity(LLVector3::zero); - root->setAcceleration(LLVector3::zero); - //root->setAngularVelocity(LLVector3::zero); - root->resetRot(); - - // leave component mode - if (gSavedSettings.getBOOL("EditLinkedParts")) - { - gSavedSettings.setBOOL("EditLinkedParts", FALSE); - promoteSelectionToRoot(); - } - - // have selection manager handle edit menu immediately after - // user selects an object - if (mSelectedObjects->getObjectCount()) - { - gEditMenuHandler = this; - } - - return mSelectedObjects; -} - -//----------------------------------------------------------------------------- -// Select the object, parents and children. -//----------------------------------------------------------------------------- -LLObjectSelectionHandle LLSelectMgr::selectObjectAndFamily(const std::vector& object_list, - BOOL send_to_sim) -{ - // Collect all of the objects, children included - LLDynamicArray objects; - - //clear primary object (no primary object) - mSelectedObjects->mPrimaryObject = NULL; - - if (object_list.size() < 1) - { - return NULL; - } - - // NOTE -- we add the objects in REVERSE ORDER - // to preserve the order in the mSelectedObjects list - for (std::vector::const_reverse_iterator riter = object_list.rbegin(); - riter != object_list.rend(); ++riter) - { - LLViewerObject *object = *riter; - - llassert( object ); - - if (!canSelectObject(object)) continue; - - object->addThisAndNonJointChildren(objects); - addAsFamily(objects); - - // Stop the object from moving (this anticipates changes on the - // simulator in LLTask::userSelect) - object->setVelocity(LLVector3::zero); - object->setAcceleration(LLVector3::zero); - //object->setAngularVelocity(LLVector3::zero); - object->resetRot(); - } - - updateSelectionCenter(); - saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); - updatePointAt(); - dialog_refresh_all(); - - // Almost always send to simulator, so you get a copy of the permissions - // structure back. - // JC: The one case where you don't want to do this is if you're selecting - // all the objects on a sim. - if (send_to_sim) - { - sendSelect(); - } - - // leave component mode - if (gSavedSettings.getBOOL("EditLinkedParts")) - { - gSavedSettings.setBOOL("EditLinkedParts", FALSE); - promoteSelectionToRoot(); - } - - // have selection manager handle edit menu immediately after - // user selects an object - if (mSelectedObjects->getObjectCount()) - { - gEditMenuHandler = this; - } - - return mSelectedObjects; -} - -// Use for when the simulator kills an object. This version also -// handles informing the current tool of the object's deletion. -// -// Caller needs to call dialog_refresh_all if necessary. -BOOL LLSelectMgr::removeObjectFromSelections(const LLUUID &id) -{ - BOOL object_found = FALSE; - LLTool *tool = NULL; - if (!gNoRender) - { - tool = LLToolMgr::getInstance()->getCurrentTool(); - - // It's possible that the tool is editing an object that is not selected - LLViewerObject* tool_editing_object = tool->getEditingObject(); - if( tool_editing_object && tool_editing_object->mID == id) - { - tool->stopEditing(); - object_found = TRUE; - } - } - - // Iterate through selected objects list and kill the object - if( !object_found ) - { - for (LLObjectSelection::iterator iter = getSelection()->begin(); - iter != getSelection()->end(); ) - { - LLObjectSelection::iterator curiter = iter++; - LLViewerObject* object = (*curiter)->getObject(); - if (object->mID == id) - { - if (tool) - { - tool->stopEditing(); - } - - // lose the selection, don't tell simulator, it knows - deselectObjectAndFamily(object, FALSE); - object_found = TRUE; - break; // must break here, may have removed multiple objects from list - } - else if (object->isAvatar()) - { - // It's possible the item being removed has an avatar sitting on it - // So remove the avatar that is sitting on the object. - deselectObjectAndFamily(object, FALSE); - break; // must break here, may have removed multiple objects from list - } - } - } - - return object_found; -} - -void LLSelectMgr::deselectObjectAndFamily(LLViewerObject* object, BOOL send_to_sim, BOOL include_entire_object) -{ - // bail if nothing selected or if object wasn't selected in the first place - if(!object) return; - if(!object->isSelected()) return; - - // Collect all of the objects, and remove them - LLDynamicArray objects; - - if (include_entire_object) - { - // Since we're selecting a family, start at the root, but - // don't include an avatar. - LLViewerObject* root = object; - - while(!root->isAvatar() && root->getParent() && !root->isJointChild()) - { - LLViewerObject* parent = (LLViewerObject*)root->getParent(); - if (parent->isAvatar()) - { - break; - } - root = parent; - } - - object = root; - } - else - { - object = (LLViewerObject*)object->getRoot(); - } - - object->addThisAndAllChildren(objects); - remove(objects); - - if (!send_to_sim) return; - - //----------------------------------------------------------- - // Inform simulator of deselection - //----------------------------------------------------------- - LLViewerRegion* regionp = object->getRegion(); - - BOOL start_new_message = TRUE; - S32 select_count = 0; - - LLMessageSystem* msg = gMessageSystem; - for (U32 i = 0; i < objects.size(); i++) - { - if (start_new_message) - { - msg->newMessageFast(_PREHASH_ObjectDeselect); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - select_count++; - start_new_message = FALSE; - } - - msg->nextBlockFast(_PREHASH_ObjectData); - msg->addU32Fast(_PREHASH_ObjectLocalID, (objects[i])->getLocalID()); - select_count++; - - // Zap the angular velocity, as the sim will set it to zero - objects[i]->setAngularVelocity( 0,0,0 ); - objects[i]->setVelocity( 0,0,0 ); - - if(msg->isSendFull(NULL) || select_count >= MAX_OBJECTS_PER_PACKET) - { - msg->sendReliable(regionp->getHost() ); - select_count = 0; - start_new_message = TRUE; - } - } - - if (!start_new_message) - { - msg->sendReliable(regionp->getHost() ); - } - - updatePointAt(); - updateSelectionCenter(); -} - -void LLSelectMgr::deselectObjectOnly(LLViewerObject* object, BOOL send_to_sim) -{ - // bail if nothing selected or if object wasn't selected in the first place - if (!object) return; - if (!object->isSelected() ) return; - - // Zap the angular velocity, as the sim will set it to zero - object->setAngularVelocity( 0,0,0 ); - object->setVelocity( 0,0,0 ); - - if (send_to_sim) - { - LLViewerRegion* region = object->getRegion(); - gMessageSystem->newMessageFast(_PREHASH_ObjectDeselect); - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - gMessageSystem->nextBlockFast(_PREHASH_ObjectData); - gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID() ); - gMessageSystem->sendReliable(region->getHost()); - } - - // This will refresh dialogs. - remove( object ); - - updatePointAt(); - updateSelectionCenter(); -} - - -//----------------------------------------------------------------------------- -// addAsFamily -//----------------------------------------------------------------------------- - -void LLSelectMgr::addAsFamily(std::vector& objects, BOOL add_to_end) -{ - for (std::vector::iterator iter = objects.begin(); - iter != objects.end(); ++iter) - { - LLViewerObject* objectp = *iter; - - // Can't select yourself - if (objectp->mID == gAgentID - && !gAllowSelectAvatar) - { - continue; - } - - if (!objectp->isSelected()) - { - LLSelectNode *nodep = new LLSelectNode(objectp, TRUE); - if (add_to_end) - { - mSelectedObjects->addNodeAtEnd(nodep); - } - else - { - mSelectedObjects->addNode(nodep); - } - objectp->setSelected(TRUE); - - if (objectp->getNumTEs() > 0) - { - nodep->selectAllTEs(TRUE); - } - else - { - // object has no faces, so don't mess with faces - } - } - else - { - // we want this object to be selected for real - // so clear transient flag - LLSelectNode* select_node = mSelectedObjects->findNode(objectp); - if (select_node) - { - select_node->setTransient(FALSE); - } - } - } - saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); -} - -//----------------------------------------------------------------------------- -// addAsIndividual() - a single object, face, etc -//----------------------------------------------------------------------------- -void LLSelectMgr::addAsIndividual(LLViewerObject *objectp, S32 face, BOOL undoable) -{ - // check to see if object is already in list - LLSelectNode *nodep = mSelectedObjects->findNode(objectp); - - // if not in list, add it - if (!nodep) - { - nodep = new LLSelectNode(objectp, TRUE); - mSelectedObjects->addNode(nodep); - llassert_always(nodep->getObject()); - } - else - { - // make this a full-fledged selection - nodep->setTransient(FALSE); - // Move it to the front of the list - mSelectedObjects->moveNodeToFront(nodep); - } - - // Make sure the object is tagged as selected - objectp->setSelected( TRUE ); - - // And make sure we don't consider it as part of a family - nodep->mIndividualSelection = TRUE; - - // Handle face selection - if (objectp->getNumTEs() <= 0) - { - // object has no faces, so don't do anything - } - else if (face == SELECT_ALL_TES) - { - nodep->selectAllTEs(TRUE); - } - else if (0 <= face && face < SELECT_MAX_TES) - { - nodep->selectTE(face, TRUE); - } - else - { - llerrs << "LLSelectMgr::add face " << face << " out-of-range" << llendl; - return; - } - - saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); - updateSelectionCenter(); - dialog_refresh_all(); -} - - -LLObjectSelectionHandle LLSelectMgr::setHoverObject(LLViewerObject *objectp, S32 face) -{ - // Always blitz hover list when setting - mHoverObjects->deleteAllNodes(); - - if (!objectp) - { - return NULL; - } - - // Can't select yourself - if (objectp->mID == gAgentID) - { - return NULL; - } - - // Can't select land - if (objectp->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH) - { - return NULL; - } - - // Collect all of the objects - LLDynamicArray objects; - objectp = objectp->getRootEdit(); - objectp->addThisAndNonJointChildren(objects); - - for (std::vector::iterator iter = objects.begin(); - iter != objects.end(); ++iter) - { - LLViewerObject* cur_objectp = *iter; - LLSelectNode* nodep = new LLSelectNode(cur_objectp, FALSE); - nodep->selectTE(face, TRUE); - mHoverObjects->addNodeAtEnd(nodep); - } - - requestObjectPropertiesFamily(objectp); - return mHoverObjects; -} - -LLSelectNode *LLSelectMgr::getHoverNode() -{ - return getHoverObjects()->getFirstRootNode(); -} - -void LLSelectMgr::highlightObjectOnly(LLViewerObject* objectp) -{ - if (!objectp) - { - return; - } - - if (objectp->getPCode() != LL_PCODE_VOLUME) - { - return; - } - - if ((gSavedSettings.getBOOL("SelectOwnedOnly") && !objectp->permYouOwner()) || - (gSavedSettings.getBOOL("SelectMovableOnly") && !objectp->permMove())) - { - // only select my own objects - return; - } - - mRectSelectedObjects.insert(objectp); -} - -void LLSelectMgr::highlightObjectAndFamily(LLViewerObject* objectp) -{ - if (!objectp) - { - return; - } - - LLViewerObject* root_obj = (LLViewerObject*)objectp->getRoot(); - - highlightObjectOnly(root_obj); - - LLViewerObject::const_child_list_t& child_list = root_obj->getChildren(); - for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); - iter != child_list.end(); iter++) - { - LLViewerObject* child = *iter; - highlightObjectOnly(child); - } -} - -// Note that this ignores the "select owned only" flag -// It's also more efficient than calling the single-object version over and over. -void LLSelectMgr::highlightObjectAndFamily(const std::vector& objects) -{ - for (std::vector::const_iterator iter1 = objects.begin(); - iter1 != objects.end(); ++iter1) - { - LLViewerObject* object = *iter1; - - if (!object) - { - continue; - } - if (object->getPCode() != LL_PCODE_VOLUME) - { - continue; - } - - LLViewerObject* root = (LLViewerObject*)object->getRoot(); - mRectSelectedObjects.insert(root); - - LLViewerObject::const_child_list_t& child_list = root->getChildren(); - for (LLViewerObject::child_list_t::const_iterator iter2 = child_list.begin(); - iter2 != child_list.end(); iter2++) - { - LLViewerObject* child = *iter2; - mRectSelectedObjects.insert(child); - } - } -} - -void LLSelectMgr::unhighlightObjectOnly(LLViewerObject* objectp) -{ - if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME)) - { - return; - } - - mRectSelectedObjects.erase(objectp); -} - -void LLSelectMgr::unhighlightObjectAndFamily(LLViewerObject* objectp) -{ - if (!objectp) - { - return; - } - - LLViewerObject* root_obj = (LLViewerObject*)objectp->getRoot(); - - unhighlightObjectOnly(root_obj); - - LLViewerObject::const_child_list_t& child_list = root_obj->getChildren(); - for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); - iter != child_list.end(); iter++) - { - LLViewerObject* child = *iter; - unhighlightObjectOnly(child); - } -} - - -void LLSelectMgr::unhighlightAll() -{ - mRectSelectedObjects.clear(); - mHighlightedObjects->deleteAllNodes(); -} - -LLObjectSelectionHandle LLSelectMgr::selectHighlightedObjects() -{ - if (!mHighlightedObjects->getNumNodes()) - { - return NULL; - } - - //clear primary object - mSelectedObjects->mPrimaryObject = NULL; - - for (LLObjectSelection::iterator iter = getHighlightedObjects()->begin(); - iter != getHighlightedObjects()->end(); ) - { - LLObjectSelection::iterator curiter = iter++; - - LLSelectNode *nodep = *curiter; - LLViewerObject* objectp = nodep->getObject(); - - if (!canSelectObject(objectp)) - { - continue; - } - - // already selected - if (objectp->isSelected()) - { - continue; - } - - LLSelectNode* new_nodep = new LLSelectNode(*nodep); - mSelectedObjects->addNode(new_nodep); - - // flag this object as selected - objectp->setSelected(TRUE); - - mSelectedObjects->mSelectType = getSelectTypeForObject(objectp); - - // request properties on root objects - if (objectp->isRootEdit()) - { - requestObjectPropertiesFamily(objectp); - } - } - - // pack up messages to let sim know these objects are selected - sendSelect(); - unhighlightAll(); - updateSelectionCenter(); - saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); - updatePointAt(); - - if (mSelectedObjects->getObjectCount()) - { - gEditMenuHandler = this; - } - - return mSelectedObjects; -} - -void LLSelectMgr::deselectHighlightedObjects() -{ - BOOL select_linked_set = !gSavedSettings.getBOOL("EditLinkedParts"); - for (std::set >::iterator iter = mRectSelectedObjects.begin(); - iter != mRectSelectedObjects.end(); iter++) - { - LLViewerObject *objectp = *iter; - if (!select_linked_set) - { - deselectObjectOnly(objectp); - } - else - { - LLViewerObject* root_object = (LLViewerObject*)objectp->getRoot(); - if (root_object->isSelected()) - { - deselectObjectAndFamily(root_object); - } - } - } - - unhighlightAll(); -} - -void LLSelectMgr::addGridObject(LLViewerObject* objectp) -{ - LLSelectNode* nodep = new LLSelectNode(objectp, FALSE); - mGridObjects.addNodeAtEnd(nodep); - - LLViewerObject::const_child_list_t& child_list = objectp->getChildren(); - for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); - iter != child_list.end(); iter++) - { - LLViewerObject* child = *iter; - nodep = new LLSelectNode(child, FALSE); - mGridObjects.addNodeAtEnd(nodep); - } -} - -void LLSelectMgr::clearGridObjects() -{ - mGridObjects.deleteAllNodes(); -} - -void LLSelectMgr::setGridMode(EGridMode mode) -{ - mGridMode = mode; - gSavedSettings.setS32("GridMode", mode); - updateSelectionCenter(); - mGridValid = FALSE; -} - -void LLSelectMgr::getGrid(LLVector3& origin, LLQuaternion &rotation, LLVector3 &scale) -{ - mGridObjects.cleanupNodes(); - - LLViewerObject* first_grid_object = mGridObjects.getFirstObject(); - - if (mGridMode == GRID_MODE_LOCAL && mSelectedObjects->getObjectCount()) - { - //LLViewerObject* root = getSelectedParentObject(mSelectedObjects->getFirstObject()); - LLBBox bbox = mSavedSelectionBBox; - mGridOrigin = mSavedSelectionBBox.getCenterAgent(); - mGridScale = mSavedSelectionBBox.getExtentLocal() * 0.5f; - - // DEV-12570 Just taking the saved selection box rotation prevents - // wild rotations of linked sets while in local grid mode - //if(mSelectedObjects->getObjectCount() < 2 || !root || root->mDrawable.isNull()) - { - mGridRotation = mSavedSelectionBBox.getRotation(); - } - /*else //set to the root object - { - mGridRotation = root->getRenderRotation(); - }*/ - } - else if (mGridMode == GRID_MODE_REF_OBJECT && first_grid_object && first_grid_object->mDrawable.notNull()) - { - mGridRotation = first_grid_object->getRenderRotation(); - LLVector3 first_grid_obj_pos = first_grid_object->getRenderPosition(); - - LLVector3 min_extents(F32_MAX, F32_MAX, F32_MAX); - LLVector3 max_extents(-F32_MAX, -F32_MAX, -F32_MAX); - BOOL grid_changed = FALSE; - for (LLObjectSelection::iterator iter = mGridObjects.begin(); - iter != mGridObjects.end(); ++iter) - { - LLViewerObject* object = (*iter)->getObject(); - LLDrawable* drawable = object->mDrawable; - if (drawable) - { - const LLVector3* ext = drawable->getSpatialExtents(); - update_min_max(min_extents, max_extents, ext[0]); - update_min_max(min_extents, max_extents, ext[1]); - grid_changed = TRUE; - } - } - if (grid_changed) - { - mGridOrigin = lerp(min_extents, max_extents, 0.5f); - LLDrawable* drawable = first_grid_object->mDrawable; - if (drawable && drawable->isActive()) - { - mGridOrigin = mGridOrigin * first_grid_object->getRenderMatrix(); - } - mGridScale = (max_extents - min_extents) * 0.5f; - } - } - else // GRID_MODE_WORLD or just plain default - { - const BOOL non_root_ok = TRUE; - LLViewerObject* first_object = mSelectedObjects->getFirstRootObject(non_root_ok); - - mGridOrigin.clearVec(); - mGridRotation.loadIdentity(); - - mSelectedObjects->mSelectType = getSelectTypeForObject( first_object ); - - switch (mSelectedObjects->mSelectType) - { - case SELECT_TYPE_ATTACHMENT: - if (first_object && first_object->getRootEdit()->mDrawable.notNull()) - { - // this means this object *has* to be an attachment - LLXform* attachment_point_xform = first_object->getRootEdit()->mDrawable->mXform.getParent(); - mGridOrigin = attachment_point_xform->getWorldPosition(); - mGridRotation = attachment_point_xform->getWorldRotation(); - mGridScale = LLVector3(1.f, 1.f, 1.f) * gSavedSettings.getF32("GridResolution"); - } - break; - case SELECT_TYPE_HUD: - // use HUD-scaled grid - mGridScale = LLVector3(0.25f, 0.25f, 0.25f); - break; - case SELECT_TYPE_WORLD: - mGridScale = LLVector3(1.f, 1.f, 1.f) * gSavedSettings.getF32("GridResolution"); - break; - } - } - llassert(mGridOrigin.isFinite()); - - origin = mGridOrigin; - rotation = mGridRotation; - scale = mGridScale; - mGridValid = TRUE; -} - -//----------------------------------------------------------------------------- -// remove() - an array of objects -//----------------------------------------------------------------------------- - -void LLSelectMgr::remove(std::vector& objects) -{ - for (std::vector::iterator iter = objects.begin(); - iter != objects.end(); ++iter) - { - LLViewerObject* objectp = *iter; - LLSelectNode* nodep = mSelectedObjects->findNode(objectp); - if (nodep) - { - objectp->setSelected(FALSE); - mSelectedObjects->removeNode(nodep); - nodep = NULL; - } - } - updateSelectionCenter(); - dialog_refresh_all(); -} - - -//----------------------------------------------------------------------------- -// remove() - a single object -//----------------------------------------------------------------------------- -void LLSelectMgr::remove(LLViewerObject *objectp, S32 te, BOOL undoable) -{ - // get object node (and verify it is in the selected list) - LLSelectNode *nodep = mSelectedObjects->findNode(objectp); - if (!nodep) - { - return; - } - - // if face = all, remove object from list - if ((objectp->getNumTEs() <= 0) || (te == SELECT_ALL_TES)) - { - // Remove all faces (or the object doesn't have faces) so remove the node - mSelectedObjects->removeNode(nodep); - nodep = NULL; - objectp->setSelected( FALSE ); - } - else if (0 <= te && te < SELECT_MAX_TES) - { - // ...valid face, check to see if it was on - if (nodep->isTESelected(te)) - { - nodep->selectTE(te, FALSE); - } - else - { - llerrs << "LLSelectMgr::remove - tried to remove TE " << te << " that wasn't selected" << llendl; - return; - } - - // ...check to see if this operation turned off all faces - BOOL found = FALSE; - for (S32 i = 0; i < nodep->getObject()->getNumTEs(); i++) - { - found = found || nodep->isTESelected(i); - } - - // ...all faces now turned off, so remove - if (!found) - { - mSelectedObjects->removeNode(nodep); - nodep = NULL; - objectp->setSelected( FALSE ); - // *FIXME: Doesn't update simulator that object is no longer selected - } - } - else - { - // ...out of range face - llerrs << "LLSelectMgr::remove - TE " << te << " out of range" << llendl; - } - - updateSelectionCenter(); - dialog_refresh_all(); -} - - -//----------------------------------------------------------------------------- -// removeAll() -//----------------------------------------------------------------------------- -void LLSelectMgr::removeAll() -{ - for (LLObjectSelection::iterator iter = mSelectedObjects->begin(); - iter != mSelectedObjects->end(); iter++ ) - { - LLViewerObject *objectp = (*iter)->getObject(); - objectp->setSelected( FALSE ); - } - - mSelectedObjects->deleteAllNodes(); - - updateSelectionCenter(); - dialog_refresh_all(); -} - -//----------------------------------------------------------------------------- -// promoteSelectionToRoot() -//----------------------------------------------------------------------------- -void LLSelectMgr::promoteSelectionToRoot() -{ - std::set selection_set; - - BOOL selection_changed = FALSE; - - for (LLObjectSelection::iterator iter = getSelection()->begin(); - iter != getSelection()->end(); ) - { - LLObjectSelection::iterator curiter = iter++; - LLSelectNode* nodep = *curiter; - LLViewerObject* object = nodep->getObject(); - - if (nodep->mIndividualSelection) - { - selection_changed = TRUE; - } - - LLViewerObject* parentp = object; - while(parentp->getParent() && !(parentp->isRootEdit() || parentp->isJointChild())) - { - parentp = (LLViewerObject*)parentp->getParent(); - } - - selection_set.insert(parentp); - } - - if (selection_changed) - { - deselectAll(); - - std::set::iterator set_iter; - for (set_iter = selection_set.begin(); set_iter != selection_set.end(); ++set_iter) - { - selectObjectAndFamily(*set_iter); - } - } -} - -//----------------------------------------------------------------------------- -// demoteSelectionToIndividuals() -//----------------------------------------------------------------------------- -void LLSelectMgr::demoteSelectionToIndividuals() -{ - LLDynamicArray objects; - - for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); - iter != getSelection()->root_end(); iter++) - { - LLViewerObject* object = (*iter)->getObject(); - object->addThisAndNonJointChildren(objects); - } - - if (objects.getLength()) - { - deselectAll(); - for (std::vector::iterator iter = objects.begin(); - iter != objects.end(); ++iter) - { - LLViewerObject* objectp = *iter; - selectObjectOnly(objectp); - } - } -} - -//----------------------------------------------------------------------------- -// dump() -//----------------------------------------------------------------------------- -void LLSelectMgr::dump() -{ - llinfos << "Selection Manager: " << mSelectedObjects->getNumNodes() << " items" << llendl; - - llinfos << "TE mode " << mTEMode << llendl; - - S32 count = 0; - for (LLObjectSelection::iterator iter = getSelection()->begin(); - iter != getSelection()->end(); iter++ ) - { - LLViewerObject* objectp = (*iter)->getObject(); - llinfos << "Object " << count << " type " << LLPrimitive::pCodeToString(objectp->getPCode()) << llendl; - llinfos << " hasLSL " << objectp->flagScripted() << llendl; - llinfos << " hasTouch " << objectp->flagHandleTouch() << llendl; - llinfos << " hasMoney " << objectp->flagTakesMoney() << llendl; - llinfos << " getposition " << objectp->getPosition() << llendl; - llinfos << " getpositionAgent " << objectp->getPositionAgent() << llendl; - llinfos << " getpositionRegion " << objectp->getPositionRegion() << llendl; - llinfos << " getpositionGlobal " << objectp->getPositionGlobal() << llendl; - LLDrawable* drawablep = objectp->mDrawable; - llinfos << " " << (drawablep&& drawablep->isVisible() ? "visible" : "invisible") << llendl; - llinfos << " " << (drawablep&& drawablep->isState(LLDrawable::FORCE_INVISIBLE) ? "force_invisible" : "") << llendl; - count++; - } - - // Face iterator - for (LLObjectSelection::iterator iter = getSelection()->begin(); - iter != getSelection()->end(); iter++ ) - { - LLSelectNode* node = *iter; - LLViewerObject* objectp = node->getObject(); - if (!objectp) - continue; - for (S32 te = 0; te < objectp->getNumTEs(); ++te ) - { - if (node->isTESelected(te)) - { - llinfos << "Object " << objectp << " te " << te << llendl; - } - } - } - - llinfos << mHighlightedObjects->getNumNodes() << " objects currently highlighted." << llendl; - - llinfos << "Center global " << mSelectionCenterGlobal << llendl; -} - -//----------------------------------------------------------------------------- -// cleanup() -//----------------------------------------------------------------------------- -void LLSelectMgr::cleanup() -{ - mSilhouetteImagep = NULL; -} - - -//--------------------------------------------------------------------------- -// Manipulate properties of selected objects -//--------------------------------------------------------------------------- - -struct LLSelectMgrSendFunctor : public LLSelectedObjectFunctor -{ - virtual bool apply(LLViewerObject* object) - { - if (object->permModify()) - { - object->sendTEUpdate(); - } - return true; - } -}; - -//----------------------------------------------------------------------------- -// selectionSetImage() -//----------------------------------------------------------------------------- -// *TODO: re-arch texture applying out of lltooldraganddrop -void LLSelectMgr::selectionSetImage(const LLUUID& imageid) -{ - // First for (no copy) textures and multiple object selection - LLViewerInventoryItem* item = gInventory.getItem(imageid); - // fffff - /* - // - if(item - && !item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID()) - && (mSelectedObjects->getNumNodes() > 1) ) - { - llwarns << "Attempted to apply no-copy texture to multiple objects" - << llendl; - return; - } - // - */ - // - - struct f : public LLSelectedTEFunctor - { - LLViewerInventoryItem* mItem; - LLUUID mImageID; - f(LLViewerInventoryItem* item, const LLUUID& id) : mItem(item), mImageID(id) {} - bool apply(LLViewerObject* objectp, S32 te) - { - if (mItem) - { - if (te == -1) // all faces - { - LLToolDragAndDrop::dropTextureAllFaces(objectp, - mItem, - LLToolDragAndDrop::SOURCE_AGENT, - LLUUID::null); - } - else // one face - { - LLToolDragAndDrop::dropTextureOneFace(objectp, - te, - mItem, - LLToolDragAndDrop::SOURCE_AGENT, - LLUUID::null); - } - } - else // not an inventory item - { - // Texture picker defaults aren't inventory items - // * Don't need to worry about permissions for them - // * Can just apply the texture and be done with it. - objectp->setTEImage(te, gImageList.getImage(mImageID, TRUE, FALSE)); - } - return true; - } - } setfunc(item, imageid); - getSelection()->applyToTEs(&setfunc); - - struct g : public LLSelectedObjectFunctor - { - LLViewerInventoryItem* mItem; - g(LLViewerInventoryItem* item) : mItem(item) {} - virtual bool apply(LLViewerObject* object) - { - if (!mItem) - { - object->sendTEUpdate(); - // 1 particle effect per object - // - if(!gSavedSettings.getBOOL("DisablePointAtAndBeam")) - { - // - LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE); - effectp->setSourceObject(gAgent.getAvatarObject()); - effectp->setTargetObject(object); - effectp->setDuration(LL_HUD_DUR_SHORT); - effectp->setColor(LLColor4U(gAgent.getEffectColor())); - // - } - // - } - return true; - } - } sendfunc(item); - getSelection()->applyToObjects(&sendfunc); -} - -//----------------------------------------------------------------------------- -// selectionSetColor() -//----------------------------------------------------------------------------- -void LLSelectMgr::selectionSetColor(const LLColor4 &color) -{ - struct f : public LLSelectedTEFunctor - { - LLColor4 mColor; - f(const LLColor4& c) : mColor(c) {} - bool apply(LLViewerObject* object, S32 te) - { - if (object->permModify()) - { - object->setTEColor(te, mColor); - } - return true; - } - } setfunc(color); - getSelection()->applyToTEs(&setfunc); - - LLSelectMgrSendFunctor sendfunc; - getSelection()->applyToObjects(&sendfunc); -} - -//----------------------------------------------------------------------------- -// selectionSetColorOnly() -//----------------------------------------------------------------------------- -void LLSelectMgr::selectionSetColorOnly(const LLColor4 &color) -{ - struct f : public LLSelectedTEFunctor - { - LLColor4 mColor; - f(const LLColor4& c) : mColor(c) {} - bool apply(LLViewerObject* object, S32 te) - { - if (object->permModify()) - { - LLColor4 prev_color = object->getTE(te)->getColor(); - mColor.mV[VALPHA] = prev_color.mV[VALPHA]; - // update viewer side color in anticipation of update from simulator - object->setTEColor(te, mColor); - } - return true; - } - } setfunc(color); - getSelection()->applyToTEs(&setfunc); - - LLSelectMgrSendFunctor sendfunc; - getSelection()->applyToObjects(&sendfunc); -} - -//----------------------------------------------------------------------------- -// selectionSetAlphaOnly() -//----------------------------------------------------------------------------- -void LLSelectMgr::selectionSetAlphaOnly(const F32 alpha) -{ - struct f : public LLSelectedTEFunctor - { - F32 mAlpha; - f(const F32& a) : mAlpha(a) {} - bool apply(LLViewerObject* object, S32 te) - { - if (object->permModify()) - { - LLColor4 prev_color = object->getTE(te)->getColor(); - prev_color.mV[VALPHA] = mAlpha; - // update viewer side color in anticipation of update from simulator - object->setTEColor(te, prev_color); - } - return true; - } - } setfunc(alpha); - getSelection()->applyToTEs(&setfunc); - - LLSelectMgrSendFunctor sendfunc; - getSelection()->applyToObjects(&sendfunc); -} - -void LLSelectMgr::selectionRevertColors() -{ - struct f : public LLSelectedTEFunctor - { - LLObjectSelectionHandle mSelectedObjects; - f(LLObjectSelectionHandle sel) : mSelectedObjects(sel) {} - bool apply(LLViewerObject* object, S32 te) - { - if (object->permModify()) - { - LLSelectNode* nodep = mSelectedObjects->findNode(object); - if (nodep && te < (S32)nodep->mSavedColors.size()) - { - LLColor4 color = nodep->mSavedColors[te]; - // update viewer side color in anticipation of update from simulator - object->setTEColor(te, color); - } - } - return true; - } - } setfunc(mSelectedObjects); - getSelection()->applyToTEs(&setfunc); - - LLSelectMgrSendFunctor sendfunc; - getSelection()->applyToObjects(&sendfunc); -} - -BOOL LLSelectMgr::selectionRevertTextures() -{ - struct f : public LLSelectedTEFunctor - { - LLObjectSelectionHandle mSelectedObjects; - f(LLObjectSelectionHandle sel) : mSelectedObjects(sel) {} - bool apply(LLViewerObject* object, S32 te) - { - if (object->permModify()) - { - LLSelectNode* nodep = mSelectedObjects->findNode(object); - if (nodep && te < (S32)nodep->mSavedTextures.size()) - { - LLUUID id = nodep->mSavedTextures[te]; - // update textures on viewer side - if (id.isNull()) - { - // this was probably a no-copy texture, leave image as-is - return FALSE; - } - else - { - object->setTEImage(te, gImageList.getImage(id)); - } - } - } - return true; - } - } setfunc(mSelectedObjects); - BOOL revert_successful = getSelection()->applyToTEs(&setfunc); - - LLSelectMgrSendFunctor sendfunc; - getSelection()->applyToObjects(&sendfunc); - - return revert_successful; -} - -void LLSelectMgr::selectionSetBumpmap(U8 bumpmap) -{ - struct f : public LLSelectedTEFunctor - { - U8 mBump; - f(const U8& b) : mBump(b) {} - bool apply(LLViewerObject* object, S32 te) - { - if (object->permModify()) - { - // update viewer side color in anticipation of update from simulator - object->setTEBumpmap(te, mBump); - } - return true; - } - } setfunc(bumpmap); - getSelection()->applyToTEs(&setfunc); - - LLSelectMgrSendFunctor sendfunc; - getSelection()->applyToObjects(&sendfunc); -} - -void LLSelectMgr::selectionSetTexGen(U8 texgen) -{ - struct f : public LLSelectedTEFunctor - { - U8 mTexgen; - f(const U8& t) : mTexgen(t) {} - bool apply(LLViewerObject* object, S32 te) - { - if (object->permModify()) - { - // update viewer side color in anticipation of update from simulator - object->setTETexGen(te, mTexgen); - } - return true; - } - } setfunc(texgen); - getSelection()->applyToTEs(&setfunc); - - LLSelectMgrSendFunctor sendfunc; - getSelection()->applyToObjects(&sendfunc); -} - - -void LLSelectMgr::selectionSetShiny(U8 shiny) -{ - struct f : public LLSelectedTEFunctor - { - U8 mShiny; - f(const U8& t) : mShiny(t) {} - bool apply(LLViewerObject* object, S32 te) - { - if (object->permModify()) - { - // update viewer side color in anticipation of update from simulator - object->setTEShiny(te, mShiny); - } - return true; - } - } setfunc(shiny); - getSelection()->applyToTEs(&setfunc); - - LLSelectMgrSendFunctor sendfunc; - getSelection()->applyToObjects(&sendfunc); -} - -void LLSelectMgr::selectionSetFullbright(U8 fullbright) -{ - struct f : public LLSelectedTEFunctor - { - U8 mFullbright; - f(const U8& t) : mFullbright(t) {} - bool apply(LLViewerObject* object, S32 te) - { - if (object->permModify()) - { - // update viewer side color in anticipation of update from simulator - object->setTEFullbright(te, mFullbright); - } - return true; - } - } setfunc(fullbright); - getSelection()->applyToTEs(&setfunc); - - struct g : public LLSelectedObjectFunctor - { - U8 mFullbright; - g(const U8& t) : mFullbright(t) {} - virtual bool apply(LLViewerObject* object) - { - if (object->permModify()) - { - object->sendTEUpdate(); - if (mFullbright) - { - U8 material = object->getMaterial(); - U8 mcode = material & LL_MCODE_MASK; - if (mcode == LL_MCODE_LIGHT) - { - mcode = LL_MCODE_GLASS; - material = (material & ~LL_MCODE_MASK) | mcode; - object->setMaterial(material); - object->sendMaterialUpdate(); - } - } - } - return true; - } - } sendfunc(fullbright); - getSelection()->applyToObjects(&sendfunc); -} - -void LLSelectMgr::selectionSetMediaTypeAndURL(U8 media_type, const std::string& media_url) -{ - U8 media_flags = LLTextureEntry::MF_NONE; - if (media_type == LLViewerObject::MEDIA_TYPE_WEB_PAGE) - { - media_flags = LLTextureEntry::MF_WEB_PAGE; - } - - struct f : public LLSelectedTEFunctor - { - U8 mMediaFlags; - f(const U8& t) : mMediaFlags(t) {} - bool apply(LLViewerObject* object, S32 te) - { - if (object->permModify()) - { - // update viewer side color in anticipation of update from simulator - object->setTEMediaFlags(te, mMediaFlags); - } - return true; - } - } setfunc(media_flags); - getSelection()->applyToTEs(&setfunc); - - struct g : public LLSelectedObjectFunctor - { - U8 media_type; - const std::string& media_url ; - g(U8 a, const std::string& b) : media_type(a), media_url(b) {} - virtual bool apply(LLViewerObject* object) - { - if (object->permModify()) - { - object->sendTEUpdate(); - object->setMediaType(media_type); - object->setMediaURL(media_url); - } - return true; - } - } sendfunc(media_type, media_url); - getSelection()->applyToObjects(&sendfunc); -} - -void LLSelectMgr::selectionSetGlow(F32 glow) -{ - struct f1 : public LLSelectedTEFunctor - { - F32 mGlow; - f1(F32 glow) : mGlow(glow) {}; - bool apply(LLViewerObject* object, S32 face) - { - if (object->permModify()) - { - // update viewer side color in anticipation of update from simulator - object->setTEGlow(face, mGlow); - } - return true; - } - } func1(glow); - mSelectedObjects->applyToTEs( &func1 ); - - struct f2 : public LLSelectedObjectFunctor - { - virtual bool apply(LLViewerObject* object) - { - if (object->permModify()) - { - object->sendTEUpdate(); - } - return true; - } - } func2; - mSelectedObjects->applyToObjects( &func2 ); -} - - -//----------------------------------------------------------------------------- -// findObjectPermissions() -//----------------------------------------------------------------------------- -LLPermissions* LLSelectMgr::findObjectPermissions(const LLViewerObject* object) -{ - for (LLObjectSelection::valid_iterator iter = getSelection()->valid_begin(); - iter != getSelection()->valid_end(); iter++ ) - { - LLSelectNode* nodep = *iter; - if (nodep->getObject() == object) - { - return nodep->mPermissions; - } - } - - return NULL; -} - - -//----------------------------------------------------------------------------- -// selectionGetGlow() -//----------------------------------------------------------------------------- -BOOL LLSelectMgr::selectionGetGlow(F32 *glow) -{ - BOOL identical; - F32 lglow = 0.f; - struct f1 : public LLSelectedTEGetFunctor - { - F32 get(LLViewerObject* object, S32 face) - { - return object->getTE(face)->getGlow(); - } - } func; - identical = mSelectedObjects->getSelectedTEValue( &func, lglow ); - - *glow = lglow; - return identical; -} - -//----------------------------------------------------------------------------- -// selectionSetMaterial() -//----------------------------------------------------------------------------- -void LLSelectMgr::selectionSetMaterial(U8 material) -{ - struct f : public LLSelectedObjectFunctor - { - U8 mMaterial; - f(const U8& t) : mMaterial(t) {} - virtual bool apply(LLViewerObject* object) - { - if (object->permModify()) - { - U8 cur_material = object->getMaterial(); - U8 material = mMaterial | (cur_material & ~LL_MCODE_MASK); - object->setMaterial(material); - object->sendMaterialUpdate(); - } - return true; - } - } sendfunc(material); - getSelection()->applyToObjects(&sendfunc); -} - -// TRUE if all selected objects have this PCode -BOOL LLSelectMgr::selectionAllPCode(LLPCode code) -{ - struct f : public LLSelectedObjectFunctor - { - LLPCode mCode; - f(const LLPCode& t) : mCode(t) {} - virtual bool apply(LLViewerObject* object) - { - if (object->getPCode() != mCode) - { - return FALSE; - } - return true; - } - } func(code); - BOOL res = getSelection()->applyToObjects(&func); - return res; -} - -bool LLSelectMgr::selectionGetIncludeInSearch(bool* include_in_search_out) -{ - LLViewerObject *object = mSelectedObjects->getFirstRootObject(); - if (!object) return FALSE; - - bool include_in_search = object->getIncludeInSearch(); - - bool identical = true; - - for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); - iter != getSelection()->root_end(); iter++) - { - LLViewerObject* object = (*iter)->getObject(); - - if ( include_in_search != object->getIncludeInSearch()) - { - identical = false; - break; - } - } - - *include_in_search_out = include_in_search; - return identical; -} - -void LLSelectMgr::selectionSetIncludeInSearch(bool include_in_search) -{ - LLViewerObject* object = NULL; - for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); - iter != getSelection()->root_end(); iter++) - { - object = (*iter)->getObject(); - object->setIncludeInSearch(include_in_search); - } - sendListToRegions( - "ObjectIncludeInSearch", - packAgentAndSessionID, - packObjectIncludeInSearch, - &include_in_search, - SEND_ONLY_ROOTS); -} - -BOOL LLSelectMgr::selectionGetClickAction(U8 *out_action) -{ - LLViewerObject *object = mSelectedObjects->getFirstObject(); - if (!object) - { - return FALSE; - } - - U8 action = object->getClickAction(); - *out_action = action; - - struct f : public LLSelectedObjectFunctor - { - U8 mAction; - f(const U8& t) : mAction(t) {} - virtual bool apply(LLViewerObject* object) - { - if ( mAction != object->getClickAction()) - { - return false; - } - return true; - } - } func(action); - BOOL res = getSelection()->applyToObjects(&func); - return res; -} - -void LLSelectMgr::selectionSetClickAction(U8 action) -{ - struct f : public LLSelectedObjectFunctor - { - U8 mAction; - f(const U8& t) : mAction(t) {} - virtual bool apply(LLViewerObject* object) - { - object->setClickAction(mAction); - return true; - } - } func(action); - getSelection()->applyToObjects(&func); - - sendListToRegions("ObjectClickAction", - packAgentAndSessionID, - packObjectClickAction, - &action, - SEND_INDIVIDUALS); -} - - -//----------------------------------------------------------------------------- -// godlike requests -//----------------------------------------------------------------------------- - -typedef std::pair godlike_request_t; - -void LLSelectMgr::sendGodlikeRequest(const std::string& request, const std::string& param) -{ - // If the agent is neither godlike nor an estate owner, the server - // will reject the request. - std::string message_type; - if (gAgent.isGodlike()) - { - message_type = "GodlikeMessage"; - } - else - { - message_type = "EstateOwnerMessage"; - } - - godlike_request_t data(request, param); - if(!mSelectedObjects->getRootObjectCount()) - { - LLMessageSystem* msg = gMessageSystem; - msg->newMessage(message_type.c_str()); - LLSelectMgr::packGodlikeHead(&data); - gAgent.sendReliableMessage(); - } - else - { - sendListToRegions(message_type, packGodlikeHead, packObjectIDAsParam, &data, SEND_ONLY_ROOTS); - } -} - -void LLSelectMgr::packGodlikeHead(void* user_data) -{ - LLMessageSystem* msg = gMessageSystem; - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->addUUID("TransactionID", LLUUID::null); - godlike_request_t* data = (godlike_request_t*)user_data; - msg->nextBlock("MethodData"); - msg->addString("Method", data->first); - msg->addUUID("Invoice", LLUUID::null); - - // The parameters used to be restricted to either string or - // integer. This mimics that behavior under the new 'string-only' - // parameter list by not packing a string if there wasn't one - // specified. The object ids will be packed in the - // packObjectIDAsParam() method. - if(data->second.size() > 0) - { - msg->nextBlock("ParamList"); - msg->addString("Parameter", data->second); - } -} - -// static -void LLSelectMgr::packObjectIDAsParam(LLSelectNode* node, void *) -{ - std::string buf = llformat("%u", node->getObject()->getLocalID()); - gMessageSystem->nextBlock("ParamList"); - gMessageSystem->addString("Parameter", buf); -} - -//----------------------------------------------------------------------------- -// Rotation options -//----------------------------------------------------------------------------- -void LLSelectMgr::selectionResetRotation() -{ - struct f : public LLSelectedObjectFunctor - { - virtual bool apply(LLViewerObject* object) - { - LLQuaternion identity(0.f, 0.f, 0.f, 1.f); - object->setRotation(identity); - if (object->mDrawable.notNull()) - { - gPipeline.markMoved(object->mDrawable, TRUE); - } - object->sendRotationUpdate(); - return true; - } - } func; - getSelection()->applyToRootObjects(&func); -} - -void LLSelectMgr::selectionRotateAroundZ(F32 degrees) -{ - LLQuaternion rot( degrees * DEG_TO_RAD, LLVector3(0,0,1) ); - struct f : public LLSelectedObjectFunctor - { - LLQuaternion mRot; - f(const LLQuaternion& rot) : mRot(rot) {} - virtual bool apply(LLViewerObject* object) - { - object->setRotation( object->getRotationEdit() * mRot ); - if (object->mDrawable.notNull()) - { - gPipeline.markMoved(object->mDrawable, TRUE); - } - object->sendRotationUpdate(); - return true; - } - } func(rot); - getSelection()->applyToRootObjects(&func); -} - - -//----------------------------------------------------------------------------- -// selectionTexScaleAutofit() -//----------------------------------------------------------------------------- -void LLSelectMgr::selectionTexScaleAutofit(F32 repeats_per_meter) -{ - struct f : public LLSelectedTEFunctor - { - F32 mRepeatsPerMeter; - f(const F32& t) : mRepeatsPerMeter(t) {} - bool apply(LLViewerObject* object, S32 te) - { - - if (object->permModify()) - { - // Compute S,T to axis mapping - U32 s_axis, t_axis; - if (!LLPrimitive::getTESTAxes(te, &s_axis, &t_axis)) - { - return TRUE; - } - - F32 new_s = object->getScale().mV[s_axis] * mRepeatsPerMeter; - F32 new_t = object->getScale().mV[t_axis] * mRepeatsPerMeter; - - object->setTEScale(te, new_s, new_t); - } - return true; - } - } setfunc(repeats_per_meter); - getSelection()->applyToTEs(&setfunc); - - LLSelectMgrSendFunctor sendfunc; - getSelection()->applyToObjects(&sendfunc); -} - - - -// Called at the end of a scale operation, this adjusts the textures to attempt to -// maintain a constant repeats per meter. -// BUG: Only works for flex boxes. -//----------------------------------------------------------------------------- -// adjustTexturesByScale() -//----------------------------------------------------------------------------- -void LLSelectMgr::adjustTexturesByScale(BOOL send_to_sim, BOOL stretch) -{ - for (LLObjectSelection::iterator iter = getSelection()->begin(); - iter != getSelection()->end(); iter++) - { - LLSelectNode* selectNode = *iter; - LLViewerObject* object = selectNode->getObject(); - - if (!object) - { - continue; - } - - if (!object->permModify()) - { - continue; - } - - if (object->getNumTEs() == 0) - { - continue; - } - - BOOL send = FALSE; - - for (U8 te_num = 0; te_num < object->getNumTEs(); te_num++) - { - const LLTextureEntry* tep = object->getTE(te_num); - - BOOL planar = tep->getTexGen() == LLTextureEntry::TEX_GEN_PLANAR; - if (planar == stretch) - { - // Figure out how S,T changed with scale operation - U32 s_axis, t_axis; - if (!LLPrimitive::getTESTAxes(te_num, &s_axis, &t_axis)) - { - continue; - } - - LLVector3 scale_ratio = selectNode->mTextureScaleRatios[te_num]; - LLVector3 object_scale = object->getScale(); - - // Apply new scale to face - if (planar) - { - object->setTEScale(te_num, 1.f/object_scale.mV[s_axis]*scale_ratio.mV[s_axis], - 1.f/object_scale.mV[t_axis]*scale_ratio.mV[t_axis]); - } - else - { - object->setTEScale(te_num, scale_ratio.mV[s_axis]*object_scale.mV[s_axis], - scale_ratio.mV[t_axis]*object_scale.mV[t_axis]); - } - send = send_to_sim; - } - } - - if (send) - { - object->sendTEUpdate(); - } - } -} - -//----------------------------------------------------------------------------- -// selectGetAllRootsValid() -// Returns TRUE if the viewer has information on all selected objects -//----------------------------------------------------------------------------- -BOOL LLSelectMgr::selectGetAllRootsValid() -{ - for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); - iter != getSelection()->root_end(); ++iter ) - { - LLSelectNode* node = *iter; - if( !node->mValid ) - { - return FALSE; - } - } - return TRUE; -} - - -//----------------------------------------------------------------------------- -// selectGetAllValid() -// Returns TRUE if the viewer has information on all selected objects -//----------------------------------------------------------------------------- -BOOL LLSelectMgr::selectGetAllValid() -{ - for (LLObjectSelection::iterator iter = getSelection()->begin(); - iter != getSelection()->end(); ++iter ) - { - LLSelectNode* node = *iter; - if( !node->mValid ) - { - return FALSE; - } - } - return TRUE; -} - - -//----------------------------------------------------------------------------- -// selectGetModify() - return TRUE if current agent can modify all -// selected objects. -//----------------------------------------------------------------------------- -BOOL LLSelectMgr::selectGetModify() -{ - for (LLObjectSelection::iterator iter = getSelection()->begin(); - iter != getSelection()->end(); iter++ ) - { - LLSelectNode* node = *iter; - LLViewerObject* object = node->getObject(); - if( !object || !node->mValid ) - { - return FALSE; - } - if( !object->permModify() ) - { - return FALSE; - } - } - return TRUE; -} - -//----------------------------------------------------------------------------- -// selectGetRootsModify() - return TRUE if current agent can modify all -// selected root objects. -//----------------------------------------------------------------------------- -BOOL LLSelectMgr::selectGetRootsModify() -{ - for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); - iter != getSelection()->root_end(); iter++ ) - { - LLSelectNode* node = *iter; - LLViewerObject* object = node->getObject(); - if( !node->mValid ) - { - return FALSE; - } - if( !object->permModify() ) - { - return FALSE; - } - } - - return TRUE; -} - - -//----------------------------------------------------------------------------- -// selectGetRootsTransfer() - return TRUE if current agent can transfer all -// selected root objects. -//----------------------------------------------------------------------------- -BOOL LLSelectMgr::selectGetRootsTransfer() -{ - for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); - iter != getSelection()->root_end(); iter++ ) - { - LLSelectNode* node = *iter; - LLViewerObject* object = node->getObject(); - if( !node->mValid ) - { - return FALSE; - } - if(!object->permTransfer()) - { - return FALSE; - } - } - return TRUE; -} - -//----------------------------------------------------------------------------- -// selectGetRootsCopy() - return TRUE if current agent can copy all -// selected root objects. -//----------------------------------------------------------------------------- -BOOL LLSelectMgr::selectGetRootsCopy() -{ - for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); - iter != getSelection()->root_end(); iter++ ) - { - LLSelectNode* node = *iter; - LLViewerObject* object = node->getObject(); - if( !node->mValid ) - { - return FALSE; - } - if(!object->permCopy()) - { - return FALSE; - } - } - return TRUE; -} - -//----------------------------------------------------------------------------- -// selectGetCreator() -// Creator information only applies to root objects. -//----------------------------------------------------------------------------- -BOOL LLSelectMgr::selectGetCreator(LLUUID& result_id, std::string& name) -{ - BOOL identical = TRUE; - BOOL first = TRUE; - LLUUID first_id; - for (LLObjectSelection::root_object_iterator iter = getSelection()->root_object_begin(); - iter != getSelection()->root_object_end(); iter++ ) - { - LLSelectNode* node = *iter; - if (!node->mValid) - { - return FALSE; - } - - if (first) - { - first_id = node->mPermissions->getCreator(); - first = FALSE; - } - else - { - if ( !(first_id == node->mPermissions->getCreator() ) ) - { - identical = FALSE; - break; - } - } - } - if (first_id.isNull()) - { - return FALSE; - } - - result_id = first_id; - - if (identical) - { - gCacheName->getFullName(first_id, name); - } - else - { - name.assign( "(multiple)" ); - } - - return identical; -} - - -//----------------------------------------------------------------------------- -// selectGetOwner() -// Owner information only applies to roots. -//----------------------------------------------------------------------------- -BOOL LLSelectMgr::selectGetOwner(LLUUID& result_id, std::string& name) -{ - BOOL identical = TRUE; - BOOL first = TRUE; - BOOL first_group_owned = FALSE; - LLUUID first_id; - for (LLObjectSelection::root_object_iterator iter = getSelection()->root_object_begin(); - iter != getSelection()->root_object_end(); iter++ ) - { - LLSelectNode* node = *iter; - if (!node->mValid) - { - return FALSE; - } - - if (first) - { - node->mPermissions->getOwnership(first_id, first_group_owned); - first = FALSE; - } - else - { - LLUUID owner_id; - BOOL is_group_owned = FALSE; - if (!(node->mPermissions->getOwnership(owner_id, is_group_owned)) - || owner_id != first_id || is_group_owned != first_group_owned) - { - identical = FALSE; - break; - } - } - } - if (first_id.isNull()) - { - return FALSE; - } - - result_id = first_id; - - if (identical) - { - BOOL public_owner = (first_id.isNull() && !first_group_owned); - if (first_group_owned) - { - name.assign( "(Group Owned)"); - } - else if(!public_owner) - { - gCacheName->getFullName(first_id, name); - } - else - { - name.assign("Public"); - } - } - else - { - name.assign( "(multiple)" ); - } - - return identical; -} - - -//----------------------------------------------------------------------------- -// selectGetLastOwner() -// Owner information only applies to roots. -//----------------------------------------------------------------------------- -BOOL LLSelectMgr::selectGetLastOwner(LLUUID& result_id, std::string& name) -{ - BOOL identical = TRUE; - BOOL first = TRUE; - LLUUID first_id; - for (LLObjectSelection::root_object_iterator iter = getSelection()->root_object_begin(); - iter != getSelection()->root_object_end(); iter++ ) - { - LLSelectNode* node = *iter; - if (!node->mValid) - { - return FALSE; - } - - if (first) - { - first_id = node->mPermissions->getLastOwner(); - first = FALSE; - } - else - { - if ( !(first_id == node->mPermissions->getLastOwner() ) ) - { - identical = FALSE; - break; - } - } - } - if (first_id.isNull()) - { - return FALSE; - } - - result_id = first_id; - - if (identical) - { - BOOL public_owner = (first_id.isNull()); - if(!public_owner) - { - gCacheName->getFullName(first_id, name); - } - else - { - name.assign("Public or Group"); - } - } - else - { - name.assign( "" ); - } - - return identical; -} - - -//----------------------------------------------------------------------------- -// selectGetGroup() -// Group information only applies to roots. -//----------------------------------------------------------------------------- -BOOL LLSelectMgr::selectGetGroup(LLUUID& result_id) -{ - BOOL identical = TRUE; - BOOL first = TRUE; - LLUUID first_id; - for (LLObjectSelection::root_object_iterator iter = getSelection()->root_object_begin(); - iter != getSelection()->root_object_end(); iter++ ) - { - LLSelectNode* node = *iter; - if (!node->mValid) - { - return FALSE; - } - - if (first) - { - first_id = node->mPermissions->getGroup(); - first = FALSE; - } - else - { - if ( !(first_id == node->mPermissions->getGroup() ) ) - { - identical = FALSE; - break; - } - } - } - - result_id = first_id; - - return identical; -} - -//----------------------------------------------------------------------------- -// selectIsGroupOwned() -// Only operates on root nodes. -// Returns TRUE if all have valid data and they are all group owned. -//----------------------------------------------------------------------------- -BOOL LLSelectMgr::selectIsGroupOwned() -{ - BOOL found_one = FALSE; - for (LLObjectSelection::root_object_iterator iter = getSelection()->root_object_begin(); - iter != getSelection()->root_object_end(); iter++ ) - { - LLSelectNode* node = *iter; - if (!node->mValid) - { - return FALSE; - } - found_one = TRUE; - if (!node->mPermissions->isGroupOwned()) - { - return FALSE; - } - } - return found_one ? TRUE : FALSE; -} - -//----------------------------------------------------------------------------- -// selectGetPerm() -// Only operates on root nodes. -// Returns TRUE if all have valid data. -// mask_on has bits set to TRUE where all permissions are TRUE -// mask_off has bits set to TRUE where all permissions are FALSE -// if a bit is off both in mask_on and mask_off, the values differ within -// the selection. -//----------------------------------------------------------------------------- -BOOL LLSelectMgr::selectGetPerm(U8 which_perm, U32* mask_on, U32* mask_off) -{ - U32 mask; - U32 mask_and = 0xffffffff; - U32 mask_or = 0x00000000; - BOOL all_valid = FALSE; - - for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); - iter != getSelection()->root_end(); iter++) - { - LLSelectNode* node = *iter; - - if (!node->mValid) - { - all_valid = FALSE; - break; - } - - all_valid = TRUE; - - switch( which_perm ) - { - case PERM_BASE: - mask = node->mPermissions->getMaskBase(); - break; - case PERM_OWNER: - mask = node->mPermissions->getMaskOwner(); - break; - case PERM_GROUP: - mask = node->mPermissions->getMaskGroup(); - break; - case PERM_EVERYONE: - mask = node->mPermissions->getMaskEveryone(); - break; - case PERM_NEXT_OWNER: - mask = node->mPermissions->getMaskNextOwner(); - break; - default: - mask = 0x0; - break; - } - mask_and &= mask; - mask_or |= mask; - } - - if (all_valid) - { - // ...TRUE through all ANDs means all TRUE - *mask_on = mask_and; - - // ...FALSE through all ORs means all FALSE - *mask_off = ~mask_or; - return TRUE; - } - else - { - *mask_on = 0; - *mask_off = 0; - return FALSE; - } -} - - - -BOOL LLSelectMgr::selectGetOwnershipCost(S32* out_cost) -{ - return mSelectedObjects->getOwnershipCost(*out_cost); -} - -BOOL LLSelectMgr::selectGetPermissions(LLPermissions& result_perm) -{ - BOOL first = TRUE; - LLPermissions perm; - for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); - iter != getSelection()->root_end(); iter++ ) - { - LLSelectNode* node = *iter; - if (!node->mValid) - { - return FALSE; - } - - if (first) - { - perm = *(node->mPermissions); - first = FALSE; - } - else - { - perm.accumulate(*(node->mPermissions)); - } - } - - result_perm = perm; - - return TRUE; -} - - -void LLSelectMgr::selectDelete() -{ - S32 deleteable_count = 0; - - BOOL locked_but_deleteable_object = FALSE; - BOOL no_copy_but_deleteable_object = FALSE; - BOOL all_owned_by_you = TRUE; - - for (LLObjectSelection::iterator iter = getSelection()->begin(); - iter != getSelection()->end(); iter++) - { - LLViewerObject* obj = (*iter)->getObject(); - - if( obj->isAttachment() ) - { - continue; - } - - deleteable_count++; - - // Check to see if you can delete objects which are locked. - if(!obj->permMove()) - { - locked_but_deleteable_object = TRUE; - } - if(!obj->permCopy()) - { - no_copy_but_deleteable_object = TRUE; - } - if(!obj->permYouOwner()) - { - all_owned_by_you = FALSE; - } - } - - if( 0 == deleteable_count ) - { - make_ui_sound("UISndInvalidOp"); - return; - } - - LLNotification::Params params("ConfirmObjectDeleteLock"); - params.functor(boost::bind(&LLSelectMgr::confirmDelete, _1, _2, getSelection())); - - if(locked_but_deleteable_object || - no_copy_but_deleteable_object || - !all_owned_by_you) - { - // convert any transient pie-menu selections to full selection so this operation - // has some context - // NOTE: if user cancels delete operation, this will potentially leave objects selected outside of build mode - // but this is ok, if not ideal - convertTransient(); - - //This is messy, but needed to get all english our of the UI. - if(locked_but_deleteable_object && !no_copy_but_deleteable_object && all_owned_by_you) - { - //Locked only - params.name("ConfirmObjectDeleteLock"); - } - else if(!locked_but_deleteable_object && no_copy_but_deleteable_object && all_owned_by_you) - { - //No Copy only - params.name("ConfirmObjectDeleteNoCopy"); - } - else if(!locked_but_deleteable_object && !no_copy_but_deleteable_object && !all_owned_by_you) - { - //not owned only - params.name("ConfirmObjectDeleteNoOwn"); - } - else if(locked_but_deleteable_object && no_copy_but_deleteable_object && all_owned_by_you) - { - //locked and no copy - params.name("ConfirmObjectDeleteLockNoCopy"); - } - else if(locked_but_deleteable_object && !no_copy_but_deleteable_object && !all_owned_by_you) - { - //locked and not owned - params.name("ConfirmObjectDeleteLockNoOwn"); - } - else if(!locked_but_deleteable_object && no_copy_but_deleteable_object && !all_owned_by_you) - { - //no copy and not owned - params.name("ConfirmObjectDeleteNoCopyNoOwn"); - } - else - { - //locked, no copy and not owned - params.name("ConfirmObjectDeleteLockNoCopyNoOwn"); - } - - LLNotifications::instance().add(params); - } - else - { - LLNotifications::instance().forceResponse(params, 0); - } -} - -// static -bool LLSelectMgr::confirmDelete(const LLSD& notification, const LLSD& response, LLObjectSelectionHandle handle) -{ - S32 option = LLNotification::getSelectedOption(notification, response); - if (!handle->getObjectCount()) - { - llwarns << "Nothing to delete!" << llendl; - return false; - } - - switch(option) - { - case 0: - { - // TODO: Make sure you have delete permissions on all of them. - LLUUID trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH); - // attempt to derez into the trash. - LLDeRezInfo* info = new LLDeRezInfo(DRD_TRASH, trash_id); - LLSelectMgr::getInstance()->sendListToRegions("DeRezObject", - packDeRezHeader, - packObjectLocalID, - (void*)info, - SEND_ONLY_ROOTS); - // VEFFECT: Delete Object - one effect for all deletes - if(!gSavedSettings.getBOOL("DisablePointAtAndBeam")) - { - if (LLSelectMgr::getInstance()->mSelectedObjects->mSelectType != SELECT_TYPE_HUD) - { - LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); - effectp->setPositionGlobal( LLSelectMgr::getInstance()->getSelectionCenterGlobal() ); - effectp->setColor(LLColor4U(gAgent.getEffectColor())); - F32 duration = 0.5f; - duration += LLSelectMgr::getInstance()->mSelectedObjects->getObjectCount() / 64.f; - effectp->setDuration(duration); - } - } - - gAgent.setLookAt(LOOKAT_TARGET_CLEAR); - - // Keep track of how many objects have been deleted. - F64 obj_delete_count = LLViewerStats::getInstance()->getStat(LLViewerStats::ST_OBJECT_DELETE_COUNT); - obj_delete_count += LLSelectMgr::getInstance()->mSelectedObjects->getObjectCount(); - LLViewerStats::getInstance()->setStat(LLViewerStats::ST_OBJECT_DELETE_COUNT, obj_delete_count ); - } - break; - case 1: - default: - break; - } - return false; -} - - -void LLSelectMgr::selectForceDelete() -{ - sendListToRegions( - "ObjectDelete", - packDeleteHeader, - packObjectLocalID, - (void*)TRUE, - SEND_ONLY_ROOTS); -} - -void LLSelectMgr::selectGetAggregateSaleInfo(U32 &num_for_sale, - BOOL &is_for_sale_mixed, - BOOL &is_sale_price_mixed, - S32 &total_sale_price, - S32 &individual_sale_price) -{ - num_for_sale = 0; - is_for_sale_mixed = FALSE; - is_sale_price_mixed = FALSE; - total_sale_price = 0; - individual_sale_price = 0; - - - // Empty set. - if (getSelection()->root_begin() == getSelection()->root_end()) - return; - - LLSelectNode *node = *(getSelection()->root_begin()); - const BOOL first_node_for_sale = node->mSaleInfo.isForSale(); - const S32 first_node_sale_price = node->mSaleInfo.getSalePrice(); - - for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); - iter != getSelection()->root_end(); iter++) - { - LLSelectNode* node = *iter; - const BOOL node_for_sale = node->mSaleInfo.isForSale(); - const S32 node_sale_price = node->mSaleInfo.getSalePrice(); - - // Set mixed if the fields don't match the first node's fields. - if (node_for_sale != first_node_for_sale) - is_for_sale_mixed = TRUE; - if (node_sale_price != first_node_sale_price) - is_sale_price_mixed = TRUE; - - if (node_for_sale) - { - total_sale_price += node_sale_price; - num_for_sale ++; - } - } - - individual_sale_price = first_node_sale_price; - if (is_for_sale_mixed) - { - is_sale_price_mixed = TRUE; - individual_sale_price = 0; - } -} - -// returns TRUE if all nodes are valid. method also stores an -// accumulated sale info. -BOOL LLSelectMgr::selectGetSaleInfo(LLSaleInfo& result_sale_info) -{ - BOOL first = TRUE; - LLSaleInfo sale_info; - for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); - iter != getSelection()->root_end(); iter++ ) - { - LLSelectNode* node = *iter; - if (!node->mValid) - { - return FALSE; - } - - if (first) - { - sale_info = node->mSaleInfo; - first = FALSE; - } - else - { - sale_info.accumulate(node->mSaleInfo); - } - } - - result_sale_info = sale_info; - - return TRUE; -} - -BOOL LLSelectMgr::selectGetAggregatePermissions(LLAggregatePermissions& result_perm) -{ - BOOL first = TRUE; - LLAggregatePermissions perm; - for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); - iter != getSelection()->root_end(); iter++ ) - { - LLSelectNode* node = *iter; - if (!node->mValid) - { - return FALSE; - } - - if (first) - { - perm = node->mAggregatePerm; - first = FALSE; - } - else - { - perm.aggregate(node->mAggregatePerm); - } - } - - result_perm = perm; - - return TRUE; -} - -BOOL LLSelectMgr::selectGetAggregateTexturePermissions(LLAggregatePermissions& result_perm) -{ - BOOL first = TRUE; - LLAggregatePermissions perm; - for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); - iter != getSelection()->root_end(); iter++ ) - { - LLSelectNode* node = *iter; - if (!node->mValid) - { - return FALSE; - } - - LLAggregatePermissions t_perm = node->getObject()->permYouOwner() ? node->mAggregateTexturePermOwner : node->mAggregateTexturePerm; - if (first) - { - perm = t_perm; - first = FALSE; - } - else - { - perm.aggregate(t_perm); - } - } - - result_perm = perm; - - return TRUE; -} - - -//-------------------------------------------------------------------- -// Duplicate objects -//-------------------------------------------------------------------- - -// JC - If this doesn't work right, duplicate the selection list -// before doing anything, do a deselect, then send the duplicate -// messages. -struct LLDuplicateData -{ - LLVector3 offset; - U32 flags; -}; - -void LLSelectMgr::selectDuplicate(const LLVector3& offset, BOOL select_copy) -{ - if (mSelectedObjects->isAttachment()) - { - //RN: do not duplicate attachments - make_ui_sound("UISndInvalidOp"); - return; - } - LLDuplicateData data; - - data.offset = offset; - data.flags = (select_copy ? FLAGS_CREATE_SELECTED : 0x0); - - sendListToRegions("ObjectDuplicate", packDuplicateHeader, packDuplicate, &data, SEND_ONLY_ROOTS); - - if (select_copy) - { - // the new copy will be coming in selected - deselectAll(); - } - else - { - for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); - iter != getSelection()->root_end(); iter++ ) - { - LLSelectNode* node = *iter; - node->mDuplicated = TRUE; - node->mDuplicatePos = node->getObject()->getPositionGlobal(); - node->mDuplicateRot = node->getObject()->getRotation(); - } - } -} - -void LLSelectMgr::repeatDuplicate() -{ - if (mSelectedObjects->isAttachment()) - { - //RN: do not duplicate attachments - make_ui_sound("UISndInvalidOp"); - return; - } - - std::vector non_duplicated_objects; - - for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); - iter != getSelection()->root_end(); iter++ ) - { - LLSelectNode* node = *iter; - if (!node->mDuplicated) - { - non_duplicated_objects.push_back(node->getObject()); - } - } - - // make sure only previously duplicated objects are selected - for (std::vector::iterator iter = non_duplicated_objects.begin(); - iter != non_duplicated_objects.end(); ++iter) - { - LLViewerObject* objectp = *iter; - deselectObjectAndFamily(objectp); - } - - // duplicate objects in place - LLDuplicateData data; - - data.offset = LLVector3::zero; - data.flags = 0x0; - - sendListToRegions("ObjectDuplicate", packDuplicateHeader, packDuplicate, &data, SEND_ONLY_ROOTS); - - // move current selection based on delta from duplication position and update duplication position - for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); - iter != getSelection()->root_end(); iter++ ) - { - LLSelectNode* node = *iter; - if (node->mDuplicated) - { - LLQuaternion cur_rot = node->getObject()->getRotation(); - LLQuaternion rot_delta = (~node->mDuplicateRot * cur_rot); - LLQuaternion new_rot = cur_rot * rot_delta; - LLVector3d cur_pos = node->getObject()->getPositionGlobal(); - LLVector3d new_pos = cur_pos + ((cur_pos - node->mDuplicatePos) * rot_delta); - - node->mDuplicatePos = node->getObject()->getPositionGlobal(); - node->mDuplicateRot = node->getObject()->getRotation(); - node->getObject()->setPositionGlobal(new_pos); - node->getObject()->setRotation(new_rot); - } - } - - sendMultipleUpdate(UPD_ROTATION | UPD_POSITION); -} - -// static -void LLSelectMgr::packDuplicate( LLSelectNode* node, void *duplicate_data ) -{ - gMessageSystem->nextBlockFast(_PREHASH_ObjectData); - gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID()); -} - - -//-------------------------------------------------------------------- -// Duplicate On Ray -//-------------------------------------------------------------------- - -// Duplicates the selected objects, but places the copy along a cast -// ray. -struct LLDuplicateOnRayData -{ - LLVector3 mRayStartRegion; - LLVector3 mRayEndRegion; - BOOL mBypassRaycast; - BOOL mRayEndIsIntersection; - LLUUID mRayTargetID; - BOOL mCopyCenters; - BOOL mCopyRotates; - U32 mFlags; -}; - -void LLSelectMgr::selectDuplicateOnRay(const LLVector3 &ray_start_region, - const LLVector3 &ray_end_region, - BOOL bypass_raycast, - BOOL ray_end_is_intersection, - const LLUUID &ray_target_id, - BOOL copy_centers, - BOOL copy_rotates, - BOOL select_copy) -{ - if (mSelectedObjects->isAttachment()) - { - // do not duplicate attachments - make_ui_sound("UISndInvalidOp"); - return; - } - - LLDuplicateOnRayData data; - - data.mRayStartRegion = ray_start_region; - data.mRayEndRegion = ray_end_region; - data.mBypassRaycast = bypass_raycast; - data.mRayEndIsIntersection = ray_end_is_intersection; - data.mRayTargetID = ray_target_id; - data.mCopyCenters = copy_centers; - data.mCopyRotates = copy_rotates; - data.mFlags = (select_copy ? FLAGS_CREATE_SELECTED : 0x0); - - sendListToRegions("ObjectDuplicateOnRay", - packDuplicateOnRayHead, packObjectLocalID, &data, SEND_ONLY_ROOTS); - - if (select_copy) - { - // the new copy will be coming in selected - deselectAll(); - } -} - -// static -void LLSelectMgr::packDuplicateOnRayHead(void *user_data) -{ - LLMessageSystem *msg = gMessageSystem; - LLDuplicateOnRayData *data = (LLDuplicateOnRayData *)user_data; - - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() ); - msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID() ); - msg->addVector3Fast(_PREHASH_RayStart, data->mRayStartRegion ); - msg->addVector3Fast(_PREHASH_RayEnd, data->mRayEndRegion ); - msg->addBOOLFast(_PREHASH_BypassRaycast, data->mBypassRaycast ); - msg->addBOOLFast(_PREHASH_RayEndIsIntersection, data->mRayEndIsIntersection ); - msg->addBOOLFast(_PREHASH_CopyCenters, data->mCopyCenters ); - msg->addBOOLFast(_PREHASH_CopyRotates, data->mCopyRotates ); - msg->addUUIDFast(_PREHASH_RayTargetID, data->mRayTargetID ); - msg->addU32Fast(_PREHASH_DuplicateFlags, data->mFlags ); -} - - - -//------------------------------------------------------------------------ -// Object position, scale, rotation update, all-in-one -//------------------------------------------------------------------------ - -void LLSelectMgr::sendMultipleUpdate(U32 type) -{ - if (type == UPD_NONE) return; - // send individual updates when selecting textures or individual objects - ESendType send_type = (!gSavedSettings.getBOOL("EditLinkedParts") && !getTEMode()) ? SEND_ONLY_ROOTS : SEND_ROOTS_FIRST; - if (send_type == SEND_ONLY_ROOTS) - { - // tell simulator to apply to whole linked sets - type |= UPD_LINKED_SETS; - } - - sendListToRegions( - "MultipleObjectUpdate", - packAgentAndSessionID, - packMultipleUpdate, - &type, - send_type); -} - -// static -void LLSelectMgr::packMultipleUpdate(LLSelectNode* node, void *user_data) -{ - LLViewerObject* object = node->getObject(); - U32 *type32 = (U32 *)user_data; - U8 type = (U8)*type32; - U8 data[256]; - - gMessageSystem->nextBlockFast(_PREHASH_ObjectData); - gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID() ); - gMessageSystem->addU8Fast(_PREHASH_Type, type ); - - S32 offset = 0; - - // JC: You MUST pack the data in this order. The receiving - // routine process_multiple_update_message on simulator will - // extract them in this order. - - if (type & UPD_POSITION) - { - htonmemcpy(&data[offset], &(object->getPosition().mV), MVT_LLVector3, 12); - offset += 12; - } - if (type & UPD_ROTATION) - { - LLQuaternion quat = object->getRotation(); - LLVector3 vec = quat.packToVector3(); - htonmemcpy(&data[offset], &(vec.mV), MVT_LLQuaternion, 12); - offset += 12; - } - if (type & UPD_SCALE) - { - //llinfos << "Sending object scale " << object->getScale() << llendl; - htonmemcpy(&data[offset], &(object->getScale().mV), MVT_LLVector3, 12); - offset += 12; - } - gMessageSystem->addBinaryDataFast(_PREHASH_Data, data, offset); -} - -//------------------------------------------------------------------------ -// Ownership -//------------------------------------------------------------------------ -struct LLOwnerData -{ - LLUUID owner_id; - LLUUID group_id; - BOOL override; -}; - -void LLSelectMgr::sendOwner(const LLUUID& owner_id, - const LLUUID& group_id, - BOOL override) -{ - LLOwnerData data; - - data.owner_id = owner_id; - data.group_id = group_id; - data.override = override; - - sendListToRegions("ObjectOwner", packOwnerHead, packObjectLocalID, &data, SEND_ONLY_ROOTS); -} - -// static -void LLSelectMgr::packOwnerHead(void *user_data) -{ - LLOwnerData *data = (LLOwnerData *)user_data; - - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() ); - gMessageSystem->nextBlockFast(_PREHASH_HeaderData); - gMessageSystem->addBOOLFast(_PREHASH_Override, data->override); - gMessageSystem->addUUIDFast(_PREHASH_OwnerID, data->owner_id); - gMessageSystem->addUUIDFast(_PREHASH_GroupID, data->group_id); -} - -//------------------------------------------------------------------------ -// Group -//------------------------------------------------------------------------ - -void LLSelectMgr::sendGroup(const LLUUID& group_id) -{ - LLUUID local_group_id(group_id); - sendListToRegions("ObjectGroup", packAgentAndSessionAndGroupID, packObjectLocalID, &local_group_id, SEND_ONLY_ROOTS); -} - - -//------------------------------------------------------------------------ -// Buy -//------------------------------------------------------------------------ - -struct LLBuyData -{ - std::vector mObjectsSent; - LLUUID mCategoryID; - LLSaleInfo mSaleInfo; -}; - -// *NOTE: does not work for multiple object buy, which UI does not -// currently support sale info is used for verification only, if it -// doesn't match region info then sale is canceled Need to get sale -// info -as displayed in the UI- for every item. -void LLSelectMgr::sendBuy(const LLUUID& buyer_id, const LLUUID& category_id, const LLSaleInfo sale_info) -{ - LLBuyData buy; - buy.mCategoryID = category_id; - buy.mSaleInfo = sale_info; - sendListToRegions("ObjectBuy", packAgentGroupAndCatID, packBuyObjectIDs, &buy, SEND_ONLY_ROOTS); -} - -// static -void LLSelectMgr::packBuyObjectIDs(LLSelectNode* node, void* data) -{ - LLBuyData* buy = (LLBuyData*)data; - - LLViewerObject* object = node->getObject(); - if (std::find(buy->mObjectsSent.begin(), buy->mObjectsSent.end(), object) == buy->mObjectsSent.end()) - { - buy->mObjectsSent.push_back(object); - gMessageSystem->nextBlockFast(_PREHASH_ObjectData); - gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID() ); - gMessageSystem->addU8Fast(_PREHASH_SaleType, buy->mSaleInfo.getSaleType()); - gMessageSystem->addS32Fast(_PREHASH_SalePrice, buy->mSaleInfo.getSalePrice()); - } -} - -//------------------------------------------------------------------------ -// Permissions -//------------------------------------------------------------------------ - -struct LLPermData -{ - U8 mField; - BOOL mSet; - U32 mMask; - BOOL mOverride; -}; - -// TODO: Make this able to fail elegantly. -void LLSelectMgr::selectionSetObjectPermissions(U8 field, - BOOL set, - U32 mask, - BOOL override) -{ - LLPermData data; - - data.mField = field; - data.mSet = set; - data.mMask = mask; - data.mOverride = override; - - sendListToRegions("ObjectPermissions", packPermissionsHead, packPermissions, &data, SEND_ONLY_ROOTS); -} - -void LLSelectMgr::packPermissionsHead(void* user_data) -{ - LLPermData* data = (LLPermData*)user_data; - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - gMessageSystem->nextBlockFast(_PREHASH_HeaderData); - gMessageSystem->addBOOLFast(_PREHASH_Override, data->mOverride); -} - - -// Now that you've added a bunch of objects, send a select message -// on the entire list for efficiency. -/* -void LLSelectMgr::sendSelect() -{ - llerrs << "Not implemented" << llendl; -} -*/ - -void LLSelectMgr::deselectAll() -{ - if (!mSelectedObjects->getNumNodes()) - { - return; - } - - // Zap the angular velocity, as the sim will set it to zero - for (LLObjectSelection::iterator iter = mSelectedObjects->begin(); - iter != mSelectedObjects->end(); iter++ ) - { - LLViewerObject *objectp = (*iter)->getObject(); - objectp->setAngularVelocity( 0,0,0 ); - objectp->setVelocity( 0,0,0 ); - } - - sendListToRegions( - "ObjectDeselect", - packAgentAndSessionID, - packObjectLocalID, - NULL, - SEND_INDIVIDUALS); - - removeAll(); - - mLastSentSelectionCenterGlobal.clearVec(); - - updatePointAt(); -} - -void LLSelectMgr::deselectAllForStandingUp() -{ - /* - This function is similar deselectAll() except for the first if statement - which was removed. This is needed as a workaround for DEV-2854 - */ - - // Zap the angular velocity, as the sim will set it to zero - for (LLObjectSelection::iterator iter = mSelectedObjects->begin(); - iter != mSelectedObjects->end(); iter++ ) - { - LLViewerObject *objectp = (*iter)->getObject(); - objectp->setAngularVelocity( 0,0,0 ); - objectp->setVelocity( 0,0,0 ); - } - - sendListToRegions( - "ObjectDeselect", - packAgentAndSessionID, - packObjectLocalID, - NULL, - SEND_INDIVIDUALS); - - removeAll(); - - mLastSentSelectionCenterGlobal.clearVec(); - - updatePointAt(); -} - -void LLSelectMgr::deselectUnused() -{ - // no more outstanding references to this selection - if (mSelectedObjects->getNumRefs() == 1) - { - deselectAll(); - } -} - -void LLSelectMgr::convertTransient() -{ - LLObjectSelection::iterator node_it; - for (node_it = mSelectedObjects->begin(); node_it != mSelectedObjects->end(); ++node_it) - { - LLSelectNode *nodep = *node_it; - nodep->setTransient(FALSE); - } -} - -void LLSelectMgr::deselectAllIfTooFar() -{ - if (mSelectedObjects->isEmpty() || mSelectedObjects->mSelectType == SELECT_TYPE_HUD) - { - return; - } - - // HACK: Don't deselect when we're navigating to rate an object's - // owner or creator. JC - if (gPieObject->getVisible() || gPieRate->getVisible() ) - { - return; - } - - LLVector3d selectionCenter = getSelectionCenterGlobal(); - if (gSavedSettings.getBOOL("LimitSelectDistance") - && (!mSelectedObjects->getPrimaryObject() || !mSelectedObjects->getPrimaryObject()->isAvatar()) - && !mSelectedObjects->isAttachment() - && !selectionCenter.isExactlyZero()) - { - F32 deselect_dist = gSavedSettings.getF32("MaxSelectDistance"); - F32 deselect_dist_sq = deselect_dist * deselect_dist; - - LLVector3d select_delta = gAgent.getPositionGlobal() - selectionCenter; - F32 select_dist_sq = (F32) select_delta.magVecSquared(); - - if (select_dist_sq > deselect_dist_sq) - { - if (gDebugSelectMgr) - { - llinfos << "Selection manager: auto-deselecting, select_dist = " << fsqrtf(select_dist_sq) << llendl; - llinfos << "agent pos global = " << gAgent.getPositionGlobal() << llendl; - llinfos << "selection pos global = " << selectionCenter << llendl; - } - - deselectAll(); - } - } -} - - -void LLSelectMgr::selectionSetObjectName(const std::string& name) -{ - // we only work correctly if 1 object is selected. - if(mSelectedObjects->getRootObjectCount() == 1) - { - sendListToRegions("ObjectName", - packAgentAndSessionID, - packObjectName, - (void*)(new std::string(name)), - SEND_ONLY_ROOTS); - } - else if(mSelectedObjects->getObjectCount() == 1) - { - sendListToRegions("ObjectName", - packAgentAndSessionID, - packObjectName, - (void*)(new std::string(name)), - SEND_INDIVIDUALS); - } -} - -void LLSelectMgr::selectionSetObjectDescription(const std::string& desc) -{ - // we only work correctly if 1 object is selected. - if(mSelectedObjects->getRootObjectCount() == 1) - { - sendListToRegions("ObjectDescription", - packAgentAndSessionID, - packObjectDescription, - (void*)(new std::string(desc)), - SEND_ONLY_ROOTS); - } - else if(mSelectedObjects->getObjectCount() == 1) - { - sendListToRegions("ObjectDescription", - packAgentAndSessionID, - packObjectDescription, - (void*)(new std::string(desc)), - SEND_INDIVIDUALS); - } -} - -void LLSelectMgr::selectionSetObjectCategory(const LLCategory& category) -{ - // for now, we only want to be able to set one root category at - // a time. - if(mSelectedObjects->getRootObjectCount() != 1) return; - sendListToRegions("ObjectCategory", - packAgentAndSessionID, - packObjectCategory, - (void*)(&category), - SEND_ONLY_ROOTS); -} - -void LLSelectMgr::selectionSetObjectSaleInfo(const LLSaleInfo& sale_info) -{ - sendListToRegions("ObjectSaleInfo", - packAgentAndSessionID, - packObjectSaleInfo, - (void*)(&sale_info), - SEND_ONLY_ROOTS); -} - -//---------------------------------------------------------------------- -// Attachments -//---------------------------------------------------------------------- - -void LLSelectMgr::sendAttach(U8 attachment_point) -{ - LLViewerObject* attach_object = mSelectedObjects->getFirstRootObject(); - - if (!attach_object || !gAgent.getAvatarObject() || mSelectedObjects->mSelectType != SELECT_TYPE_WORLD) - { - return; - } - - BOOL build_mode = LLToolMgr::getInstance()->inEdit(); - // Special case: Attach to default location for this object. - if (0 == attachment_point || - get_if_there(gAgent.getAvatarObject()->mAttachmentPoints, (S32)attachment_point, (LLViewerJointAttachment*)NULL)) - { - sendListToRegions( - "ObjectAttach", - packAgentIDAndSessionAndAttachment, - packObjectIDAndRotation, - &attachment_point, - SEND_ONLY_ROOTS ); - if (!build_mode) - { - deselectAll(); - } - } -} - -void LLSelectMgr::sendDetach() -{ - if (!mSelectedObjects->getNumNodes() || mSelectedObjects->mSelectType == SELECT_TYPE_WORLD) - { - return; - } - - sendListToRegions( - "ObjectDetach", - packAgentAndSessionID, - packObjectLocalID, - NULL, - SEND_ONLY_ROOTS ); -} - - -void LLSelectMgr::sendDropAttachment() -{ - if (!mSelectedObjects->getNumNodes() || mSelectedObjects->mSelectType == SELECT_TYPE_WORLD) - { - return; - } - - sendListToRegions( - "ObjectDrop", - packAgentAndSessionID, - packObjectLocalID, - NULL, - SEND_ONLY_ROOTS); -} - -//---------------------------------------------------------------------- -// Links -//---------------------------------------------------------------------- - -void LLSelectMgr::sendLink() -{ - if (!mSelectedObjects->getNumNodes()) - { - return; - } - - sendListToRegions( - "ObjectLink", - packAgentAndSessionID, - packObjectLocalID, - NULL, - SEND_ONLY_ROOTS); -} - -void LLSelectMgr::sendDelink() -{ - if (!mSelectedObjects->getNumNodes()) - { - return; - } - - // Delink needs to send individuals so you can unlink a single object from - // a linked set. - sendListToRegions( - "ObjectDelink", - packAgentAndSessionID, - packObjectLocalID, - NULL, - SEND_INDIVIDUALS); -} - - -//---------------------------------------------------------------------- -// Hinges -//---------------------------------------------------------------------- - -/* -void LLSelectMgr::sendHinge(U8 type) -{ - if (!mSelectedObjects->getNumNodes()) - { - return; - } - - sendListToRegions( - "ObjectHinge", - packHingeHead, - packObjectLocalID, - &type, - SEND_ONLY_ROOTS); -} - - -void LLSelectMgr::sendDehinge() -{ - if (!mSelectedObjects->getNumNodes()) - { - return; - } - - sendListToRegions( - "ObjectDehinge", - packAgentAndSessionID, - packObjectLocalID, - NULL, - SEND_ONLY_ROOTS); -}*/ - -void LLSelectMgr::sendSelect() -{ - if (!mSelectedObjects->getNumNodes()) - { - return; - } - - sendListToRegions( - "ObjectSelect", - packAgentAndSessionID, - packObjectLocalID, - NULL, - SEND_INDIVIDUALS); -} - -// static -void LLSelectMgr::packHingeHead(void *user_data) -{ - U8 *type = (U8 *)user_data; - - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() ); - gMessageSystem->nextBlockFast(_PREHASH_JointType); - gMessageSystem->addU8Fast(_PREHASH_Type, *type ); -} - - -void LLSelectMgr::selectionDump() -{ - struct f : public LLSelectedObjectFunctor - { - virtual bool apply(LLViewerObject* object) - { - object->dump(); - return true; - } - } func; - getSelection()->applyToObjects(&func); -} - -void LLSelectMgr::saveSelectedObjectColors() -{ - struct f : public LLSelectedNodeFunctor - { - virtual bool apply(LLSelectNode* node) - { - node->saveColors(); - return true; - } - } func; - getSelection()->applyToNodes(&func); -} - -void LLSelectMgr::saveSelectedObjectTextures() -{ - // invalidate current selection so we update saved textures - struct f : public LLSelectedNodeFunctor - { - virtual bool apply(LLSelectNode* node) - { - node->mValid = FALSE; - return true; - } - } func; - getSelection()->applyToNodes(&func); - - // request object properties message to get updated permissions data - sendSelect(); -} - - -// This routine should be called whenever a drag is initiated. -// also need to know to which simulator to send update message -void LLSelectMgr::saveSelectedObjectTransform(EActionType action_type) -{ - if (mSelectedObjects->isEmpty()) - { - // nothing selected, so nothing to save - return; - } - - struct f : public LLSelectedNodeFunctor - { - EActionType mActionType; - f(EActionType a) : mActionType(a) {} - virtual bool apply(LLSelectNode* selectNode) - { - LLViewerObject* object = selectNode->getObject(); - if (!object) - { - return true; // skip - } - selectNode->mSavedPositionLocal = object->getPosition(); - if (object->isAttachment()) - { - if (object->isRootEdit()) - { - LLXform* parent_xform = object->mDrawable->getXform()->getParent(); - if (parent_xform) - { - selectNode->mSavedPositionGlobal = gAgent.getPosGlobalFromAgent((object->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition()); - } - else - { - selectNode->mSavedPositionGlobal = object->getPositionGlobal(); - } - } - else - { - LLViewerObject* attachment_root = (LLViewerObject*)object->getParent(); - LLXform* parent_xform = attachment_root ? attachment_root->mDrawable->getXform()->getParent() : NULL; - if (parent_xform) - { - LLVector3 root_pos = (attachment_root->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition(); - LLQuaternion root_rot = (attachment_root->getRotation() * parent_xform->getWorldRotation()); - selectNode->mSavedPositionGlobal = gAgent.getPosGlobalFromAgent((object->getPosition() * root_rot) + root_pos); - } - else - { - selectNode->mSavedPositionGlobal = object->getPositionGlobal(); - } - } - selectNode->mSavedRotation = object->getRenderRotation(); - } - else - { - selectNode->mSavedPositionGlobal = object->getPositionGlobal(); - selectNode->mSavedRotation = object->getRotationRegion(); - } - - selectNode->mSavedScale = object->getScale(); - selectNode->saveTextureScaleRatios(); - return true; - } - } func(action_type); - getSelection()->applyToNodes(&func); - - mSavedSelectionBBox = getBBoxOfSelection(); -} - -struct LLSelectMgrApplyFlags : public LLSelectedObjectFunctor -{ - LLSelectMgrApplyFlags(U32 flags, BOOL state) : mFlags(flags), mState(state) {} - U32 mFlags; - BOOL mState; - virtual bool apply(LLViewerObject* object) - { - if ( object->permModify() && // preemptive permissions check - object->isRoot() && // don't send for child objects - !object->isJointChild()) - { - object->setFlags( mFlags, mState); - } - return true; - } -}; - -void LLSelectMgr::selectionUpdatePhysics(BOOL physics) -{ - LLSelectMgrApplyFlags func( FLAGS_USE_PHYSICS, physics); - getSelection()->applyToObjects(&func); -} - -void LLSelectMgr::selectionUpdateTemporary(BOOL is_temporary) -{ - LLSelectMgrApplyFlags func( FLAGS_TEMPORARY_ON_REZ, is_temporary); - getSelection()->applyToObjects(&func); -} - -void LLSelectMgr::selectionUpdatePhantom(BOOL is_phantom) -{ - LLSelectMgrApplyFlags func( FLAGS_PHANTOM, is_phantom); - getSelection()->applyToObjects(&func); -} - -void LLSelectMgr::selectionUpdateCastShadows(BOOL cast_shadows) -{ - LLSelectMgrApplyFlags func( FLAGS_CAST_SHADOWS, cast_shadows); - getSelection()->applyToObjects(&func); -} - - -//---------------------------------------------------------------------- -// Helpful packing functions for sendObjectMessage() -//---------------------------------------------------------------------- - -// static -void LLSelectMgr::packAgentIDAndSessionAndAttachment( void *user_data) -{ - U8 *attachment_point = (U8*)user_data; - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - gMessageSystem->addU8Fast(_PREHASH_AttachmentPoint, *attachment_point); -} - -// static -void LLSelectMgr::packAgentID( void *user_data) -{ - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); -} - -// static -void LLSelectMgr::packAgentAndSessionID(void* user_data) -{ - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); -} - -// static -void LLSelectMgr::packAgentAndGroupID(void* user_data) -{ - LLOwnerData *data = (LLOwnerData *)user_data; - - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, data->owner_id ); - gMessageSystem->addUUIDFast(_PREHASH_GroupID, data->group_id ); -} - -// static -void LLSelectMgr::packAgentAndSessionAndGroupID(void* user_data) -{ - LLUUID* group_idp = (LLUUID*) user_data; - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - gMessageSystem->addUUIDFast(_PREHASH_GroupID, *group_idp); -} - -// static -void LLSelectMgr::packDuplicateHeader(void* data) -{ - LLUUID group_id(gAgent.getGroupID()); - packAgentAndSessionAndGroupID(&group_id); - - LLDuplicateData* dup_data = (LLDuplicateData*) data; - - gMessageSystem->nextBlockFast(_PREHASH_SharedData); - gMessageSystem->addVector3Fast(_PREHASH_Offset, dup_data->offset); - gMessageSystem->addU32Fast(_PREHASH_DuplicateFlags, dup_data->flags); -} - -// static -void LLSelectMgr::packDeleteHeader(void* userdata) -{ - BOOL force = (BOOL)(intptr_t)userdata; - - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - gMessageSystem->addBOOLFast(_PREHASH_Force, force); -} - -// static -void LLSelectMgr::packAgentGroupAndCatID(void* user_data) -{ - LLBuyData* buy = (LLBuyData*)user_data; - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - gMessageSystem->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID()); - gMessageSystem->addUUIDFast(_PREHASH_CategoryID, buy->mCategoryID); -} - -//static -void LLSelectMgr::packDeRezHeader(void* user_data) -{ - LLDeRezInfo* info = (LLDeRezInfo*)user_data; - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - gMessageSystem->nextBlockFast(_PREHASH_AgentBlock); - gMessageSystem->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID()); - gMessageSystem->addU8Fast(_PREHASH_Destination, (U8)info->mDestination); - gMessageSystem->addUUIDFast(_PREHASH_DestinationID, info->mDestinationID); - LLUUID tid; - tid.generate(); - gMessageSystem->addUUIDFast(_PREHASH_TransactionID, tid); - const U8 PACKET = 1; - gMessageSystem->addU8Fast(_PREHASH_PacketCount, PACKET); - gMessageSystem->addU8Fast(_PREHASH_PacketNumber, PACKET); -} - -// static -void LLSelectMgr::packObjectID(LLSelectNode* node, void *user_data) -{ - gMessageSystem->nextBlockFast(_PREHASH_ObjectData); - gMessageSystem->addUUIDFast(_PREHASH_ObjectID, node->getObject()->mID ); -} - -void LLSelectMgr::packObjectIDAndRotation(LLSelectNode* node, void *user_data) -{ - gMessageSystem->nextBlockFast(_PREHASH_ObjectData); - gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID() ); - gMessageSystem->addQuatFast(_PREHASH_Rotation, node->getObject()->getRotation()); -} - -void LLSelectMgr::packObjectClickAction(LLSelectNode* node, void *user_data) -{ - gMessageSystem->nextBlockFast(_PREHASH_ObjectData); - gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID() ); - gMessageSystem->addU8("ClickAction", node->getObject()->getClickAction()); -} - -void LLSelectMgr::packObjectIncludeInSearch(LLSelectNode* node, void *user_data) -{ - gMessageSystem->nextBlockFast(_PREHASH_ObjectData); - gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID() ); - gMessageSystem->addBOOL("IncludeInSearch", node->getObject()->getIncludeInSearch()); -} - -// static -void LLSelectMgr::packObjectLocalID(LLSelectNode* node, void *) -{ - gMessageSystem->nextBlockFast(_PREHASH_ObjectData); - gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID()); -} - -// static -void LLSelectMgr::packObjectName(LLSelectNode* node, void* user_data) -{ - const std::string* name = (const std::string*)user_data; - if(!name->empty()) - { - gMessageSystem->nextBlockFast(_PREHASH_ObjectData); - gMessageSystem->addU32Fast(_PREHASH_LocalID, node->getObject()->getLocalID()); - gMessageSystem->addStringFast(_PREHASH_Name, *name); - } - delete name; -} - -// static -void LLSelectMgr::packObjectDescription(LLSelectNode* node, void* user_data) -{ - const std::string* desc = (const std::string*)user_data; - if(!desc->empty()) - { - gMessageSystem->nextBlockFast(_PREHASH_ObjectData); - gMessageSystem->addU32Fast(_PREHASH_LocalID, node->getObject()->getLocalID()); - gMessageSystem->addStringFast(_PREHASH_Description, *desc); - } -} - -// static -void LLSelectMgr::packObjectCategory(LLSelectNode* node, void* user_data) -{ - LLCategory* category = (LLCategory*)user_data; - if(!category) return; - gMessageSystem->nextBlockFast(_PREHASH_ObjectData); - gMessageSystem->addU32Fast(_PREHASH_LocalID, node->getObject()->getLocalID()); - category->packMessage(gMessageSystem); -} - -// static -void LLSelectMgr::packObjectSaleInfo(LLSelectNode* node, void* user_data) -{ - LLSaleInfo* sale_info = (LLSaleInfo*)user_data; - if(!sale_info) return; - gMessageSystem->nextBlockFast(_PREHASH_ObjectData); - gMessageSystem->addU32Fast(_PREHASH_LocalID, node->getObject()->getLocalID()); - sale_info->packMessage(gMessageSystem); -} - -// static -void LLSelectMgr::packPhysics(LLSelectNode* node, void *user_data) -{ -} - -// static -void LLSelectMgr::packShape(LLSelectNode* node, void *user_data) -{ -} - -// static -void LLSelectMgr::packPermissions(LLSelectNode* node, void *user_data) -{ - LLPermData *data = (LLPermData *)user_data; - - gMessageSystem->nextBlockFast(_PREHASH_ObjectData); - gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID()); - - gMessageSystem->addU8Fast(_PREHASH_Field, data->mField); - gMessageSystem->addBOOLFast(_PREHASH_Set, data->mSet); - gMessageSystem->addU32Fast(_PREHASH_Mask, data->mMask); -} - -// Utility function to send some information to every region containing -// an object on the selection list. We want to do this to reduce the total -// number of packets sent by the viewer. -void LLSelectMgr::sendListToRegions(const std::string& message_name, - void (*pack_header)(void *user_data), - void (*pack_body)(LLSelectNode* node, void *user_data), - void *user_data, - ESendType send_type) -{ - LLSelectNode* node; - LLViewerRegion* last_region; - LLViewerRegion* current_region; - - S32 objects_sent = 0; - S32 packets_sent = 0; - S32 objects_in_this_packet = 0; - - //clear update override data (allow next update through) - struct f : public LLSelectedNodeFunctor - { - virtual bool apply(LLSelectNode* node) - { - node->mLastPositionLocal.setVec(0,0,0); - node->mLastRotation = LLQuaternion(); - node->mLastScale.setVec(0,0,0); - return true; - } - } func; - getSelection()->applyToNodes(&func); - - std::queue nodes_to_send; - - struct push_all : public LLSelectedNodeFunctor - { - std::queue& nodes_to_send; - push_all(std::queue& n) : nodes_to_send(n) {} - virtual bool apply(LLSelectNode* node) - { - if (node->getObject()) - { - nodes_to_send.push(node); - } - return true; - } - }; - struct push_some : public LLSelectedNodeFunctor - { - std::queue& nodes_to_send; - bool mRoots; - push_some(std::queue& n, bool roots) : nodes_to_send(n), mRoots(roots) {} - virtual bool apply(LLSelectNode* node) - { - if (node->getObject()) - { - BOOL is_root = node->getObject()->isRootEdit(); - if ((mRoots && is_root) || (!mRoots && !is_root)) - { - nodes_to_send.push(node); - } - } - return true; - } - }; - struct push_all pushall(nodes_to_send); - struct push_some pushroots(nodes_to_send, TRUE); - struct push_some pushnonroots(nodes_to_send, FALSE); - - switch(send_type) - { - case SEND_ONLY_ROOTS: - if(message_name == "ObjectBuy") - getSelection()->applyToRootNodes(&pushroots); - else - getSelection()->applyToRootNodes(&pushall); - - break; - case SEND_INDIVIDUALS: - getSelection()->applyToNodes(&pushall); - break; - case SEND_ROOTS_FIRST: - // first roots... - getSelection()->applyToNodes(&pushroots); - // then children... - getSelection()->applyToNodes(&pushnonroots); - break; - case SEND_CHILDREN_FIRST: - // first children... - getSelection()->applyToNodes(&pushnonroots); - // then roots... - getSelection()->applyToNodes(&pushroots); - break; - - default: - llerrs << "Bad send type " << send_type << " passed to SendListToRegions()" << llendl; - } - - // bail if nothing selected - if (nodes_to_send.empty()) - { - return; - } - - node = nodes_to_send.front(); - nodes_to_send.pop(); - - // cache last region information - current_region = node->getObject()->getRegion(); - - // Start duplicate message - // CRO: this isn't - gMessageSystem->newMessage(message_name.c_str()); - (*pack_header)(user_data); - - // For each object - while (node != NULL) - { - // remember the last region, look up the current one - last_region = current_region; - current_region = node->getObject()->getRegion(); - - // if to same simulator and message not too big - if ((current_region == last_region) - && (! gMessageSystem->isSendFull(NULL)) - && (objects_in_this_packet < MAX_OBJECTS_PER_PACKET)) - { - // add another instance of the body of the data - (*pack_body)(node, user_data); - ++objects_sent; - ++objects_in_this_packet; - - // and on to the next object - if(nodes_to_send.empty()) - { - node = NULL; - } - else - { - node = nodes_to_send.front(); - nodes_to_send.pop(); - } - } - else - { - // otherwise send current message and start new one - gMessageSystem->sendReliable( last_region->getHost()); - packets_sent++; - objects_in_this_packet = 0; - - gMessageSystem->newMessage(message_name.c_str()); - (*pack_header)(user_data); - - // don't move to the next object, we still need to add the - // body data. - } - } - - // flush messages - if (gMessageSystem->getCurrentSendTotal() > 0) - { - gMessageSystem->sendReliable( current_region->getHost()); - packets_sent++; - } - else - { - gMessageSystem->clearMessage(); - } - - // llinfos << "sendListToRegions " << message_name << " obj " << objects_sent << " pkt " << packets_sent << llendl; -} - - -// -// Network communications -// - -void LLSelectMgr::requestObjectPropertiesFamily(LLViewerObject* object) -{ - // Remember that we asked the properties of this object. - sObjectPropertiesFamilyRequests.insert(object->mID); - //llinfos << "Registered an ObjectPropertiesFamily request for object " << object->mID << llendl; - - LLMessageSystem* msg = gMessageSystem; - - msg->newMessageFast(_PREHASH_RequestObjectPropertiesFamily); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_ObjectData); - msg->addU32Fast(_PREHASH_RequestFlags, 0x0 ); - msg->addUUIDFast(_PREHASH_ObjectID, object->mID ); - - LLViewerRegion* regionp = object->getRegion(); - msg->sendReliable( regionp->getHost() ); -} - - -// static -void LLSelectMgr::processObjectProperties(LLMessageSystem* msg, void** user_data) -{ - S32 i; - S32 count = msg->getNumberOfBlocksFast(_PREHASH_ObjectData); - for (i = 0; i < count; i++) - { - LLUUID id; - msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, id, i); - - LLUUID creator_id; - LLUUID owner_id; - LLUUID group_id; - LLUUID last_owner_id; - U64 creation_date; - LLUUID extra_id; - U32 base_mask, owner_mask, group_mask, everyone_mask, next_owner_mask; - LLSaleInfo sale_info; - LLCategory category; - LLAggregatePermissions ag_perms; - LLAggregatePermissions ag_texture_perms; - LLAggregatePermissions ag_texture_perms_owner; - - msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_CreatorID, creator_id, i); - msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_OwnerID, owner_id, i); - msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_GroupID, group_id, i); - msg->getU64Fast(_PREHASH_ObjectData, _PREHASH_CreationDate, creation_date, i); - msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_BaseMask, base_mask, i); - msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_OwnerMask, owner_mask, i); - msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_GroupMask, group_mask, i); - msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_EveryoneMask, everyone_mask, i); - msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_NextOwnerMask, next_owner_mask, i); - sale_info.unpackMultiMessage(msg, _PREHASH_ObjectData, i); - - ag_perms.unpackMessage(msg, _PREHASH_ObjectData, _PREHASH_AggregatePerms, i); - ag_texture_perms.unpackMessage(msg, _PREHASH_ObjectData, _PREHASH_AggregatePermTextures, i); - ag_texture_perms_owner.unpackMessage(msg, _PREHASH_ObjectData, _PREHASH_AggregatePermTexturesOwner, i); - category.unpackMultiMessage(msg, _PREHASH_ObjectData, i); - - S16 inv_serial = 0; - msg->getS16Fast(_PREHASH_ObjectData, _PREHASH_InventorySerial, inv_serial, i); - - LLUUID item_id; - msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ItemID, item_id, i); - LLUUID folder_id; - msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_FolderID, folder_id, i); - LLUUID from_task_id; - msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_FromTaskID, from_task_id, i); - - msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_LastOwnerID, last_owner_id, i); - - std::string name; - msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Name, name, i); - std::string desc; - msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Description, desc, i); - - std::string touch_name; - msg->getStringFast(_PREHASH_ObjectData, _PREHASH_TouchName, touch_name, i); - std::string sit_name; - msg->getStringFast(_PREHASH_ObjectData, _PREHASH_SitName, sit_name, i); - - //unpack TE IDs - std::vector texture_ids; - S32 size = msg->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_TextureID); - if (size > 0) - { - S8 packed_buffer[SELECT_MAX_TES * UUID_BYTES]; - msg->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextureID, packed_buffer, 0, i, SELECT_MAX_TES * UUID_BYTES); - - for (S32 buf_offset = 0; buf_offset < size; buf_offset += UUID_BYTES) - { - LLUUID tid; - memcpy(tid.mData, packed_buffer + buf_offset, UUID_BYTES); /* Flawfinder: ignore */ - texture_ids.push_back(tid); - } - } - - // Send to export floaters - LLFloaterExport::receiveObjectProperties(id, name, desc); - // - - // Iterate through nodes at end, since it can be on both the regular AND hover list - struct f : public LLSelectedNodeFunctor - { - LLUUID mID; - f(const LLUUID& id) : mID(id) {} - virtual bool apply(LLSelectNode* node) - { - return (node->getObject() && node->getObject()->mID == mID); - } - } func(id); - LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode(&func); - - if (node) - { - if (node->mInventorySerial != inv_serial) - { - node->getObject()->dirtyInventory(); - } - - // save texture data as soon as we get texture perms first time - if (!node->mValid) - { - BOOL can_copy = FALSE; - BOOL can_transfer = FALSE; - - LLAggregatePermissions::EValue value = LLAggregatePermissions::AP_NONE; - if(node->getObject()->permYouOwner()) - { - value = ag_texture_perms_owner.getValue(PERM_COPY); - if (value == LLAggregatePermissions::AP_EMPTY || value == LLAggregatePermissions::AP_ALL) - { - can_copy = TRUE; - } - value = ag_texture_perms_owner.getValue(PERM_TRANSFER); - if (value == LLAggregatePermissions::AP_EMPTY || value == LLAggregatePermissions::AP_ALL) - { - can_transfer = TRUE; - } - } - else - { - value = ag_texture_perms.getValue(PERM_COPY); - if (value == LLAggregatePermissions::AP_EMPTY || value == LLAggregatePermissions::AP_ALL) - { - can_copy = TRUE; - } - value = ag_texture_perms.getValue(PERM_TRANSFER); - if (value == LLAggregatePermissions::AP_EMPTY || value == LLAggregatePermissions::AP_ALL) - { - can_transfer = TRUE; - } - } - - if (can_copy && can_transfer) - { - // this should be the only place that saved textures is called - node->saveTextures(texture_ids); - } - } - - node->mValid = TRUE; - node->mPermissions->init(creator_id, owner_id, - last_owner_id, group_id); - node->mPermissions->initMasks(base_mask, owner_mask, everyone_mask, group_mask, next_owner_mask); - node->mCreationDate = creation_date; - node->mItemID = item_id; - node->mFolderID = folder_id; - node->mFromTaskID = from_task_id; - node->mName.assign(name); - node->mDescription.assign(desc); - node->mSaleInfo = sale_info; - node->mAggregatePerm = ag_perms; - node->mAggregateTexturePerm = ag_texture_perms; - node->mAggregateTexturePermOwner = ag_texture_perms_owner; - node->mCategory = category; - node->mInventorySerial = inv_serial; - node->mSitName.assign(sit_name); - node->mTouchName.assign(touch_name); - } - } - - dialog_refresh_all(); - - // silly hack to allow 'save into inventory' - if(gPopupMenuView->getVisible()) - { - gPopupMenuView->setItemEnabled(SAVE_INTO_INVENTORY, - enable_save_into_inventory(NULL)); - } - - // hack for left-click buy object - LLToolPie::selectionPropertiesReceived(); -} - -// static -void LLSelectMgr::processObjectPropertiesFamily(LLMessageSystem* msg, void** user_data) -{ - LLUUID id; - msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, id); - if (sObjectPropertiesFamilyRequests.count(id) == 0) - { - // This reply is not for us. - return; - } - // We got the reply, so remove the object from the list of pending requests - sObjectPropertiesFamilyRequests.erase(id); - //llinfos << "Got ObjectPropertiesFamily reply for object " << id << llendl; - - U32 request_flags; - LLUUID creator_id; - LLUUID owner_id; - LLUUID group_id; - LLUUID extra_id; - U32 base_mask, owner_mask, group_mask, everyone_mask, next_owner_mask; - LLSaleInfo sale_info; - LLCategory category; - - msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_RequestFlags, request_flags ); - msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_OwnerID, owner_id ); - msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_GroupID, group_id ); - msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_BaseMask, base_mask ); - msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_OwnerMask, owner_mask ); - msg->getU32Fast(_PREHASH_ObjectData,_PREHASH_GroupMask, group_mask ); - msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_EveryoneMask, everyone_mask ); - msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_NextOwnerMask, next_owner_mask); - sale_info.unpackMessage(msg, _PREHASH_ObjectData); - category.unpackMessage(msg, _PREHASH_ObjectData); - - LLUUID last_owner_id; - msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_LastOwnerID, last_owner_id ); - - // unpack name & desc - std::string name; - msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Name, name); - - std::string desc; - msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Description, desc); - - // Send to export floaters - LLFloaterExport::receiveObjectProperties(id, name, desc); - // - // the reporter widget askes the server for info about picked objects - if (request_flags & (COMPLAINT_REPORT_REQUEST | BUG_REPORT_REQUEST)) - { - EReportType report_type = (COMPLAINT_REPORT_REQUEST & request_flags) ? COMPLAINT_REPORT : BUG_REPORT; - LLFloaterReporter *reporterp = LLFloaterReporter::getReporter(report_type); - if (reporterp) - { - std::string fullname; - gCacheName->getFullName(owner_id, fullname); - reporterp->setPickedObjectProperties(name, fullname, owner_id); - } - } - else if (request_flags & OBJECT_PAY_REQUEST) - { - // check if the owner of the paid object is muted - LLMuteList::getInstance()->autoRemove(owner_id, LLMuteList::AR_MONEY); - } - - // Now look through all of the hovered nodes - struct f : public LLSelectedNodeFunctor - { - LLUUID mID; - f(const LLUUID& id) : mID(id) {} - virtual bool apply(LLSelectNode* node) - { - return (node->getObject() && node->getObject()->mID == mID); - } - } func(id); - LLSelectNode* node = LLSelectMgr::getInstance()->getHoverObjects()->getFirstNode(&func); - - if (node) - { - node->mValid = TRUE; - node->mPermissions->init(LLUUID::null, owner_id, - last_owner_id, group_id); - node->mPermissions->initMasks(base_mask, owner_mask, everyone_mask, group_mask, next_owner_mask); - node->mSaleInfo = sale_info; - node->mCategory = category; - node->mName.assign(name); - node->mDescription.assign(desc); - } - - dialog_refresh_all(); -} - - -// static -void LLSelectMgr::processForceObjectSelect(LLMessageSystem* msg, void**) -{ - BOOL reset_list; - msg->getBOOL("Header", "ResetList", reset_list); - - if (reset_list) - { - LLSelectMgr::getInstance()->deselectAll(); - } - - LLUUID full_id; - S32 local_id; - LLViewerObject* object; - std::vector objects; - S32 i; - S32 block_count = msg->getNumberOfBlocks("Data"); - - for (i = 0; i < block_count; i++) - { - msg->getS32("Data", "LocalID", local_id, i); - - gObjectList.getUUIDFromLocal(full_id, - local_id, - msg->getSenderIP(), - msg->getSenderPort()); - object = gObjectList.findObject(full_id); - if (object) - { - objects.push_back(object); - } - } - - // Don't select, just highlight - LLSelectMgr::getInstance()->highlightObjectAndFamily(objects); -} - - -extern LLGLdouble gGLModelView[16]; - -void LLSelectMgr::updateSilhouettes() -{ - S32 num_sils_genned = 0; - - LLVector3d cameraPos = gAgent.getCameraPositionGlobal(); - F32 currentCameraZoom = gAgent.getCurrentCameraBuildOffset(); - - if (!mSilhouetteImagep) - { - mSilhouetteImagep = gImageList.getImageFromFile("silhouette.j2c", TRUE, TRUE); - } - - mHighlightedObjects->cleanupNodes(); - - if((cameraPos - mLastCameraPos).magVecSquared() > SILHOUETTE_UPDATE_THRESHOLD_SQUARED * currentCameraZoom * currentCameraZoom) - { - struct f : public LLSelectedObjectFunctor - { - virtual bool apply(LLViewerObject* object) - { - object->setChanged(LLXform::SILHOUETTE); - return true; - } - } func; - getSelection()->applyToObjects(&func); - - mLastCameraPos = gAgent.getCameraPositionGlobal(); - } - - std::vector changed_objects; - - updateSelectionSilhouette(mSelectedObjects, num_sils_genned, changed_objects); - if (mRectSelectedObjects.size() > 0) - { - //gGLSPipelineSelection.set(); - - //mSilhouetteImagep->bindTexture(); - //glAlphaFunc(GL_GREATER, sHighlightAlphaTest); - - std::set roots; - - // sync mHighlightedObjects with mRectSelectedObjects since the latter is rebuilt every frame and former - // persists from frame to frame to avoid regenerating object silhouettes - // mHighlightedObjects includes all siblings of rect selected objects - - BOOL select_linked_set = !gSavedSettings.getBOOL("EditLinkedParts"); - - // generate list of roots from current object selection - for (std::set >::iterator iter = mRectSelectedObjects.begin(); - iter != mRectSelectedObjects.end(); iter++) - { - LLViewerObject *objectp = *iter; - if (select_linked_set) - { - LLViewerObject *rootp = (LLViewerObject*)objectp->getRoot(); - roots.insert(rootp); - } - else - { - roots.insert(objectp); - } - } - - // remove highlight nodes not in roots list - std::vector remove_these_nodes; - std::vector remove_these_roots; - - for (LLObjectSelection::iterator iter = mHighlightedObjects->begin(); - iter != mHighlightedObjects->end(); iter++) - { - LLSelectNode* node = *iter; - LLViewerObject* objectp = node->getObject(); - if (!objectp) - continue; - if (objectp->isRoot() || !select_linked_set) - { - if (roots.count(objectp) == 0) - { - remove_these_nodes.push_back(node); - } - else - { - remove_these_roots.push_back(objectp); - } - } - else - { - LLViewerObject* rootp = (LLViewerObject*)objectp->getRoot(); - - if (roots.count(rootp) == 0) - { - remove_these_nodes.push_back(node); - } - } - } - - // remove all highlight nodes no longer in rectangle selection - for (std::vector::iterator iter = remove_these_nodes.begin(); - iter != remove_these_nodes.end(); ++iter) - { - LLSelectNode* nodep = *iter; - mHighlightedObjects->removeNode(nodep); - } - - // remove all root objects already being highlighted - for (std::vector::iterator iter = remove_these_roots.begin(); - iter != remove_these_roots.end(); ++iter) - { - LLViewerObject* objectp = *iter; - roots.erase(objectp); - } - - // add all new objects in rectangle selection - for (std::set::iterator iter = roots.begin(); - iter != roots.end(); iter++) - { - LLViewerObject* objectp = *iter; - if (!canSelectObject(objectp)) - { - continue; - } - - LLSelectNode* rect_select_root_node = new LLSelectNode(objectp, TRUE); - rect_select_root_node->selectAllTEs(TRUE); - - if (!select_linked_set) - { - rect_select_root_node->mIndividualSelection = TRUE; - } - else - { - LLViewerObject::const_child_list_t& child_list = objectp->getChildren(); - for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); - iter != child_list.end(); iter++) - { - LLViewerObject* child_objectp = *iter; - - if (!canSelectObject(child_objectp)) - { - continue; - } - - LLSelectNode* rect_select_node = new LLSelectNode(child_objectp, TRUE); - rect_select_node->selectAllTEs(TRUE); - mHighlightedObjects->addNodeAtEnd(rect_select_node); - } - } - - // Add the root last, to preserve order for link operations. - mHighlightedObjects->addNodeAtEnd(rect_select_root_node); - } - - num_sils_genned = 0; - - // render silhouettes for highlighted objects - //BOOL subtracting_from_selection = (gKeyboard->currentMask(TRUE) == MASK_CONTROL); - for (S32 pass = 0; pass < 2; pass++) - { - for (LLObjectSelection::iterator iter = mHighlightedObjects->begin(); - iter != mHighlightedObjects->end(); iter++) - { - LLSelectNode* node = *iter; - LLViewerObject* objectp = node->getObject(); - if (!objectp) - continue; - - // do roots first, then children so that root flags are cleared ASAP - BOOL roots_only = (pass == 0); - BOOL is_root = objectp->isRootEdit(); - if (roots_only != is_root) - { - continue; - } - - if (!node->mSilhouetteExists - || objectp->isChanged(LLXform::SILHOUETTE) - || (objectp->getParent() && objectp->getParent()->isChanged(LLXform::SILHOUETTE))) - { - if (num_sils_genned++ < MAX_SILS_PER_FRAME) - { - generateSilhouette(node, LLViewerCamera::getInstance()->getOrigin()); - changed_objects.push_back(objectp); - } - else if (objectp->isAttachment() && objectp->getRootEdit()->mDrawable.notNull()) - { - //RN: hack for orthogonal projection of HUD attachments - LLViewerJointAttachment* attachment_pt = (LLViewerJointAttachment*)objectp->getRootEdit()->mDrawable->getParent(); - if (attachment_pt && attachment_pt->getIsHUDAttachment()) - { - LLVector3 camera_pos = LLVector3(-10000.f, 0.f, 0.f); - generateSilhouette(node, camera_pos); - } - } - } - //LLColor4 highlight_color; - // - //if (subtracting_from_selection) - //{ - // node->renderOneSilhouette(LLColor4::red); - //} - //else if (!objectp->isSelected()) - //{ - // highlight_color = objectp->isRoot() ? sHighlightParentColor : sHighlightChildColor; - // node->renderOneSilhouette(highlight_color); - //} - } - } - //mSilhouetteImagep->unbindTexture(0, GL_TEXTURE_2D); - } - else - { - mHighlightedObjects->deleteAllNodes(); - } - - for (std::vector::iterator iter = changed_objects.begin(); - iter != changed_objects.end(); ++iter) - { - // clear flags after traversing node list (as child objects need to refer to parent flags, etc) - LLViewerObject* objectp = *iter; - objectp->clearChanged(LLXform::MOVED | LLXform::SILHOUETTE); - } - - //gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); -} - -void LLSelectMgr::updateSelectionSilhouette(LLObjectSelectionHandle object_handle, S32& num_sils_genned, std::vector& changed_objects) -{ - if (object_handle->getNumNodes()) - { - //gGLSPipelineSelection.set(); - - //mSilhouetteImagep->bindTexture(); - //glAlphaFunc(GL_GREATER, sHighlightAlphaTest); - - for (S32 pass = 0; pass < 2; pass++) - { - for (LLObjectSelection::iterator iter = object_handle->begin(); - iter != object_handle->end(); iter++) - { - LLSelectNode* node = *iter; - LLViewerObject* objectp = node->getObject(); - if (!objectp) - continue; - // do roots first, then children so that root flags are cleared ASAP - BOOL roots_only = (pass == 0); - BOOL is_root = (objectp->isRootEdit()); - if (roots_only != is_root || objectp->mDrawable.isNull()) - { - continue; - } - - if (!node->mSilhouetteExists - || objectp->isChanged(LLXform::SILHOUETTE) - || (objectp->getParent() && objectp->getParent()->isChanged(LLXform::SILHOUETTE))) - { - if (num_sils_genned++ < MAX_SILS_PER_FRAME)// && objectp->mDrawable->isVisible()) - { - generateSilhouette(node, LLViewerCamera::getInstance()->getOrigin()); - changed_objects.push_back(objectp); - } - else if (objectp->isAttachment()) - { - //RN: hack for orthogonal projection of HUD attachments - LLViewerJointAttachment* attachment_pt = (LLViewerJointAttachment*)objectp->getRootEdit()->mDrawable->getParent(); - if (attachment_pt && attachment_pt->getIsHUDAttachment()) - { - LLVector3 camera_pos = LLVector3(-10000.f, 0.f, 0.f); - generateSilhouette(node, camera_pos); - } - } - } - } - } - } -} -void LLSelectMgr::renderSilhouettes(BOOL for_hud) -{ - if (!mRenderSilhouettes || !LLSelectMgr::sRenderSelectionHighlights) - { - return; - } - - gGL.getTexUnit(0)->bind(mSilhouetteImagep.get()); - LLGLSPipelineSelection gls_select; - gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.f); - LLGLEnable blend(GL_BLEND); - LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); - - LLVOAvatar* avatar = gAgent.getAvatarObject(); - if (for_hud && avatar) - { - LLBBox hud_bbox = avatar->getHUDBBox(); - - F32 cur_zoom = gAgent.mHUDCurZoom; - - // set up transform to encompass bounding box of HUD - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - F32 depth = llmax(1.f, hud_bbox.getExtentLocal().mV[VX] * 1.1f); - glOrtho(-0.5f * LLViewerCamera::getInstance()->getAspect(), 0.5f * LLViewerCamera::getInstance()->getAspect(), -0.5f, 0.5f, 0.f, depth); - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glLoadMatrixf(OGL_TO_CFR_ROTATION); // Load Cory's favorite reference frame - glTranslatef(-hud_bbox.getCenterLocal().mV[VX] + (depth *0.5f), 0.f, 0.f); - glScalef(cur_zoom, cur_zoom, cur_zoom); - } - if (mSelectedObjects->getNumNodes()) - { - LLUUID inspect_item_id = LLFloaterInspect::getSelectedUUID(); - - // - //for (S32 pass = 0; pass < 2; pass++) - //{ - // - for (LLObjectSelection::iterator iter = mSelectedObjects->begin(); - iter != mSelectedObjects->end(); iter++) - { - LLSelectNode* node = *iter; - LLViewerObject* objectp = node->getObject(); - if (!objectp) - continue; - if (objectp->isHUDAttachment() != for_hud) - { - continue; - } - if(objectp->getID() == inspect_item_id) - { - node->renderOneSilhouette(sHighlightInspectColor); - } - else if (node->isTransient()) - { - BOOL oldHidden = LLSelectMgr::sRenderHiddenSelections; - LLSelectMgr::sRenderHiddenSelections = FALSE; - node->renderOneSilhouette(sContextSilhouetteColor); - LLSelectMgr::sRenderHiddenSelections = oldHidden; - } - else if (objectp->isRootEdit()) - { - node->renderOneSilhouette(sSilhouetteParentColor); - } - else - { - node->renderOneSilhouette(sSilhouetteChildColor); - } - } - // - //} - // - } - - if (mHighlightedObjects->getNumNodes()) - { - // render silhouettes for highlighted objects - BOOL subtracting_from_selection = (gKeyboard->currentMask(TRUE) == MASK_CONTROL); - for (S32 pass = 0; pass < 2; pass++) - { - for (LLObjectSelection::iterator iter = mHighlightedObjects->begin(); - iter != mHighlightedObjects->end(); iter++) - { - LLSelectNode* node = *iter; - LLViewerObject* objectp = node->getObject(); - if (!objectp) - continue; - if (objectp->isHUDAttachment() != for_hud) - { - continue; - } - - if (subtracting_from_selection) - { - node->renderOneSilhouette(LLColor4::red); - } - else if (!objectp->isSelected()) - { - LLColor4 highlight_color = objectp->isRoot() ? sHighlightParentColor : sHighlightChildColor; - node->renderOneSilhouette(highlight_color); - } - } - } - } - - if (for_hud && avatar) - { - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - stop_glerror(); - } - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); -} - -void LLSelectMgr::generateSilhouette(LLSelectNode* nodep, const LLVector3& view_point) -{ - LLViewerObject* objectp = nodep->getObject(); - - if (objectp && objectp->getPCode() == LL_PCODE_VOLUME) - { - ((LLVOVolume*)objectp)->generateSilhouette(nodep, view_point); - } -} - -// -// Utility classes -// -LLSelectNode::LLSelectNode(LLViewerObject* object, BOOL glow) -: mObject(object), - mIndividualSelection(FALSE), - mTransient(FALSE), - mValid(FALSE), - mPermissions(new LLPermissions()), - mInventorySerial(0), - mSilhouetteExists(FALSE), - mDuplicated(FALSE), - mTESelectMask(0), - mLastTESelected(0), - mName(LLStringUtil::null), - mDescription(LLStringUtil::null), - mTouchName(LLStringUtil::null), - mSitName(LLStringUtil::null), - mCreationDate(0) -{ - selectAllTEs(FALSE); - saveColors(); -} - -LLSelectNode::LLSelectNode(const LLSelectNode& nodep) -{ - mTESelectMask = nodep.mTESelectMask; - mLastTESelected = nodep.mLastTESelected; - - mIndividualSelection = nodep.mIndividualSelection; - - mValid = nodep.mValid; - mTransient = nodep.mTransient; - mPermissions = new LLPermissions(*nodep.mPermissions); - mSaleInfo = nodep.mSaleInfo;; - mAggregatePerm = nodep.mAggregatePerm; - mAggregateTexturePerm = nodep.mAggregateTexturePerm; - mAggregateTexturePermOwner = nodep.mAggregateTexturePermOwner; - mName = nodep.mName; - mDescription = nodep.mDescription; - mCategory = nodep.mCategory; - mSavedPositionLocal = nodep.mSavedPositionLocal; - mSavedPositionGlobal = nodep.mSavedPositionGlobal; - mSavedScale = nodep.mSavedScale; - mSavedRotation = nodep.mSavedRotation; - mDuplicated = nodep.mDuplicated; - mDuplicatePos = nodep.mDuplicatePos; - mDuplicateRot = nodep.mDuplicateRot; - mItemID = nodep.mItemID; - mFolderID = nodep.mFolderID; - mFromTaskID = nodep.mFromTaskID; - mTouchName = nodep.mTouchName; - mSitName = nodep.mSitName; - mCreationDate = nodep.mCreationDate; - - mSilhouetteVertices = nodep.mSilhouetteVertices; - mSilhouetteNormals = nodep.mSilhouetteNormals; - mSilhouetteSegments = nodep.mSilhouetteSegments; - mSilhouetteExists = nodep.mSilhouetteExists; - mObject = nodep.mObject; - - std::vector::const_iterator color_iter; - mSavedColors.clear(); - for (color_iter = nodep.mSavedColors.begin(); color_iter != nodep.mSavedColors.end(); ++color_iter) - { - mSavedColors.push_back(*color_iter); - } - - saveTextures(nodep.mSavedTextures); -} - -LLSelectNode::~LLSelectNode() -{ - delete mPermissions; - mPermissions = NULL; -} - -void LLSelectNode::selectAllTEs(BOOL b) -{ - mTESelectMask = b ? 0xFFFFFFFF : 0x0; - mLastTESelected = 0; -} - -void LLSelectNode::selectTE(S32 te_index, BOOL selected) -{ - if (te_index < 0 || te_index >= SELECT_MAX_TES) - { - return; - } - if (selected) - { - mTESelectMask |= (0x1 << te_index); - } - else - { - mTESelectMask &= ~(0x1 << te_index); - } - mLastTESelected = te_index; -} - -BOOL LLSelectNode::isTESelected(S32 te_index) -{ - if (te_index < 0 || te_index >= mObject->getNumTEs()) - { - return FALSE; - } - return (mTESelectMask & (0x1 << te_index)) != 0; -} - -S32 LLSelectNode::getLastSelectedTE() -{ - if (!isTESelected(mLastTESelected)) - { - return -1; - } - return mLastTESelected; -} - -LLViewerObject* LLSelectNode::getObject() -{ - if (!mObject) - { - return NULL; - } - else if (mObject->isDead()) - { - mObject = NULL; - } - return mObject; -} - -void LLSelectNode::setObject(LLViewerObject* object) -{ - mObject = object; -} - -void LLSelectNode::saveColors() -{ - if (mObject.notNull()) - { - mSavedColors.clear(); - for (S32 i = 0; i < mObject->getNumTEs(); i++) - { - const LLTextureEntry* tep = mObject->getTE(i); - mSavedColors.push_back(tep->getColor()); - } - } -} - -void LLSelectNode::saveTextures(const std::vector& textures) -{ - if (mObject.notNull()) - { - mSavedTextures.clear(); - - for (std::vector::const_iterator texture_it = textures.begin(); - texture_it != textures.end(); ++texture_it) - { - mSavedTextures.push_back(*texture_it); - } - } -} - -void LLSelectNode::saveTextureScaleRatios() -{ - mTextureScaleRatios.clear(); - if (mObject.notNull()) - { - for (U8 i = 0; i < mObject->getNumTEs(); i++) - { - F32 s,t; - const LLTextureEntry* tep = mObject->getTE(i); - tep->getScale(&s,&t); - U32 s_axis = 0; - U32 t_axis = 0; - - LLPrimitive::getTESTAxes(i, &s_axis, &t_axis); - - LLVector3 v; - LLVector3 scale = mObject->getScale(); - - if (tep->getTexGen() == LLTextureEntry::TEX_GEN_PLANAR) - { - v.mV[s_axis] = s*scale.mV[s_axis]; - v.mV[t_axis] = t*scale.mV[t_axis]; - } - else - { - v.mV[s_axis] = s/scale.mV[s_axis]; - v.mV[t_axis] = t/scale.mV[t_axis]; - } - - mTextureScaleRatios.push_back(v); - } - } -} - - -// This implementation should be similar to LLTask::allowOperationOnTask -BOOL LLSelectNode::allowOperationOnNode(PermissionBit op, U64 group_proxy_power) const -{ - // Extract ownership. - BOOL object_is_group_owned = FALSE; - LLUUID object_owner_id; - mPermissions->getOwnership(object_owner_id, object_is_group_owned); - - // Operations on invalid or public objects is not allowed. - if (!mObject || (mObject->isDead()) || !mPermissions->isOwned()) - { - return FALSE; - } - - // The transfer permissions can never be given through proxy. - if (PERM_TRANSFER == op) - { - // The owner of an agent-owned object can transfer to themselves. - if ( !object_is_group_owned - && (gAgent.getID() == object_owner_id) ) - { - return TRUE; - } - else - { - // Otherwise check aggregate permissions. - return mObject->permTransfer(); - } - } - - if (PERM_MOVE == op - || PERM_MODIFY == op) - { - // only owners can move or modify their attachments - // no proxy allowed. - if (mObject->isAttachment() && object_owner_id != gAgent.getID()) - { - return FALSE; - } - } - - // Calculate proxy_agent_id and group_id to use for permissions checks. - // proxy_agent_id may be set to the object owner through group powers. - // group_id can only be set to the object's group, if the agent is in that group. - LLUUID group_id = LLUUID::null; - LLUUID proxy_agent_id = gAgent.getID(); - - // Gods can always operate. - if (gAgent.isGodlike()) - { - return TRUE; - } - - // Check if the agent is in the same group as the object. - LLUUID object_group_id = mPermissions->getGroup(); - if (object_group_id.notNull() && - gAgent.isInGroup(object_group_id)) - { - // Assume the object's group during this operation. - group_id = object_group_id; - } - - // Only allow proxy powers for PERM_COPY if the actual agent can - // receive the item (ie has PERM_TRANSFER permissions). - // NOTE: op == PERM_TRANSFER has already been handled, but if - // that ever changes we need to BLOCK proxy powers for PERM_TRANSFER. DK 03/28/06 - if (PERM_COPY != op || mPermissions->allowTransferTo(gAgent.getID())) - { - // Check if the agent can assume ownership through group proxy or agent-granted proxy. - if ( ( object_is_group_owned - && gAgent.hasPowerInGroup(object_owner_id, group_proxy_power)) - // Only allow proxy for move, modify, and copy. - || ( (PERM_MOVE == op || PERM_MODIFY == op || PERM_COPY == op) - && (!object_is_group_owned - && gAgent.isGrantedProxy(*mPermissions)))) - { - // This agent is able to assume the ownership role for this operation. - proxy_agent_id = object_owner_id; - } - } - - // We now have max ownership information. - if (PERM_OWNER == op) - { - // This this was just a check for ownership, we can now return the answer. - return (proxy_agent_id == object_owner_id ? TRUE : FALSE); - } - - // check permissions to see if the agent can operate - return (mPermissions->allowOperationBy(op, proxy_agent_id, group_id)); -} - -//----------------------------------------------------------------------------- -// renderOneSilhouette() -//----------------------------------------------------------------------------- -void LLSelectNode::renderOneSilhouette(const LLColor4 &color) -{ - LLViewerObject* objectp = getObject(); - if (!objectp) - { - return; - } - - LLDrawable* drawable = objectp->mDrawable; - if(!drawable) - { - return; - } - - if (!mSilhouetteExists) - { - return; - } - - BOOL is_hud_object = objectp->isHUDAttachment(); - - if (mSilhouetteVertices.size() == 0 || mSilhouetteNormals.size() != mSilhouetteVertices.size()) - { - return; - } - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - if (!is_hud_object) - { - glLoadIdentity(); - glMultMatrixd(gGLModelView); - } - - - if (drawable->isActive()) - { - glMultMatrixf((F32*) objectp->getRenderMatrix().mMatrix); - } - - LLVolume *volume = objectp->getVolume(); - if (volume) - { - F32 silhouette_thickness; - if (is_hud_object && gAgent.getAvatarObject()) - { - silhouette_thickness = LLSelectMgr::sHighlightThickness / gAgent.mHUDCurZoom; - } - else - { - LLVector3 view_vector = LLViewerCamera::getInstance()->getOrigin() - objectp->getRenderPosition(); - silhouette_thickness = view_vector.magVec() * LLSelectMgr::sHighlightThickness * (LLViewerCamera::getInstance()->getView() / LLViewerCamera::getInstance()->getDefaultFOV()); - } - F32 animationTime = (F32)LLFrameTimer::getElapsedSeconds(); - - F32 u_coord = fmod(animationTime * LLSelectMgr::sHighlightUAnim, 1.f); - F32 v_coord = 1.f - fmod(animationTime * LLSelectMgr::sHighlightVAnim, 1.f); - F32 u_divisor = 1.f / ((F32)(mSilhouetteVertices.size() - 1)); - - if (LLSelectMgr::sRenderHiddenSelections) // && gFloaterTools && gFloaterTools->getVisible()) - { - gGL.flush(); - gGL.blendFunc(LLRender::BF_SOURCE_COLOR, LLRender::BF_ONE); - LLGLEnable fog(GL_FOG); - glFogi(GL_FOG_MODE, GL_LINEAR); - float d = (LLViewerCamera::getInstance()->getPointOfInterest()-LLViewerCamera::getInstance()->getOrigin()).magVec(); - LLColor4 fogCol = color * (F32)llclamp((LLSelectMgr::getInstance()->getSelectionCenterGlobal()-gAgent.getCameraPositionGlobal()).magVec()/(LLSelectMgr::getInstance()->getBBoxOfSelection().getExtentLocal().magVec()*4), 0.0, 1.0); - glFogf(GL_FOG_START, d); - glFogf(GL_FOG_END, d*(1 + (LLViewerCamera::getInstance()->getView() / LLViewerCamera::getInstance()->getDefaultFOV()))); - glFogfv(GL_FOG_COLOR, fogCol.mV); - - LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_GEQUAL); - gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); - gGL.begin(LLRender::LINES); - { - S32 i = 0; - for (S32 seg_num = 0; seg_num < (S32)mSilhouetteSegments.size(); seg_num++) - { - for(; i < mSilhouetteSegments[seg_num]; i++) - { - u_coord += u_divisor * LLSelectMgr::sHighlightUScale; - - gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f); - gGL.texCoord2f( u_coord, v_coord ); - gGL.vertex3fv( mSilhouetteVertices[i].mV ); - } - } - } - gGL.end(); - u_coord = fmod(animationTime * LLSelectMgr::sHighlightUAnim, 1.f); - } - - gGL.flush(); - gGL.setSceneBlendType(LLRender::BT_ALPHA); - gGL.begin(LLRender::TRIANGLES); - { - S32 i = 0; - for (S32 seg_num = 0; seg_num < (S32)mSilhouetteSegments.size(); seg_num++) - { - S32 first_i = i; - LLVector3 v; - LLVector2 t; - - for(; i < mSilhouetteSegments[seg_num]; i++) - { - - if (i == first_i) { - LLVector3 vert = (mSilhouetteNormals[i]) * silhouette_thickness; - vert += mSilhouetteVertices[i]; - - gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.0f); //LLSelectMgr::sHighlightAlpha); - gGL.texCoord2f( u_coord, v_coord + LLSelectMgr::sHighlightVScale ); - gGL.vertex3fv( vert.mV ); - - u_coord += u_divisor * LLSelectMgr::sHighlightUScale; - - gGL.color4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2); - gGL.texCoord2f( u_coord, v_coord ); - gGL.vertex3fv( mSilhouetteVertices[i].mV ); - - v = mSilhouetteVertices[i]; - t = LLVector2(u_coord, v_coord); - } - else { - LLVector3 vert = (mSilhouetteNormals[i]) * silhouette_thickness; - vert += mSilhouetteVertices[i]; - - gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.0f); //LLSelectMgr::sHighlightAlpha); - gGL.texCoord2f( u_coord, v_coord + LLSelectMgr::sHighlightVScale ); - gGL.vertex3fv( vert.mV ); - gGL.vertex3fv( vert.mV ); - - gGL.texCoord2fv(t.mV); - u_coord += u_divisor * LLSelectMgr::sHighlightUScale; - gGL.color4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2); - gGL.vertex3fv(v.mV); - gGL.texCoord2f( u_coord, v_coord ); - gGL.vertex3fv( mSilhouetteVertices[i].mV ); - - } - } - } - } - gGL.end(); - gGL.flush(); - } - glPopMatrix(); -} - -// -// Utility Functions -// - -// Update everyone who cares about the selection list -void dialog_refresh_all() -{ - if (gNoRender) - { - return; - } - - //could refresh selected object info in toolbar here - - gFloaterTools->dirty(); - - if( gPieObject->getVisible() ) - { - gPieObject->arrange(); - } - - if( gPieAttachment->getVisible() ) - { - gPieAttachment->arrange(); - } - - LLFloaterProperties::dirtyAll(); - LLFloaterInspect::dirty(); -} - -S32 get_family_count(LLViewerObject *parent) -{ - if (!parent) - { - llwarns << "Trying to get_family_count on null parent!" << llendl; - } - S32 count = 1; // for this object - LLViewerObject::const_child_list_t& child_list = parent->getChildren(); - for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); - iter != child_list.end(); iter++) - { - LLViewerObject* child = *iter; - - if (!child) - { - llwarns << "Family object has NULL child! Show Doug." << llendl; - } - else if (child->isDead()) - { - llwarns << "Family object has dead child object. Show Doug." << llendl; - } - else - { - if (LLSelectMgr::getInstance()->canSelectObject(child)) - { - count += get_family_count( child ); - } - } - } - return count; -} - -//----------------------------------------------------------------------------- -// updateSelectionCenter -//----------------------------------------------------------------------------- -void LLSelectMgr::updateSelectionCenter() -{ - const F32 MOVE_SELECTION_THRESHOLD = 1.f; // Movement threshold in meters for updating selection - // center (tractor beam) - - //override any object updates received - //for selected objects - overrideObjectUpdates(); - - LLViewerObject* object = mSelectedObjects->getFirstObject(); - if (!object) - { - // nothing selected, probably grabbing - // Ignore by setting to avatar origin. - mSelectionCenterGlobal.clearVec(); - mShowSelection = FALSE; - mSelectionBBox = LLBBox(); - mPauseRequest = NULL; - resetAgentHUDZoom(); - - } - else - { - mSelectedObjects->mSelectType = getSelectTypeForObject(object); - - if (mSelectedObjects->mSelectType == SELECT_TYPE_ATTACHMENT && gAgent.getAvatarObject()) - { - mPauseRequest = gAgent.getAvatarObject()->requestPause(); - } - else - { - mPauseRequest = NULL; - } - - if (mSelectedObjects->mSelectType != SELECT_TYPE_HUD && gAgent.getAvatarObject()) - { - // reset hud ZOOM - gAgent.mHUDTargetZoom = 1.f; - gAgent.mHUDCurZoom = 1.f; - } - - mShowSelection = FALSE; - LLBBox bbox; - - // have stuff selected - LLVector3d select_center; - // keep a list of jointed objects for showing the joint HUDEffects - - // Initialize the bounding box to the root prim, so the BBox orientation - // matches the root prim's (affecting the orientation of the manipulators). - bbox.addBBoxAgent( (mSelectedObjects->getFirstRootObject(TRUE))->getBoundingBoxAgent() ); - - std::vector < LLViewerObject *> jointed_objects; - - for (LLObjectSelection::iterator iter = mSelectedObjects->begin(); - iter != mSelectedObjects->end(); iter++) - { - LLSelectNode* node = *iter; - LLViewerObject* object = node->getObject(); - if (!object) - continue; - LLViewerObject *myAvatar = gAgent.getAvatarObject(); - LLViewerObject *root = object->getRootEdit(); - if (mSelectedObjects->mSelectType == SELECT_TYPE_WORLD && // not an attachment - !root->isChild(myAvatar) && // not the object you're sitting on - !object->isAvatar()) // not another avatar - { - mShowSelection = TRUE; - } - - bbox.addBBoxAgent( object->getBoundingBoxAgent() ); - - if (object->isJointChild()) - { - jointed_objects.push_back(object); - } - } - - LLVector3 bbox_center_agent = bbox.getCenterAgent(); - mSelectionCenterGlobal = gAgent.getPosGlobalFromAgent(bbox_center_agent); - mSelectionBBox = bbox; - - } - - if ( !(gAgentID == LLUUID::null)) - { - LLTool *tool = LLToolMgr::getInstance()->getCurrentTool(); - if (mShowSelection) - { - LLVector3d select_center_global; - - if( tool->isEditing() ) - { - select_center_global = tool->getEditingPointGlobal(); - } - else - { - select_center_global = mSelectionCenterGlobal; - } - - // Send selection center if moved beyond threshold (used to animate tractor beam) - LLVector3d diff; - diff = select_center_global - mLastSentSelectionCenterGlobal; - - if ( diff.magVecSquared() > MOVE_SELECTION_THRESHOLD*MOVE_SELECTION_THRESHOLD ) - { - // Transmit updated selection center - mLastSentSelectionCenterGlobal = select_center_global; - } - } - } - - // give up edit menu if no objects selected - if (gEditMenuHandler == this && mSelectedObjects->getObjectCount() == 0) - { - gEditMenuHandler = NULL; - } -} - -void LLSelectMgr::updatePointAt() -{ - if (mShowSelection) - { - if (mSelectedObjects->getObjectCount()) - { - LLVector3 select_offset; - const LLPickInfo& pick = gViewerWindow->getLastPick(); - LLViewerObject *click_object = pick.getObject(); - if (click_object && click_object->isSelected()) - { - // clicked on another object in our selection group, use that as target - select_offset.setVec(pick.mObjectOffset); - select_offset.rotVec(~click_object->getRenderRotation()); - - gAgent.setPointAt(POINTAT_TARGET_SELECT, click_object, select_offset); - gAgent.setLookAt(LOOKAT_TARGET_SELECT, click_object, select_offset); - } - else - { - // didn't click on an object this time, revert to pointing at center of first object - gAgent.setPointAt(POINTAT_TARGET_SELECT, mSelectedObjects->getFirstObject()); - gAgent.setLookAt(LOOKAT_TARGET_SELECT, mSelectedObjects->getFirstObject()); - } - } - else - { - gAgent.setPointAt(POINTAT_TARGET_CLEAR); - gAgent.setLookAt(LOOKAT_TARGET_CLEAR); - } - } - else - { - gAgent.setPointAt(POINTAT_TARGET_CLEAR); - gAgent.setLookAt(LOOKAT_TARGET_CLEAR); - } -} - -//----------------------------------------------------------------------------- -// getBBoxOfSelection() -//----------------------------------------------------------------------------- -LLBBox LLSelectMgr::getBBoxOfSelection() const -{ - return mSelectionBBox; -} - - -//----------------------------------------------------------------------------- -// canUndo() -//----------------------------------------------------------------------------- -BOOL LLSelectMgr::canUndo() const -{ - return const_cast(this)->mSelectedObjects->getFirstEditableObject() != NULL; // HACK: casting away constness - MG -} - -//----------------------------------------------------------------------------- -// undo() -//----------------------------------------------------------------------------- -void LLSelectMgr::undo() -{ - BOOL select_linked_set = !gSavedSettings.getBOOL("EditLinkedParts"); - LLUUID group_id(gAgent.getGroupID()); - sendListToRegions("Undo", packAgentAndSessionAndGroupID, packObjectID, &group_id, select_linked_set ? SEND_ONLY_ROOTS : SEND_CHILDREN_FIRST); -} - -//----------------------------------------------------------------------------- -// canRedo() -//----------------------------------------------------------------------------- -BOOL LLSelectMgr::canRedo() const -{ - return const_cast(this)->mSelectedObjects->getFirstEditableObject() != NULL; // HACK: casting away constness - MG -} - -//----------------------------------------------------------------------------- -// redo() -//----------------------------------------------------------------------------- -void LLSelectMgr::redo() -{ - BOOL select_linked_set = !gSavedSettings.getBOOL("EditLinkedParts"); - LLUUID group_id(gAgent.getGroupID()); - sendListToRegions("Redo", packAgentAndSessionAndGroupID, packObjectID, &group_id, select_linked_set ? SEND_ONLY_ROOTS : SEND_CHILDREN_FIRST); -} - -//----------------------------------------------------------------------------- -// canDoDelete() -//----------------------------------------------------------------------------- -BOOL LLSelectMgr::canDoDelete() const -{ - // Note: Can only delete root objects (see getFirstDeleteableObject() for more info) - return const_cast(this)->mSelectedObjects->getFirstDeleteableObject() != NULL; // HACK: casting away constness - MG -} - -//----------------------------------------------------------------------------- -// doDelete() -//----------------------------------------------------------------------------- -void LLSelectMgr::doDelete() -{ - selectDelete(); -} - -//----------------------------------------------------------------------------- -// canDeselect() -//----------------------------------------------------------------------------- -BOOL LLSelectMgr::canDeselect() const -{ - return !mSelectedObjects->isEmpty(); -} - -//----------------------------------------------------------------------------- -// deselect() -//----------------------------------------------------------------------------- -void LLSelectMgr::deselect() -{ - deselectAll(); -} -//----------------------------------------------------------------------------- -// canDuplicate() -//----------------------------------------------------------------------------- -BOOL LLSelectMgr::canDuplicate() const -{ - return const_cast(this)->mSelectedObjects->getFirstCopyableObject() != NULL; // HACK: casting away constness - MG -} -//----------------------------------------------------------------------------- -// duplicate() -//----------------------------------------------------------------------------- -void LLSelectMgr::duplicate() -{ - LLVector3 offset(0.5f, 0.5f, 0.f); - selectDuplicate(offset, TRUE); -} - -ESelectType LLSelectMgr::getSelectTypeForObject(LLViewerObject* object) -{ - if (!object) - { - return SELECT_TYPE_WORLD; - } - if (object->isHUDAttachment()) - { - return SELECT_TYPE_HUD; - } - else if (object->isAttachment()) - { - return SELECT_TYPE_ATTACHMENT; - } - else - { - return SELECT_TYPE_WORLD; - } -} - -void LLSelectMgr::validateSelection() -{ - struct f : public LLSelectedObjectFunctor - { - virtual bool apply(LLViewerObject* object) - { - if (!LLSelectMgr::getInstance()->canSelectObject(object)) - { - LLSelectMgr::getInstance()->deselectObjectOnly(object); - } - return true; - } - } func; - getSelection()->applyToObjects(&func); -} - -BOOL LLSelectMgr::canSelectObject(LLViewerObject* object) -{ - // Never select dead objects - if (!object || object->isDead()) - { - return FALSE; - } - - if (mForceSelection) - { - return TRUE; - } - - if ((gSavedSettings.getBOOL("SelectOwnedOnly") && !object->permYouOwner()) || - (gSavedSettings.getBOOL("SelectMovableOnly") && !object->permMove())) - { - // only select my own objects - return FALSE; - } - - // Can't select orphans - if (object->isOrphaned()) return FALSE; - - // Can't select avatars - if (object->isAvatar()) return FALSE; - - // Can't select land - if (object->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH) return FALSE; - - // - //ESelectType selection_type = getSelectTypeForObject(object); - //if (mSelectedObjects->getObjectCount() > 0 && mSelectedObjects->mSelectType != selection_type) return FALSE; - // - - return TRUE; -} - -BOOL LLSelectMgr::setForceSelection(BOOL force) -{ - std::swap(mForceSelection,force); - return force; -} - -void LLSelectMgr::resetAgentHUDZoom() -{ - gAgent.mHUDTargetZoom = 1.f; - gAgent.mHUDCurZoom = 1.f; -} - -void LLSelectMgr::getAgentHUDZoom(F32 &target_zoom, F32 ¤t_zoom) const -{ - target_zoom = gAgent.mHUDTargetZoom; - current_zoom = gAgent.mHUDCurZoom; -} - -void LLSelectMgr::setAgentHUDZoom(F32 target_zoom, F32 current_zoom) -{ - gAgent.mHUDTargetZoom = target_zoom; - gAgent.mHUDCurZoom = current_zoom; -} - -LLObjectSelection::LLObjectSelection() : - LLRefCount(), - mSelectType(SELECT_TYPE_WORLD) -{ -} - -LLObjectSelection::~LLObjectSelection() -{ - deleteAllNodes(); -} - -void LLObjectSelection::cleanupNodes() -{ - for (list_t::iterator iter = mList.begin(); iter != mList.end(); ) - { - list_t::iterator curiter = iter++; - LLSelectNode* node = *curiter; - if (node->getObject() == NULL || node->getObject()->isDead()) - { - mList.erase(curiter); - delete node; - } - } -} - -void LLObjectSelection::updateEffects() -{ -} - -S32 LLObjectSelection::getNumNodes() -{ - return mList.size(); -} - -void LLObjectSelection::addNode(LLSelectNode *nodep) -{ - llassert_always(nodep->getObject() && !nodep->getObject()->isDead()); - mList.push_front(nodep); - mSelectNodeMap[nodep->getObject()] = nodep; -} - -void LLObjectSelection::addNodeAtEnd(LLSelectNode *nodep) -{ - llassert_always(nodep->getObject() && !nodep->getObject()->isDead()); - mList.push_back(nodep); - mSelectNodeMap[nodep->getObject()] = nodep; -} - -void LLObjectSelection::moveNodeToFront(LLSelectNode *nodep) -{ - mList.remove(nodep); - mList.push_front(nodep); -} - -void LLObjectSelection::removeNode(LLSelectNode *nodep) -{ - mSelectNodeMap.erase(nodep->getObject()); - if (nodep->getObject() == mPrimaryObject) - { - mPrimaryObject = NULL; - } - nodep->setObject(NULL); // Will get erased in cleanupNodes() - mList.remove(nodep); -} - -void LLObjectSelection::deleteAllNodes() -{ - std::for_each(mList.begin(), mList.end(), DeletePointer()); - mList.clear(); - mSelectNodeMap.clear(); - mPrimaryObject = NULL; -} - -LLSelectNode* LLObjectSelection::findNode(LLViewerObject* objectp) -{ - std::map, LLSelectNode*>::iterator found_it = mSelectNodeMap.find(objectp); - if (found_it != mSelectNodeMap.end()) - { - return found_it->second; - } - return NULL; -} - -//----------------------------------------------------------------------------- -// isEmpty() -//----------------------------------------------------------------------------- -BOOL LLObjectSelection::isEmpty() const -{ - return (mList.size() == 0); -} - -//----------------------------------------------------------------------------- -// getOwnershipCost() -//----------------------------------------------------------------------------- -BOOL LLObjectSelection::getOwnershipCost(S32 &cost) -{ - S32 count = getObjectCount(); - cost = count * OWNERSHIP_COST_PER_OBJECT; - return (count > 0); -} - - -//----------------------------------------------------------------------------- -// getObjectCount() - returns number of non null objects -//----------------------------------------------------------------------------- -S32 LLObjectSelection::getObjectCount() -{ - cleanupNodes(); - S32 count = mList.size(); - return count; -} - - -//----------------------------------------------------------------------------- -// getTECount() -//----------------------------------------------------------------------------- -S32 LLObjectSelection::getTECount() -{ - S32 count = 0; - for (LLObjectSelection::iterator iter = begin(); iter != end(); iter++) - { - LLSelectNode* node = *iter; - LLViewerObject* object = node->getObject(); - if (!object) - continue; - S32 num_tes = object->getNumTEs(); - for (S32 te = 0; te < num_tes; te++) - { - if (node->isTESelected(te)) - { - ++count; - } - } - } - return count; -} - -//----------------------------------------------------------------------------- -// getRootObjectCount() -//----------------------------------------------------------------------------- -S32 LLObjectSelection::getRootObjectCount() -{ - S32 count = 0; - for (LLObjectSelection::root_iterator iter = root_begin(); iter != root_end(); iter++) - { - ++count; - } - return count; -} - -bool LLObjectSelection::applyToObjects(LLSelectedObjectFunctor* func) -{ - bool result = true; - for (iterator iter = begin(); iter != end(); ) - { - iterator nextiter = iter++; - LLViewerObject* object = (*nextiter)->getObject(); - if (!object) - continue; - bool r = func->apply(object); - result = result && r; - } - return result; -} - -bool LLObjectSelection::applyToRootObjects(LLSelectedObjectFunctor* func, bool firstonly) -{ - bool result = firstonly ? false : true; - for (root_iterator iter = root_begin(); iter != root_end(); ) - { - root_iterator nextiter = iter++; - LLViewerObject* object = (*nextiter)->getObject(); - if (!object) - continue; - bool r = func->apply(object); - if (firstonly && r) - return true; - else - result = result && r; - } - return result; -} - -bool LLObjectSelection::applyToTEs(LLSelectedTEFunctor* func, bool firstonly) -{ - bool result = firstonly ? false : true; - for (iterator iter = begin(); iter != end(); ) - { - iterator nextiter = iter++; - LLSelectNode* node = *nextiter; - LLViewerObject* object = (*nextiter)->getObject(); - if (!object) - continue; - S32 num_tes = llmin((S32)object->getNumTEs(), (S32)object->getNumFaces()); // avatars have TEs but no faces - for (S32 te = 0; te < num_tes; ++te) - { - if (node->isTESelected(te)) - { - bool r = func->apply(object, te); - if (firstonly && r) - return true; - else - result = result && r; - } - } - } - return result; -} - -bool LLObjectSelection::applyToNodes(LLSelectedNodeFunctor *func, bool firstonly) -{ - bool result = firstonly ? false : true; - for (iterator iter = begin(); iter != end(); ) - { - iterator nextiter = iter++; - LLSelectNode* node = *nextiter; - bool r = func->apply(node); - if (firstonly && r) - return true; - else - result = result && r; - } - return result; -} - -bool LLObjectSelection::applyToRootNodes(LLSelectedNodeFunctor *func, bool firstonly) -{ - bool result = firstonly ? false : true; - for (root_iterator iter = root_begin(); iter != root_end(); ) - { - root_iterator nextiter = iter++; - LLSelectNode* node = *nextiter; - bool r = func->apply(node); - if (firstonly && r) - return true; - else - result = result && r; - } - return result; -} - -//----------------------------------------------------------------------------- -// contains() -//----------------------------------------------------------------------------- -BOOL LLObjectSelection::contains(LLViewerObject* object) -{ - return findNode(object) != NULL; -} - - -//----------------------------------------------------------------------------- -// contains() -//----------------------------------------------------------------------------- -BOOL LLObjectSelection::contains(LLViewerObject* object, S32 te) -{ - if (te == SELECT_ALL_TES) - { - // ...all faces - for (LLObjectSelection::iterator iter = begin(); - iter != end(); iter++) - { - LLSelectNode* nodep = *iter; - if (nodep->getObject() == object) - { - BOOL all_selected = TRUE; - for (S32 i = 0; i < SELECT_MAX_TES; i++) - { - all_selected = all_selected && nodep->isTESelected(i); - } - return all_selected; - } - } - return FALSE; - } - else - { - // ...one face - for (LLObjectSelection::iterator iter = begin(); iter != end(); iter++) - { - LLSelectNode* nodep = *iter; - if (nodep->getObject() == object && nodep->isTESelected(te)) - { - return TRUE; - } - } - return FALSE; - } -} - -// returns TRUE is any node is currenly worn as an attachment -BOOL LLObjectSelection::isAttachment() -{ - return (mSelectType == SELECT_TYPE_ATTACHMENT || mSelectType == SELECT_TYPE_HUD); -} - -//----------------------------------------------------------------------------- -// getSelectedParentObject() -//----------------------------------------------------------------------------- -LLViewerObject* getSelectedParentObject(LLViewerObject *object) -{ - LLViewerObject *parent; - while (object && (parent = (LLViewerObject*)object->getParent())) - { - if (parent->isSelected()) - { - object = parent; - } - else - { - break; - } - } - return object; -} - -//----------------------------------------------------------------------------- -// getFirstNode -//----------------------------------------------------------------------------- -LLSelectNode* LLObjectSelection::getFirstNode(LLSelectedNodeFunctor* func) -{ - for (iterator iter = begin(); iter != end(); ++iter) - { - LLSelectNode* node = *iter; - if (func == NULL || func->apply(node)) - { - return node; - } - } - return NULL; -} - -LLSelectNode* LLObjectSelection::getFirstRootNode(LLSelectedNodeFunctor* func, BOOL non_root_ok) -{ - for (root_iterator iter = root_begin(); iter != root_end(); ++iter) - { - LLSelectNode* node = *iter; - if (func == NULL || func->apply(node)) - { - return node; - } - } - if (non_root_ok) - { - // Get non root - return getFirstNode(func); - } - return NULL; -} - - -//----------------------------------------------------------------------------- -// getFirstSelectedObject -//----------------------------------------------------------------------------- -LLViewerObject* LLObjectSelection::getFirstSelectedObject(LLSelectedNodeFunctor* func, BOOL get_parent) -{ - LLSelectNode* res = getFirstNode(func); - if (res && get_parent) - { - return getSelectedParentObject(res->getObject()); - } - else if (res) - { - return res->getObject(); - } - return NULL; -} - -//----------------------------------------------------------------------------- -// getFirstObject() -//----------------------------------------------------------------------------- -LLViewerObject* LLObjectSelection::getFirstObject() -{ - LLSelectNode* res = getFirstNode(NULL); - return res ? res->getObject() : NULL; -} - -//----------------------------------------------------------------------------- -// getFirstRootObject() -//----------------------------------------------------------------------------- -LLViewerObject* LLObjectSelection::getFirstRootObject(BOOL non_root_ok) -{ - LLSelectNode* res = getFirstRootNode(NULL, non_root_ok); - return res ? res->getObject() : NULL; -} - -//----------------------------------------------------------------------------- -// getFirstMoveableNode() -//----------------------------------------------------------------------------- -LLSelectNode* LLObjectSelection::getFirstMoveableNode(BOOL get_root_first) -{ - struct f : public LLSelectedNodeFunctor - { - bool apply(LLSelectNode* node) - { - LLViewerObject* obj = node->getObject(); - return obj && obj->permMove(); - } - } func; - LLSelectNode* res = get_root_first ? getFirstRootNode(&func, TRUE) : getFirstNode(&func); - return res; -} - -//----------------------------------------------------------------------------- -// getFirstCopyableObject() -//----------------------------------------------------------------------------- -LLViewerObject* LLObjectSelection::getFirstCopyableObject(BOOL get_parent) -{ - struct f : public LLSelectedNodeFunctor - { - bool apply(LLSelectNode* node) - { - LLViewerObject* obj = node->getObject(); - return obj && obj->permCopy() && !obj->isAttachment(); - } - } func; - return getFirstSelectedObject(&func, get_parent); -} - -//----------------------------------------------------------------------------- -// getFirstDeleteableObject() -//----------------------------------------------------------------------------- -LLViewerObject* LLObjectSelection::getFirstDeleteableObject() -{ - //RN: don't currently support deletion of child objects, as that requires separating them first - // then derezzing to trash - - struct f : public LLSelectedNodeFunctor - { - bool apply(LLSelectNode* node) - { - LLViewerObject* obj = node->getObject(); - // you can delete an object if you are the owner - // or you have permission to modify it. - if( obj && ( (obj->permModify()) || - (obj->permYouOwner()) || - (!obj->permAnyOwner()) )) // public - { - if( !obj->isAttachment() ) - { - return true; - } - } - return false; - } - } func; - LLSelectNode* node = getFirstNode(&func); - return node ? node->getObject() : NULL; -} - -//----------------------------------------------------------------------------- -// getFirstEditableObject() -//----------------------------------------------------------------------------- -LLViewerObject* LLObjectSelection::getFirstEditableObject(BOOL get_parent) -{ - struct f : public LLSelectedNodeFunctor - { - bool apply(LLSelectNode* node) - { - LLViewerObject* obj = node->getObject(); - return obj && obj->permModify(); - } - } func; - return getFirstSelectedObject(&func, get_parent); -} - -//----------------------------------------------------------------------------- -// getFirstMoveableObject() -//----------------------------------------------------------------------------- -LLViewerObject* LLObjectSelection::getFirstMoveableObject(BOOL get_parent) -{ - struct f : public LLSelectedNodeFunctor - { - bool apply(LLSelectNode* node) - { - LLViewerObject* obj = node->getObject(); - return obj && obj->permMove(); - } - } func; - return getFirstSelectedObject(&func, get_parent); -} - -//----------------------------------------------------------------------------- -// Position + Rotation update methods called from LLViewerJoystick -//----------------------------------------------------------------------------- -bool LLSelectMgr::selectionMove(const LLVector3& displ, - F32 roll, F32 pitch, F32 yaw, U32 update_type) -{ - if (update_type == UPD_NONE) - { - return false; - } - - LLVector3 displ_global; - bool update_success = true; - bool update_position = update_type & UPD_POSITION; - bool update_rotation = update_type & UPD_ROTATION; - const bool noedit_linked_parts = !gSavedSettings.getBOOL("EditLinkedParts"); - - if (update_position) - { - // calculate the distance of the object closest to the camera origin - F32 min_dist = 1e+30f; - LLVector3 obj_pos; - for (LLObjectSelection::root_iterator it = getSelection()->root_begin(); - it != getSelection()->root_end(); ++it) - { - obj_pos = (*it)->getObject()->getPositionEdit(); - - F32 obj_dist = dist_vec(obj_pos, LLViewerCamera::getInstance()->getOrigin()); - if (obj_dist < min_dist) - { - min_dist = obj_dist; - } - } - - // factor the distance inside the displacement vector. This will get us - // equally visible movements for both close and far away selections. - min_dist = sqrt(min_dist) / 2; - displ_global.setVec(displ.mV[0]*min_dist, - displ.mV[1]*min_dist, - displ.mV[2]*min_dist); - - // equates to: Displ_global = Displ * M_cam_axes_in_global_frame - displ_global = LLViewerCamera::getInstance()->rotateToAbsolute(displ_global); - } - - LLQuaternion new_rot; - if (update_rotation) - { - // let's calculate the rotation around each camera axes - LLQuaternion qx(roll, LLViewerCamera::getInstance()->getAtAxis()); - LLQuaternion qy(pitch, LLViewerCamera::getInstance()->getLeftAxis()); - LLQuaternion qz(yaw, LLViewerCamera::getInstance()->getUpAxis()); - new_rot.setQuat(qx * qy * qz); - } - - LLViewerObject *obj; - S32 obj_count = getSelection()->getObjectCount(); - for (LLObjectSelection::root_iterator it = getSelection()->root_begin(); - it != getSelection()->root_end(); ++it ) - { - obj = (*it)->getObject(); - bool enable_pos = false, enable_rot = false; - bool perm_move = obj->permMove(); - bool perm_mod = obj->permModify(); - - LLVector3d sel_center(getSelectionCenterGlobal()); - - if (update_rotation) - { - enable_rot = perm_move - && ((perm_mod && !obj->isAttachment()) || noedit_linked_parts); - - if (enable_rot) - { - int children_count = obj->getChildren().size(); - if (obj_count > 1 && children_count > 0) - { - // for linked sets, rotate around the group center - const LLVector3 t(obj->getPositionGlobal() - sel_center); - - // Ra = T x R x T^-1 - LLMatrix4 mt; mt.setTranslation(t); - const LLMatrix4 mnew_rot(new_rot); - LLMatrix4 mt_1; mt_1.setTranslation(-t); - mt *= mnew_rot; - mt *= mt_1; - - // Rfin = Rcur * Ra - obj->setRotation(obj->getRotationEdit() * mt.quaternion()); - displ_global += mt.getTranslation(); - } - else - { - obj->setRotation(obj->getRotationEdit() * new_rot); - } - } - else - { - update_success = false; - } - } - - if (update_position) - { - // establish if object can be moved or not - enable_pos = perm_move && !obj->isAttachment() - && (perm_mod || noedit_linked_parts); - - if (enable_pos) - { - obj->setPosition(obj->getPositionEdit() + displ_global); - } - else - { - update_success = false; - } - } - - if (enable_pos && enable_rot && obj->mDrawable.notNull()) - { - gPipeline.markMoved(obj->mDrawable, TRUE); - } - } - - if (update_position && update_success && obj_count > 1) - { - updateSelectionCenter(); - } - - return update_success; -} - -void LLSelectMgr::sendSelectionMove() -{ - LLSelectNode *node = mSelectedObjects->getFirstRootNode(); - if (node == NULL) - { - return; - } - - //saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); - - U32 update_type = UPD_POSITION | UPD_ROTATION; - LLViewerRegion *last_region, *curr_region = node->getObject()->getRegion(); - S32 objects_in_this_packet = 0; - - // apply to linked objects if unable to select their individual parts - if (!gSavedSettings.getBOOL("EditLinkedParts") && !getTEMode()) - { - // tell simulator to apply to whole linked sets - update_type |= UPD_LINKED_SETS; - } - - // prepare first bulk message - gMessageSystem->newMessage("MultipleObjectUpdate"); - packAgentAndSessionID(&update_type); - - LLViewerObject *obj = NULL; - for (LLObjectSelection::root_iterator it = getSelection()->root_begin(); - it != getSelection()->root_end(); ++it) - { - obj = (*it)->getObject(); - - // note: following code adapted from sendListToRegions() (@3924) - last_region = curr_region; - curr_region = obj->getRegion(); - - // if not simulator or message too big - if (curr_region != last_region - || gMessageSystem->isSendFull(NULL) - || objects_in_this_packet >= MAX_OBJECTS_PER_PACKET) - { - // send sim the current message and start new one - gMessageSystem->sendReliable(last_region->getHost()); - objects_in_this_packet = 0; - gMessageSystem->newMessage("MultipleObjectUpdate"); - packAgentAndSessionID(&update_type); - } - - // add another instance of the body of data - packMultipleUpdate(*it, &update_type); - ++objects_in_this_packet; - } - - // flush remaining messages - if (gMessageSystem->getCurrentSendTotal() > 0) - { - gMessageSystem->sendReliable(curr_region->getHost()); - } - else - { - gMessageSystem->clearMessage(); - } - - //saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); -} +/** + * @file llselectmgr.cpp + * @brief A manager for selected objects and faces. + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +// file include +#include "llselectmgr.h" + +// library includes +#include "llcachename.h" +#include "lldbstrings.h" +#include "lleconomy.h" +#include "llgl.h" +#include "llrender.h" +#include "llpermissions.h" +#include "llpermissionsflags.h" +#include "llundo.h" +#include "lluuid.h" +#include "llvolume.h" +#include "message.h" +#include "object_flags.h" +#include "llquaternion.h" + +// viewer includes +#include "llagent.h" +#include "llviewerwindow.h" +#include "lldrawable.h" +#include "llfloaterinspect.h" +#include "llfloaterproperties.h" +#include "llfloaterreporter.h" +#include "llfloatertools.h" +#include "llframetimer.h" +#include "llfocusmgr.h" +#include "llhudeffecttrail.h" +#include "llhudmanager.h" +#include "llinventorymodel.h" +#include "llmenugl.h" +#include "llmutelist.h" +#include "llstatusbar.h" +#include "llsurface.h" +#include "lltool.h" +#include "lltooldraganddrop.h" +#include "lltoolmgr.h" +#include "lltoolpie.h" +#include "llui.h" +#include "llviewercamera.h" +#include "llviewercontrol.h" +#include "llviewerimagelist.h" +#include "llviewermedia.h" +#include "llviewermediafocus.h" +#include "llviewermenu.h" +#include "llviewerobject.h" +#include "llviewerobjectlist.h" +#include "llviewerregion.h" +#include "llviewerstats.h" +#include "llvoavatar.h" +#include "llvovolume.h" +#include "pipeline.h" +// +#include "llfloaterexport.h" +// + +#include "llglheaders.h" + +LLViewerObject* getSelectedParentObject(LLViewerObject *object) ; +// +// Consts +// + +const S32 NUM_SELECTION_UNDO_ENTRIES = 200; +const F32 SILHOUETTE_UPDATE_THRESHOLD_SQUARED = 0.02f; +const S32 OWNERSHIP_COST_PER_OBJECT = 10; // Must be the same as economy_constants.price_object_claim in the database. +const S32 MAX_ACTION_QUEUE_SIZE = 20; +const S32 MAX_SILS_PER_FRAME = 50; +const S32 MAX_OBJECTS_PER_PACKET = 254; + +// +// Globals +// + +BOOL gDebugSelectMgr = FALSE; + +BOOL gHideSelectedObjects = FALSE; +BOOL gAllowSelectAvatar = FALSE; + +BOOL LLSelectMgr::sRectSelectInclusive = TRUE; +BOOL LLSelectMgr::sRenderSelectionHighlights = TRUE; +BOOL LLSelectMgr::sRenderHiddenSelections = TRUE; +BOOL LLSelectMgr::sRenderLightRadius = FALSE; +F32 LLSelectMgr::sHighlightThickness = 0.f; +F32 LLSelectMgr::sHighlightUScale = 0.f; +F32 LLSelectMgr::sHighlightVScale = 0.f; +F32 LLSelectMgr::sHighlightAlpha = 0.f; +F32 LLSelectMgr::sHighlightAlphaTest = 0.f; +F32 LLSelectMgr::sHighlightUAnim = 0.f; +F32 LLSelectMgr::sHighlightVAnim = 0.f; +LLColor4 LLSelectMgr::sSilhouetteParentColor; +LLColor4 LLSelectMgr::sSilhouetteChildColor; +LLColor4 LLSelectMgr::sHighlightInspectColor; +LLColor4 LLSelectMgr::sHighlightParentColor; +LLColor4 LLSelectMgr::sHighlightChildColor; +LLColor4 LLSelectMgr::sContextSilhouetteColor; +std::set LLSelectMgr::sObjectPropertiesFamilyRequests; + +static LLObjectSelection *get_null_object_selection(); +template<> + const LLSafeHandle::NullFunc + LLSafeHandle::sNullFunc = get_null_object_selection; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// struct LLDeRezInfo +// +// Used to keep track of important derez info. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +struct LLDeRezInfo +{ + EDeRezDestination mDestination; + LLUUID mDestinationID; + LLDeRezInfo(EDeRezDestination dest, const LLUUID& dest_id) : + mDestination(dest), mDestinationID(dest_id) {} +}; + +// +// Imports +// + + +static LLPointer sNullSelection; + +// +// Functions +// + +void LLSelectMgr::cleanupGlobals() +{ + sNullSelection = NULL; + LLSelectMgr::getInstance()->clearSelections(); +} + +LLObjectSelection *get_null_object_selection() +{ + if (sNullSelection.isNull()) + { + sNullSelection = new LLObjectSelection; + } + return sNullSelection; +} + + +//----------------------------------------------------------------------------- +// LLSelectMgr() +//----------------------------------------------------------------------------- +LLSelectMgr::LLSelectMgr() +{ + mTEMode = FALSE; + mLastCameraPos.clearVec(); + + sHighlightThickness = gSavedSettings.getF32("SelectionHighlightThickness"); + sHighlightUScale = gSavedSettings.getF32("SelectionHighlightUScale"); + sHighlightVScale = gSavedSettings.getF32("SelectionHighlightVScale"); + sHighlightAlpha = gSavedSettings.getF32("SelectionHighlightAlpha"); + sHighlightAlphaTest = gSavedSettings.getF32("SelectionHighlightAlphaTest"); + sHighlightUAnim = gSavedSettings.getF32("SelectionHighlightUAnim"); + sHighlightVAnim = gSavedSettings.getF32("SelectionHighlightVAnim"); + + sSilhouetteParentColor = gColors.getColor("SilhouetteParentColor"); + sSilhouetteChildColor = gColors.getColor("SilhouetteChildColor"); + sHighlightParentColor = gColors.getColor("HighlightParentColor"); + sHighlightChildColor = gColors.getColor("HighlightChildColor"); + sHighlightInspectColor = gColors.getColor("HighlightInspectColor"); + sContextSilhouetteColor = gColors.getColor("ContextSilhouetteColor")*0.5f; + + sRenderLightRadius = gSavedSettings.getBOOL("RenderLightRadius"); + + mRenderSilhouettes = TRUE; + + mGridMode = GRID_MODE_WORLD; + gSavedSettings.setS32("GridMode", (S32)GRID_MODE_WORLD); + mGridValid = FALSE; + + mSelectedObjects = new LLObjectSelection(); + mHoverObjects = new LLObjectSelection(); + mHighlightedObjects = new LLObjectSelection(); +} + + +//----------------------------------------------------------------------------- +// ~LLSelectMgr() +//----------------------------------------------------------------------------- +LLSelectMgr::~LLSelectMgr() +{ + clearSelections(); +} + +void LLSelectMgr::clearSelections() +{ + mHoverObjects->deleteAllNodes(); + mSelectedObjects->deleteAllNodes(); + mHighlightedObjects->deleteAllNodes(); + mRectSelectedObjects.clear(); + mGridObjects.deleteAllNodes(); +} + +void LLSelectMgr::update() +{ + mSelectedObjects->cleanupNodes(); +} + +void LLSelectMgr::updateEffects() +{ + //keep reference grid objects active + struct f : public LLSelectedObjectFunctor + { + virtual bool apply(LLViewerObject* object) + { + LLDrawable* drawable = object->mDrawable; + if (drawable) + { + gPipeline.markMoved(drawable); + } + return true; + } + } func; + mGridObjects.applyToObjects(&func); + + if (mEffectsTimer.getElapsedTimeF32() > 1.f) + { + mSelectedObjects->updateEffects(); + mEffectsTimer.reset(); + } +} + +void LLSelectMgr::overrideObjectUpdates() +{ + //override any position updates from simulator on objects being edited + struct f : public LLSelectedNodeFunctor + { + virtual bool apply(LLSelectNode* selectNode) + { + LLViewerObject* object = selectNode->getObject(); + if (object && object->permMove()) + { + if (!selectNode->mLastPositionLocal.isExactlyZero()) + { + object->setPosition(selectNode->mLastPositionLocal); + } + if (selectNode->mLastRotation != LLQuaternion()) + { + object->setRotation(selectNode->mLastRotation); + } + if (!selectNode->mLastScale.isExactlyZero()) + { + object->setScale(selectNode->mLastScale); + } + } + return true; + } + } func; + getSelection()->applyToNodes(&func); +} + +//----------------------------------------------------------------------------- +// Select just the object, not any other group members. +//----------------------------------------------------------------------------- +LLObjectSelectionHandle LLSelectMgr::selectObjectOnly(LLViewerObject* object, S32 face) +{ + llassert( object ); + + //remember primary object + mSelectedObjects->mPrimaryObject = object; + + // Don't add an object that is already in the list + if (object->isSelected() ) { + // make sure point at position is updated + updatePointAt(); + gEditMenuHandler = this; + return NULL; + } + + if (!canSelectObject(object)) + { + //make_ui_sound("UISndInvalidOp"); + return NULL; + } + + // llinfos << "Adding object to selected object list" << llendl; + + // Place it in the list and tag it. + // This will refresh dialogs. + addAsIndividual(object, face); + + // Stop the object from moving (this anticipates changes on the + // simulator in LLTask::userSelect) + // *FIX: shouldn't zero out these either + object->setVelocity(LLVector3::zero); + object->setAcceleration(LLVector3::zero); + //object->setAngularVelocity(LLVector3::zero); + object->resetRot(); + + // Always send to simulator, so you get a copy of the + // permissions structure back. + gMessageSystem->newMessageFast(_PREHASH_ObjectSelect); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID() ); + LLViewerRegion* regionp = object->getRegion(); + gMessageSystem->sendReliable( regionp->getHost()); + + updatePointAt(); + updateSelectionCenter(); + saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); + + // have selection manager handle edit menu immediately after + // user selects an object + if (mSelectedObjects->getObjectCount()) + { + gEditMenuHandler = this; + } + + return mSelectedObjects; +} + +//----------------------------------------------------------------------------- +// Select the object, parents and children. +//----------------------------------------------------------------------------- +LLObjectSelectionHandle LLSelectMgr::selectObjectAndFamily(LLViewerObject* obj, BOOL add_to_end) +{ + llassert( obj ); + + //remember primary object + mSelectedObjects->mPrimaryObject = obj; + + // This may be incorrect if things weren't family selected before... - djs 07/08/02 + // Don't add an object that is already in the list + if (obj->isSelected() ) + { + // make sure pointat position is updated + updatePointAt(); + gEditMenuHandler = this; + return NULL; + } + + if (!canSelectObject(obj)) + { + //make_ui_sound("UISndInvalidOp"); + return NULL; + } + + // Since we're selecting a family, start at the root, but + // don't include an avatar. + LLViewerObject* root = obj; + + while(!root->isAvatar() && root->getParent() && !root->isJointChild()) + { + LLViewerObject* parent = (LLViewerObject*)root->getParent(); + if (parent->isAvatar()) + { + break; + } + root = parent; + } + + // Collect all of the objects + LLDynamicArray objects; + + root->addThisAndNonJointChildren(objects); + addAsFamily(objects, add_to_end); + + updateSelectionCenter(); + saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); + updatePointAt(); + + dialog_refresh_all(); + + // Always send to simulator, so you get a copy of the permissions + // structure back. + sendSelect(); + + // Stop the object from moving (this anticipates changes on the + // simulator in LLTask::userSelect) + root->setVelocity(LLVector3::zero); + root->setAcceleration(LLVector3::zero); + //root->setAngularVelocity(LLVector3::zero); + root->resetRot(); + + // leave component mode + if (gSavedSettings.getBOOL("EditLinkedParts")) + { + gSavedSettings.setBOOL("EditLinkedParts", FALSE); + promoteSelectionToRoot(); + } + + // have selection manager handle edit menu immediately after + // user selects an object + if (mSelectedObjects->getObjectCount()) + { + gEditMenuHandler = this; + } + + return mSelectedObjects; +} + +//----------------------------------------------------------------------------- +// Select the object, parents and children. +//----------------------------------------------------------------------------- +LLObjectSelectionHandle LLSelectMgr::selectObjectAndFamily(const std::vector& object_list, + BOOL send_to_sim) +{ + // Collect all of the objects, children included + LLDynamicArray objects; + + //clear primary object (no primary object) + mSelectedObjects->mPrimaryObject = NULL; + + if (object_list.size() < 1) + { + return NULL; + } + + // NOTE -- we add the objects in REVERSE ORDER + // to preserve the order in the mSelectedObjects list + for (std::vector::const_reverse_iterator riter = object_list.rbegin(); + riter != object_list.rend(); ++riter) + { + LLViewerObject *object = *riter; + + llassert( object ); + + if (!canSelectObject(object)) continue; + + object->addThisAndNonJointChildren(objects); + addAsFamily(objects); + + // Stop the object from moving (this anticipates changes on the + // simulator in LLTask::userSelect) + object->setVelocity(LLVector3::zero); + object->setAcceleration(LLVector3::zero); + //object->setAngularVelocity(LLVector3::zero); + object->resetRot(); + } + + updateSelectionCenter(); + saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); + updatePointAt(); + dialog_refresh_all(); + + // Almost always send to simulator, so you get a copy of the permissions + // structure back. + // JC: The one case where you don't want to do this is if you're selecting + // all the objects on a sim. + if (send_to_sim) + { + sendSelect(); + } + + // leave component mode + if (gSavedSettings.getBOOL("EditLinkedParts")) + { + gSavedSettings.setBOOL("EditLinkedParts", FALSE); + promoteSelectionToRoot(); + } + + // have selection manager handle edit menu immediately after + // user selects an object + if (mSelectedObjects->getObjectCount()) + { + gEditMenuHandler = this; + } + + return mSelectedObjects; +} + +// Use for when the simulator kills an object. This version also +// handles informing the current tool of the object's deletion. +// +// Caller needs to call dialog_refresh_all if necessary. +BOOL LLSelectMgr::removeObjectFromSelections(const LLUUID &id) +{ + BOOL object_found = FALSE; + LLTool *tool = NULL; + if (!gNoRender) + { + tool = LLToolMgr::getInstance()->getCurrentTool(); + + // It's possible that the tool is editing an object that is not selected + LLViewerObject* tool_editing_object = tool->getEditingObject(); + if( tool_editing_object && tool_editing_object->mID == id) + { + tool->stopEditing(); + object_found = TRUE; + } + } + + // Iterate through selected objects list and kill the object + if( !object_found ) + { + for (LLObjectSelection::iterator iter = getSelection()->begin(); + iter != getSelection()->end(); ) + { + LLObjectSelection::iterator curiter = iter++; + LLViewerObject* object = (*curiter)->getObject(); + if (object->mID == id) + { + if (tool) + { + tool->stopEditing(); + } + + // lose the selection, don't tell simulator, it knows + deselectObjectAndFamily(object, FALSE); + object_found = TRUE; + break; // must break here, may have removed multiple objects from list + } + else if (object->isAvatar()) + { + // It's possible the item being removed has an avatar sitting on it + // So remove the avatar that is sitting on the object. + deselectObjectAndFamily(object, FALSE); + break; // must break here, may have removed multiple objects from list + } + } + } + + return object_found; +} + +void LLSelectMgr::deselectObjectAndFamily(LLViewerObject* object, BOOL send_to_sim, BOOL include_entire_object) +{ + // bail if nothing selected or if object wasn't selected in the first place + if(!object) return; + if(!object->isSelected()) return; + + // Collect all of the objects, and remove them + LLDynamicArray objects; + + if (include_entire_object) + { + // Since we're selecting a family, start at the root, but + // don't include an avatar. + LLViewerObject* root = object; + + while(!root->isAvatar() && root->getParent() && !root->isJointChild()) + { + LLViewerObject* parent = (LLViewerObject*)root->getParent(); + if (parent->isAvatar()) + { + break; + } + root = parent; + } + + object = root; + } + else + { + object = (LLViewerObject*)object->getRoot(); + } + + object->addThisAndAllChildren(objects); + remove(objects); + + if (!send_to_sim) return; + + //----------------------------------------------------------- + // Inform simulator of deselection + //----------------------------------------------------------- + LLViewerRegion* regionp = object->getRegion(); + + BOOL start_new_message = TRUE; + S32 select_count = 0; + + LLMessageSystem* msg = gMessageSystem; + for (U32 i = 0; i < objects.size(); i++) + { + if (start_new_message) + { + msg->newMessageFast(_PREHASH_ObjectDeselect); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + select_count++; + start_new_message = FALSE; + } + + msg->nextBlockFast(_PREHASH_ObjectData); + msg->addU32Fast(_PREHASH_ObjectLocalID, (objects[i])->getLocalID()); + select_count++; + + // Zap the angular velocity, as the sim will set it to zero + objects[i]->setAngularVelocity( 0,0,0 ); + objects[i]->setVelocity( 0,0,0 ); + + if(msg->isSendFull(NULL) || select_count >= MAX_OBJECTS_PER_PACKET) + { + msg->sendReliable(regionp->getHost() ); + select_count = 0; + start_new_message = TRUE; + } + } + + if (!start_new_message) + { + msg->sendReliable(regionp->getHost() ); + } + + updatePointAt(); + updateSelectionCenter(); +} + +void LLSelectMgr::deselectObjectOnly(LLViewerObject* object, BOOL send_to_sim) +{ + // bail if nothing selected or if object wasn't selected in the first place + if (!object) return; + if (!object->isSelected() ) return; + + // Zap the angular velocity, as the sim will set it to zero + object->setAngularVelocity( 0,0,0 ); + object->setVelocity( 0,0,0 ); + + if (send_to_sim) + { + LLViewerRegion* region = object->getRegion(); + gMessageSystem->newMessageFast(_PREHASH_ObjectDeselect); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID() ); + gMessageSystem->sendReliable(region->getHost()); + } + + // This will refresh dialogs. + remove( object ); + + updatePointAt(); + updateSelectionCenter(); +} + + +//----------------------------------------------------------------------------- +// addAsFamily +//----------------------------------------------------------------------------- + +void LLSelectMgr::addAsFamily(std::vector& objects, BOOL add_to_end) +{ + for (std::vector::iterator iter = objects.begin(); + iter != objects.end(); ++iter) + { + LLViewerObject* objectp = *iter; + + // Can't select yourself + if (objectp->mID == gAgentID + && !gAllowSelectAvatar) + { + continue; + } + + if (!objectp->isSelected()) + { + LLSelectNode *nodep = new LLSelectNode(objectp, TRUE); + if (add_to_end) + { + mSelectedObjects->addNodeAtEnd(nodep); + } + else + { + mSelectedObjects->addNode(nodep); + } + objectp->setSelected(TRUE); + + if (objectp->getNumTEs() > 0) + { + nodep->selectAllTEs(TRUE); + } + else + { + // object has no faces, so don't mess with faces + } + } + else + { + // we want this object to be selected for real + // so clear transient flag + LLSelectNode* select_node = mSelectedObjects->findNode(objectp); + if (select_node) + { + select_node->setTransient(FALSE); + } + } + } + saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); +} + +//----------------------------------------------------------------------------- +// addAsIndividual() - a single object, face, etc +//----------------------------------------------------------------------------- +void LLSelectMgr::addAsIndividual(LLViewerObject *objectp, S32 face, BOOL undoable) +{ + // check to see if object is already in list + LLSelectNode *nodep = mSelectedObjects->findNode(objectp); + + // if not in list, add it + if (!nodep) + { + nodep = new LLSelectNode(objectp, TRUE); + mSelectedObjects->addNode(nodep); + llassert_always(nodep->getObject()); + } + else + { + // make this a full-fledged selection + nodep->setTransient(FALSE); + // Move it to the front of the list + mSelectedObjects->moveNodeToFront(nodep); + } + + // Make sure the object is tagged as selected + objectp->setSelected( TRUE ); + + // And make sure we don't consider it as part of a family + nodep->mIndividualSelection = TRUE; + + // Handle face selection + if (objectp->getNumTEs() <= 0) + { + // object has no faces, so don't do anything + } + else if (face == SELECT_ALL_TES) + { + nodep->selectAllTEs(TRUE); + } + else if (0 <= face && face < SELECT_MAX_TES) + { + nodep->selectTE(face, TRUE); + } + else + { + llerrs << "LLSelectMgr::add face " << face << " out-of-range" << llendl; + return; + } + + saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); + updateSelectionCenter(); + dialog_refresh_all(); +} + + +LLObjectSelectionHandle LLSelectMgr::setHoverObject(LLViewerObject *objectp, S32 face) +{ + // Always blitz hover list when setting + mHoverObjects->deleteAllNodes(); + + if (!objectp) + { + return NULL; + } + + // Can't select yourself + if (objectp->mID == gAgentID) + { + return NULL; + } + + // Can't select land + if (objectp->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH) + { + return NULL; + } + + // Collect all of the objects + LLDynamicArray objects; + objectp = objectp->getRootEdit(); + objectp->addThisAndNonJointChildren(objects); + + for (std::vector::iterator iter = objects.begin(); + iter != objects.end(); ++iter) + { + LLViewerObject* cur_objectp = *iter; + LLSelectNode* nodep = new LLSelectNode(cur_objectp, FALSE); + nodep->selectTE(face, TRUE); + mHoverObjects->addNodeAtEnd(nodep); + } + + requestObjectPropertiesFamily(objectp); + return mHoverObjects; +} + +LLSelectNode *LLSelectMgr::getHoverNode() +{ + return getHoverObjects()->getFirstRootNode(); +} + +void LLSelectMgr::highlightObjectOnly(LLViewerObject* objectp) +{ + if (!objectp) + { + return; + } + + if (objectp->getPCode() != LL_PCODE_VOLUME) + { + return; + } + + if ((gSavedSettings.getBOOL("SelectOwnedOnly") && !objectp->permYouOwner()) || + (gSavedSettings.getBOOL("SelectMovableOnly") && !objectp->permMove())) + { + // only select my own objects + return; + } + + mRectSelectedObjects.insert(objectp); +} + +void LLSelectMgr::highlightObjectAndFamily(LLViewerObject* objectp) +{ + if (!objectp) + { + return; + } + + LLViewerObject* root_obj = (LLViewerObject*)objectp->getRoot(); + + highlightObjectOnly(root_obj); + + LLViewerObject::const_child_list_t& child_list = root_obj->getChildren(); + for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); + iter != child_list.end(); iter++) + { + LLViewerObject* child = *iter; + highlightObjectOnly(child); + } +} + +// Note that this ignores the "select owned only" flag +// It's also more efficient than calling the single-object version over and over. +void LLSelectMgr::highlightObjectAndFamily(const std::vector& objects) +{ + for (std::vector::const_iterator iter1 = objects.begin(); + iter1 != objects.end(); ++iter1) + { + LLViewerObject* object = *iter1; + + if (!object) + { + continue; + } + if (object->getPCode() != LL_PCODE_VOLUME) + { + continue; + } + + LLViewerObject* root = (LLViewerObject*)object->getRoot(); + mRectSelectedObjects.insert(root); + + LLViewerObject::const_child_list_t& child_list = root->getChildren(); + for (LLViewerObject::child_list_t::const_iterator iter2 = child_list.begin(); + iter2 != child_list.end(); iter2++) + { + LLViewerObject* child = *iter2; + mRectSelectedObjects.insert(child); + } + } +} + +void LLSelectMgr::unhighlightObjectOnly(LLViewerObject* objectp) +{ + if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME)) + { + return; + } + + mRectSelectedObjects.erase(objectp); +} + +void LLSelectMgr::unhighlightObjectAndFamily(LLViewerObject* objectp) +{ + if (!objectp) + { + return; + } + + LLViewerObject* root_obj = (LLViewerObject*)objectp->getRoot(); + + unhighlightObjectOnly(root_obj); + + LLViewerObject::const_child_list_t& child_list = root_obj->getChildren(); + for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); + iter != child_list.end(); iter++) + { + LLViewerObject* child = *iter; + unhighlightObjectOnly(child); + } +} + + +void LLSelectMgr::unhighlightAll() +{ + mRectSelectedObjects.clear(); + mHighlightedObjects->deleteAllNodes(); +} + +LLObjectSelectionHandle LLSelectMgr::selectHighlightedObjects() +{ + if (!mHighlightedObjects->getNumNodes()) + { + return NULL; + } + + //clear primary object + mSelectedObjects->mPrimaryObject = NULL; + + for (LLObjectSelection::iterator iter = getHighlightedObjects()->begin(); + iter != getHighlightedObjects()->end(); ) + { + LLObjectSelection::iterator curiter = iter++; + + LLSelectNode *nodep = *curiter; + LLViewerObject* objectp = nodep->getObject(); + + if (!canSelectObject(objectp)) + { + continue; + } + + // already selected + if (objectp->isSelected()) + { + continue; + } + + LLSelectNode* new_nodep = new LLSelectNode(*nodep); + mSelectedObjects->addNode(new_nodep); + + // flag this object as selected + objectp->setSelected(TRUE); + + mSelectedObjects->mSelectType = getSelectTypeForObject(objectp); + + // request properties on root objects + if (objectp->isRootEdit()) + { + requestObjectPropertiesFamily(objectp); + } + } + + // pack up messages to let sim know these objects are selected + sendSelect(); + unhighlightAll(); + updateSelectionCenter(); + saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); + updatePointAt(); + + if (mSelectedObjects->getObjectCount()) + { + gEditMenuHandler = this; + } + + return mSelectedObjects; +} + +void LLSelectMgr::deselectHighlightedObjects() +{ + BOOL select_linked_set = !gSavedSettings.getBOOL("EditLinkedParts"); + for (std::set >::iterator iter = mRectSelectedObjects.begin(); + iter != mRectSelectedObjects.end(); iter++) + { + LLViewerObject *objectp = *iter; + if (!select_linked_set) + { + deselectObjectOnly(objectp); + } + else + { + LLViewerObject* root_object = (LLViewerObject*)objectp->getRoot(); + if (root_object->isSelected()) + { + deselectObjectAndFamily(root_object); + } + } + } + + unhighlightAll(); +} + +void LLSelectMgr::addGridObject(LLViewerObject* objectp) +{ + LLSelectNode* nodep = new LLSelectNode(objectp, FALSE); + mGridObjects.addNodeAtEnd(nodep); + + LLViewerObject::const_child_list_t& child_list = objectp->getChildren(); + for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); + iter != child_list.end(); iter++) + { + LLViewerObject* child = *iter; + nodep = new LLSelectNode(child, FALSE); + mGridObjects.addNodeAtEnd(nodep); + } +} + +void LLSelectMgr::clearGridObjects() +{ + mGridObjects.deleteAllNodes(); +} + +void LLSelectMgr::setGridMode(EGridMode mode) +{ + mGridMode = mode; + gSavedSettings.setS32("GridMode", mode); + updateSelectionCenter(); + mGridValid = FALSE; +} + +void LLSelectMgr::getGrid(LLVector3& origin, LLQuaternion &rotation, LLVector3 &scale) +{ + mGridObjects.cleanupNodes(); + + LLViewerObject* first_grid_object = mGridObjects.getFirstObject(); + + if (mGridMode == GRID_MODE_LOCAL && mSelectedObjects->getObjectCount()) + { + //LLViewerObject* root = getSelectedParentObject(mSelectedObjects->getFirstObject()); + LLBBox bbox = mSavedSelectionBBox; + mGridOrigin = mSavedSelectionBBox.getCenterAgent(); + mGridScale = mSavedSelectionBBox.getExtentLocal() * 0.5f; + + // DEV-12570 Just taking the saved selection box rotation prevents + // wild rotations of linked sets while in local grid mode + //if(mSelectedObjects->getObjectCount() < 2 || !root || root->mDrawable.isNull()) + { + mGridRotation = mSavedSelectionBBox.getRotation(); + } + /*else //set to the root object + { + mGridRotation = root->getRenderRotation(); + }*/ + } + else if (mGridMode == GRID_MODE_REF_OBJECT && first_grid_object && first_grid_object->mDrawable.notNull()) + { + mGridRotation = first_grid_object->getRenderRotation(); + LLVector3 first_grid_obj_pos = first_grid_object->getRenderPosition(); + + LLVector3 min_extents(F32_MAX, F32_MAX, F32_MAX); + LLVector3 max_extents(-F32_MAX, -F32_MAX, -F32_MAX); + BOOL grid_changed = FALSE; + for (LLObjectSelection::iterator iter = mGridObjects.begin(); + iter != mGridObjects.end(); ++iter) + { + LLViewerObject* object = (*iter)->getObject(); + LLDrawable* drawable = object->mDrawable; + if (drawable) + { + const LLVector3* ext = drawable->getSpatialExtents(); + update_min_max(min_extents, max_extents, ext[0]); + update_min_max(min_extents, max_extents, ext[1]); + grid_changed = TRUE; + } + } + if (grid_changed) + { + mGridOrigin = lerp(min_extents, max_extents, 0.5f); + LLDrawable* drawable = first_grid_object->mDrawable; + if (drawable && drawable->isActive()) + { + mGridOrigin = mGridOrigin * first_grid_object->getRenderMatrix(); + } + mGridScale = (max_extents - min_extents) * 0.5f; + } + } + else // GRID_MODE_WORLD or just plain default + { + const BOOL non_root_ok = TRUE; + LLViewerObject* first_object = mSelectedObjects->getFirstRootObject(non_root_ok); + + mGridOrigin.clearVec(); + mGridRotation.loadIdentity(); + + mSelectedObjects->mSelectType = getSelectTypeForObject( first_object ); + + switch (mSelectedObjects->mSelectType) + { + case SELECT_TYPE_ATTACHMENT: + if (first_object && first_object->getRootEdit()->mDrawable.notNull()) + { + // this means this object *has* to be an attachment + LLXform* attachment_point_xform = first_object->getRootEdit()->mDrawable->mXform.getParent(); + mGridOrigin = attachment_point_xform->getWorldPosition(); + mGridRotation = attachment_point_xform->getWorldRotation(); + mGridScale = LLVector3(1.f, 1.f, 1.f) * gSavedSettings.getF32("GridResolution"); + } + break; + case SELECT_TYPE_HUD: + // use HUD-scaled grid + mGridScale = LLVector3(0.25f, 0.25f, 0.25f); + break; + case SELECT_TYPE_WORLD: + mGridScale = LLVector3(1.f, 1.f, 1.f) * gSavedSettings.getF32("GridResolution"); + break; + } + } + llassert(mGridOrigin.isFinite()); + + origin = mGridOrigin; + rotation = mGridRotation; + scale = mGridScale; + mGridValid = TRUE; +} + +//----------------------------------------------------------------------------- +// remove() - an array of objects +//----------------------------------------------------------------------------- + +void LLSelectMgr::remove(std::vector& objects) +{ + for (std::vector::iterator iter = objects.begin(); + iter != objects.end(); ++iter) + { + LLViewerObject* objectp = *iter; + LLSelectNode* nodep = mSelectedObjects->findNode(objectp); + if (nodep) + { + objectp->setSelected(FALSE); + mSelectedObjects->removeNode(nodep); + nodep = NULL; + } + } + updateSelectionCenter(); + dialog_refresh_all(); +} + + +//----------------------------------------------------------------------------- +// remove() - a single object +//----------------------------------------------------------------------------- +void LLSelectMgr::remove(LLViewerObject *objectp, S32 te, BOOL undoable) +{ + // get object node (and verify it is in the selected list) + LLSelectNode *nodep = mSelectedObjects->findNode(objectp); + if (!nodep) + { + return; + } + + // if face = all, remove object from list + if ((objectp->getNumTEs() <= 0) || (te == SELECT_ALL_TES)) + { + // Remove all faces (or the object doesn't have faces) so remove the node + mSelectedObjects->removeNode(nodep); + nodep = NULL; + objectp->setSelected( FALSE ); + } + else if (0 <= te && te < SELECT_MAX_TES) + { + // ...valid face, check to see if it was on + if (nodep->isTESelected(te)) + { + nodep->selectTE(te, FALSE); + } + else + { + llerrs << "LLSelectMgr::remove - tried to remove TE " << te << " that wasn't selected" << llendl; + return; + } + + // ...check to see if this operation turned off all faces + BOOL found = FALSE; + for (S32 i = 0; i < nodep->getObject()->getNumTEs(); i++) + { + found = found || nodep->isTESelected(i); + } + + // ...all faces now turned off, so remove + if (!found) + { + mSelectedObjects->removeNode(nodep); + nodep = NULL; + objectp->setSelected( FALSE ); + // *FIXME: Doesn't update simulator that object is no longer selected + } + } + else + { + // ...out of range face + llerrs << "LLSelectMgr::remove - TE " << te << " out of range" << llendl; + } + + updateSelectionCenter(); + dialog_refresh_all(); +} + + +//----------------------------------------------------------------------------- +// removeAll() +//----------------------------------------------------------------------------- +void LLSelectMgr::removeAll() +{ + for (LLObjectSelection::iterator iter = mSelectedObjects->begin(); + iter != mSelectedObjects->end(); iter++ ) + { + LLViewerObject *objectp = (*iter)->getObject(); + objectp->setSelected( FALSE ); + } + + mSelectedObjects->deleteAllNodes(); + + updateSelectionCenter(); + dialog_refresh_all(); +} + +//----------------------------------------------------------------------------- +// promoteSelectionToRoot() +//----------------------------------------------------------------------------- +void LLSelectMgr::promoteSelectionToRoot() +{ + std::set selection_set; + + BOOL selection_changed = FALSE; + + for (LLObjectSelection::iterator iter = getSelection()->begin(); + iter != getSelection()->end(); ) + { + LLObjectSelection::iterator curiter = iter++; + LLSelectNode* nodep = *curiter; + LLViewerObject* object = nodep->getObject(); + + if (nodep->mIndividualSelection) + { + selection_changed = TRUE; + } + + LLViewerObject* parentp = object; + while(parentp->getParent() && !(parentp->isRootEdit() || parentp->isJointChild())) + { + parentp = (LLViewerObject*)parentp->getParent(); + } + + selection_set.insert(parentp); + } + + if (selection_changed) + { + deselectAll(); + + std::set::iterator set_iter; + for (set_iter = selection_set.begin(); set_iter != selection_set.end(); ++set_iter) + { + selectObjectAndFamily(*set_iter); + } + } +} + +//----------------------------------------------------------------------------- +// demoteSelectionToIndividuals() +//----------------------------------------------------------------------------- +void LLSelectMgr::demoteSelectionToIndividuals() +{ + LLDynamicArray objects; + + for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); + iter != getSelection()->root_end(); iter++) + { + LLViewerObject* object = (*iter)->getObject(); + object->addThisAndNonJointChildren(objects); + } + + if (objects.getLength()) + { + deselectAll(); + for (std::vector::iterator iter = objects.begin(); + iter != objects.end(); ++iter) + { + LLViewerObject* objectp = *iter; + selectObjectOnly(objectp); + } + } +} + +//----------------------------------------------------------------------------- +// dump() +//----------------------------------------------------------------------------- +void LLSelectMgr::dump() +{ + llinfos << "Selection Manager: " << mSelectedObjects->getNumNodes() << " items" << llendl; + + llinfos << "TE mode " << mTEMode << llendl; + + S32 count = 0; + for (LLObjectSelection::iterator iter = getSelection()->begin(); + iter != getSelection()->end(); iter++ ) + { + LLViewerObject* objectp = (*iter)->getObject(); + llinfos << "Object " << count << " type " << LLPrimitive::pCodeToString(objectp->getPCode()) << llendl; + llinfos << " hasLSL " << objectp->flagScripted() << llendl; + llinfos << " hasTouch " << objectp->flagHandleTouch() << llendl; + llinfos << " hasMoney " << objectp->flagTakesMoney() << llendl; + llinfos << " getposition " << objectp->getPosition() << llendl; + llinfos << " getpositionAgent " << objectp->getPositionAgent() << llendl; + llinfos << " getpositionRegion " << objectp->getPositionRegion() << llendl; + llinfos << " getpositionGlobal " << objectp->getPositionGlobal() << llendl; + LLDrawable* drawablep = objectp->mDrawable; + llinfos << " " << (drawablep&& drawablep->isVisible() ? "visible" : "invisible") << llendl; + llinfos << " " << (drawablep&& drawablep->isState(LLDrawable::FORCE_INVISIBLE) ? "force_invisible" : "") << llendl; + count++; + } + + // Face iterator + for (LLObjectSelection::iterator iter = getSelection()->begin(); + iter != getSelection()->end(); iter++ ) + { + LLSelectNode* node = *iter; + LLViewerObject* objectp = node->getObject(); + if (!objectp) + continue; + for (S32 te = 0; te < objectp->getNumTEs(); ++te ) + { + if (node->isTESelected(te)) + { + llinfos << "Object " << objectp << " te " << te << llendl; + } + } + } + + llinfos << mHighlightedObjects->getNumNodes() << " objects currently highlighted." << llendl; + + llinfos << "Center global " << mSelectionCenterGlobal << llendl; +} + +//----------------------------------------------------------------------------- +// cleanup() +//----------------------------------------------------------------------------- +void LLSelectMgr::cleanup() +{ + mSilhouetteImagep = NULL; +} + + +//--------------------------------------------------------------------------- +// Manipulate properties of selected objects +//--------------------------------------------------------------------------- + +struct LLSelectMgrSendFunctor : public LLSelectedObjectFunctor +{ + virtual bool apply(LLViewerObject* object) + { + if (object->permModify()) + { + object->sendTEUpdate(); + } + return true; + } +}; + +//----------------------------------------------------------------------------- +// selectionSetImage() +//----------------------------------------------------------------------------- +// *TODO: re-arch texture applying out of lltooldraganddrop +void LLSelectMgr::selectionSetImage(const LLUUID& imageid) +{ + // First for (no copy) textures and multiple object selection + LLViewerInventoryItem* item = gInventory.getItem(imageid); + // fffff + /* + // + if(item + && !item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID()) + && (mSelectedObjects->getNumNodes() > 1) ) + { + llwarns << "Attempted to apply no-copy texture to multiple objects" + << llendl; + return; + } + // + */ + // + + struct f : public LLSelectedTEFunctor + { + LLViewerInventoryItem* mItem; + LLUUID mImageID; + f(LLViewerInventoryItem* item, const LLUUID& id) : mItem(item), mImageID(id) {} + bool apply(LLViewerObject* objectp, S32 te) + { + if (mItem) + { + if (te == -1) // all faces + { + LLToolDragAndDrop::dropTextureAllFaces(objectp, + mItem, + LLToolDragAndDrop::SOURCE_AGENT, + LLUUID::null); + } + else // one face + { + LLToolDragAndDrop::dropTextureOneFace(objectp, + te, + mItem, + LLToolDragAndDrop::SOURCE_AGENT, + LLUUID::null); + } + } + else // not an inventory item + { + // Texture picker defaults aren't inventory items + // * Don't need to worry about permissions for them + // * Can just apply the texture and be done with it. + objectp->setTEImage(te, gImageList.getImage(mImageID, TRUE, FALSE)); + } + return true; + } + } setfunc(item, imageid); + getSelection()->applyToTEs(&setfunc); + + struct g : public LLSelectedObjectFunctor + { + LLViewerInventoryItem* mItem; + g(LLViewerInventoryItem* item) : mItem(item) {} + virtual bool apply(LLViewerObject* object) + { + if (!mItem) + { + object->sendTEUpdate(); + // 1 particle effect per object + // + if(!gSavedSettings.getBOOL("DisablePointAtAndBeam")) + { + // + LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE); + effectp->setSourceObject(gAgent.getAvatarObject()); + effectp->setTargetObject(object); + effectp->setDuration(LL_HUD_DUR_SHORT); + effectp->setColor(LLColor4U(gAgent.getEffectColor())); + // + } + // + } + return true; + } + } sendfunc(item); + getSelection()->applyToObjects(&sendfunc); +} + +//----------------------------------------------------------------------------- +// selectionSetColor() +//----------------------------------------------------------------------------- +void LLSelectMgr::selectionSetColor(const LLColor4 &color) +{ + struct f : public LLSelectedTEFunctor + { + LLColor4 mColor; + f(const LLColor4& c) : mColor(c) {} + bool apply(LLViewerObject* object, S32 te) + { + if (object->permModify()) + { + object->setTEColor(te, mColor); + } + return true; + } + } setfunc(color); + getSelection()->applyToTEs(&setfunc); + + LLSelectMgrSendFunctor sendfunc; + getSelection()->applyToObjects(&sendfunc); +} + +//----------------------------------------------------------------------------- +// selectionSetColorOnly() +//----------------------------------------------------------------------------- +void LLSelectMgr::selectionSetColorOnly(const LLColor4 &color) +{ + struct f : public LLSelectedTEFunctor + { + LLColor4 mColor; + f(const LLColor4& c) : mColor(c) {} + bool apply(LLViewerObject* object, S32 te) + { + if (object->permModify()) + { + LLColor4 prev_color = object->getTE(te)->getColor(); + mColor.mV[VALPHA] = prev_color.mV[VALPHA]; + // update viewer side color in anticipation of update from simulator + object->setTEColor(te, mColor); + } + return true; + } + } setfunc(color); + getSelection()->applyToTEs(&setfunc); + + LLSelectMgrSendFunctor sendfunc; + getSelection()->applyToObjects(&sendfunc); +} + +//----------------------------------------------------------------------------- +// selectionSetAlphaOnly() +//----------------------------------------------------------------------------- +void LLSelectMgr::selectionSetAlphaOnly(const F32 alpha) +{ + struct f : public LLSelectedTEFunctor + { + F32 mAlpha; + f(const F32& a) : mAlpha(a) {} + bool apply(LLViewerObject* object, S32 te) + { + if (object->permModify()) + { + LLColor4 prev_color = object->getTE(te)->getColor(); + prev_color.mV[VALPHA] = mAlpha; + // update viewer side color in anticipation of update from simulator + object->setTEColor(te, prev_color); + } + return true; + } + } setfunc(alpha); + getSelection()->applyToTEs(&setfunc); + + LLSelectMgrSendFunctor sendfunc; + getSelection()->applyToObjects(&sendfunc); +} + +void LLSelectMgr::selectionRevertColors() +{ + struct f : public LLSelectedTEFunctor + { + LLObjectSelectionHandle mSelectedObjects; + f(LLObjectSelectionHandle sel) : mSelectedObjects(sel) {} + bool apply(LLViewerObject* object, S32 te) + { + if (object->permModify()) + { + LLSelectNode* nodep = mSelectedObjects->findNode(object); + if (nodep && te < (S32)nodep->mSavedColors.size()) + { + LLColor4 color = nodep->mSavedColors[te]; + // update viewer side color in anticipation of update from simulator + object->setTEColor(te, color); + } + } + return true; + } + } setfunc(mSelectedObjects); + getSelection()->applyToTEs(&setfunc); + + LLSelectMgrSendFunctor sendfunc; + getSelection()->applyToObjects(&sendfunc); +} + +BOOL LLSelectMgr::selectionRevertTextures() +{ + struct f : public LLSelectedTEFunctor + { + LLObjectSelectionHandle mSelectedObjects; + f(LLObjectSelectionHandle sel) : mSelectedObjects(sel) {} + bool apply(LLViewerObject* object, S32 te) + { + if (object->permModify()) + { + LLSelectNode* nodep = mSelectedObjects->findNode(object); + if (nodep && te < (S32)nodep->mSavedTextures.size()) + { + LLUUID id = nodep->mSavedTextures[te]; + // update textures on viewer side + if (id.isNull()) + { + // this was probably a no-copy texture, leave image as-is + return FALSE; + } + else + { + object->setTEImage(te, gImageList.getImage(id)); + } + } + } + return true; + } + } setfunc(mSelectedObjects); + BOOL revert_successful = getSelection()->applyToTEs(&setfunc); + + LLSelectMgrSendFunctor sendfunc; + getSelection()->applyToObjects(&sendfunc); + + return revert_successful; +} + +void LLSelectMgr::selectionSetBumpmap(U8 bumpmap) +{ + struct f : public LLSelectedTEFunctor + { + U8 mBump; + f(const U8& b) : mBump(b) {} + bool apply(LLViewerObject* object, S32 te) + { + if (object->permModify()) + { + // update viewer side color in anticipation of update from simulator + object->setTEBumpmap(te, mBump); + } + return true; + } + } setfunc(bumpmap); + getSelection()->applyToTEs(&setfunc); + + LLSelectMgrSendFunctor sendfunc; + getSelection()->applyToObjects(&sendfunc); +} + +void LLSelectMgr::selectionSetTexGen(U8 texgen) +{ + struct f : public LLSelectedTEFunctor + { + U8 mTexgen; + f(const U8& t) : mTexgen(t) {} + bool apply(LLViewerObject* object, S32 te) + { + if (object->permModify()) + { + // update viewer side color in anticipation of update from simulator + object->setTETexGen(te, mTexgen); + } + return true; + } + } setfunc(texgen); + getSelection()->applyToTEs(&setfunc); + + LLSelectMgrSendFunctor sendfunc; + getSelection()->applyToObjects(&sendfunc); +} + + +void LLSelectMgr::selectionSetShiny(U8 shiny) +{ + struct f : public LLSelectedTEFunctor + { + U8 mShiny; + f(const U8& t) : mShiny(t) {} + bool apply(LLViewerObject* object, S32 te) + { + if (object->permModify()) + { + // update viewer side color in anticipation of update from simulator + object->setTEShiny(te, mShiny); + } + return true; + } + } setfunc(shiny); + getSelection()->applyToTEs(&setfunc); + + LLSelectMgrSendFunctor sendfunc; + getSelection()->applyToObjects(&sendfunc); +} + +void LLSelectMgr::selectionSetFullbright(U8 fullbright) +{ + struct f : public LLSelectedTEFunctor + { + U8 mFullbright; + f(const U8& t) : mFullbright(t) {} + bool apply(LLViewerObject* object, S32 te) + { + if (object->permModify()) + { + // update viewer side color in anticipation of update from simulator + object->setTEFullbright(te, mFullbright); + } + return true; + } + } setfunc(fullbright); + getSelection()->applyToTEs(&setfunc); + + struct g : public LLSelectedObjectFunctor + { + U8 mFullbright; + g(const U8& t) : mFullbright(t) {} + virtual bool apply(LLViewerObject* object) + { + if (object->permModify()) + { + object->sendTEUpdate(); + if (mFullbright) + { + U8 material = object->getMaterial(); + U8 mcode = material & LL_MCODE_MASK; + if (mcode == LL_MCODE_LIGHT) + { + mcode = LL_MCODE_GLASS; + material = (material & ~LL_MCODE_MASK) | mcode; + object->setMaterial(material); + object->sendMaterialUpdate(); + } + } + } + return true; + } + } sendfunc(fullbright); + getSelection()->applyToObjects(&sendfunc); +} + +void LLSelectMgr::selectionSetMediaTypeAndURL(U8 media_type, const std::string& media_url) +{ + U8 media_flags = LLTextureEntry::MF_NONE; + if (media_type == LLViewerObject::MEDIA_TYPE_WEB_PAGE) + { + media_flags = LLTextureEntry::MF_WEB_PAGE; + } + + struct f : public LLSelectedTEFunctor + { + U8 mMediaFlags; + f(const U8& t) : mMediaFlags(t) {} + bool apply(LLViewerObject* object, S32 te) + { + if (object->permModify()) + { + // update viewer side color in anticipation of update from simulator + object->setTEMediaFlags(te, mMediaFlags); + } + return true; + } + } setfunc(media_flags); + getSelection()->applyToTEs(&setfunc); + + struct g : public LLSelectedObjectFunctor + { + U8 media_type; + const std::string& media_url ; + g(U8 a, const std::string& b) : media_type(a), media_url(b) {} + virtual bool apply(LLViewerObject* object) + { + if (object->permModify()) + { + object->sendTEUpdate(); + object->setMediaType(media_type); + object->setMediaURL(media_url); + } + return true; + } + } sendfunc(media_type, media_url); + getSelection()->applyToObjects(&sendfunc); +} + +void LLSelectMgr::selectionSetGlow(F32 glow) +{ + struct f1 : public LLSelectedTEFunctor + { + F32 mGlow; + f1(F32 glow) : mGlow(glow) {}; + bool apply(LLViewerObject* object, S32 face) + { + if (object->permModify()) + { + // update viewer side color in anticipation of update from simulator + object->setTEGlow(face, mGlow); + } + return true; + } + } func1(glow); + mSelectedObjects->applyToTEs( &func1 ); + + struct f2 : public LLSelectedObjectFunctor + { + virtual bool apply(LLViewerObject* object) + { + if (object->permModify()) + { + object->sendTEUpdate(); + } + return true; + } + } func2; + mSelectedObjects->applyToObjects( &func2 ); +} + + +//----------------------------------------------------------------------------- +// findObjectPermissions() +//----------------------------------------------------------------------------- +LLPermissions* LLSelectMgr::findObjectPermissions(const LLViewerObject* object) +{ + for (LLObjectSelection::valid_iterator iter = getSelection()->valid_begin(); + iter != getSelection()->valid_end(); iter++ ) + { + LLSelectNode* nodep = *iter; + if (nodep->getObject() == object) + { + return nodep->mPermissions; + } + } + + return NULL; +} + + +//----------------------------------------------------------------------------- +// selectionGetGlow() +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectionGetGlow(F32 *glow) +{ + BOOL identical; + F32 lglow = 0.f; + struct f1 : public LLSelectedTEGetFunctor + { + F32 get(LLViewerObject* object, S32 face) + { + return object->getTE(face)->getGlow(); + } + } func; + identical = mSelectedObjects->getSelectedTEValue( &func, lglow ); + + *glow = lglow; + return identical; +} + +//----------------------------------------------------------------------------- +// selectionSetMaterial() +//----------------------------------------------------------------------------- +void LLSelectMgr::selectionSetMaterial(U8 material) +{ + struct f : public LLSelectedObjectFunctor + { + U8 mMaterial; + f(const U8& t) : mMaterial(t) {} + virtual bool apply(LLViewerObject* object) + { + if (object->permModify()) + { + U8 cur_material = object->getMaterial(); + U8 material = mMaterial | (cur_material & ~LL_MCODE_MASK); + object->setMaterial(material); + object->sendMaterialUpdate(); + } + return true; + } + } sendfunc(material); + getSelection()->applyToObjects(&sendfunc); +} + +// TRUE if all selected objects have this PCode +BOOL LLSelectMgr::selectionAllPCode(LLPCode code) +{ + struct f : public LLSelectedObjectFunctor + { + LLPCode mCode; + f(const LLPCode& t) : mCode(t) {} + virtual bool apply(LLViewerObject* object) + { + if (object->getPCode() != mCode) + { + return FALSE; + } + return true; + } + } func(code); + BOOL res = getSelection()->applyToObjects(&func); + return res; +} + +bool LLSelectMgr::selectionGetIncludeInSearch(bool* include_in_search_out) +{ + LLViewerObject *object = mSelectedObjects->getFirstRootObject(); + if (!object) return FALSE; + + bool include_in_search = object->getIncludeInSearch(); + + bool identical = true; + + for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); + iter != getSelection()->root_end(); iter++) + { + LLViewerObject* object = (*iter)->getObject(); + + if ( include_in_search != object->getIncludeInSearch()) + { + identical = false; + break; + } + } + + *include_in_search_out = include_in_search; + return identical; +} + +void LLSelectMgr::selectionSetIncludeInSearch(bool include_in_search) +{ + LLViewerObject* object = NULL; + for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); + iter != getSelection()->root_end(); iter++) + { + object = (*iter)->getObject(); + object->setIncludeInSearch(include_in_search); + } + sendListToRegions( + "ObjectIncludeInSearch", + packAgentAndSessionID, + packObjectIncludeInSearch, + &include_in_search, + SEND_ONLY_ROOTS); +} + +BOOL LLSelectMgr::selectionGetClickAction(U8 *out_action) +{ + LLViewerObject *object = mSelectedObjects->getFirstObject(); + if (!object) + { + return FALSE; + } + + U8 action = object->getClickAction(); + *out_action = action; + + struct f : public LLSelectedObjectFunctor + { + U8 mAction; + f(const U8& t) : mAction(t) {} + virtual bool apply(LLViewerObject* object) + { + if ( mAction != object->getClickAction()) + { + return false; + } + return true; + } + } func(action); + BOOL res = getSelection()->applyToObjects(&func); + return res; +} + +void LLSelectMgr::selectionSetClickAction(U8 action) +{ + struct f : public LLSelectedObjectFunctor + { + U8 mAction; + f(const U8& t) : mAction(t) {} + virtual bool apply(LLViewerObject* object) + { + object->setClickAction(mAction); + return true; + } + } func(action); + getSelection()->applyToObjects(&func); + + sendListToRegions("ObjectClickAction", + packAgentAndSessionID, + packObjectClickAction, + &action, + SEND_INDIVIDUALS); +} + + +//----------------------------------------------------------------------------- +// godlike requests +//----------------------------------------------------------------------------- + +typedef std::pair godlike_request_t; + +void LLSelectMgr::sendGodlikeRequest(const std::string& request, const std::string& param) +{ + // If the agent is neither godlike nor an estate owner, the server + // will reject the request. + std::string message_type; + if (gAgent.isGodlike()) + { + message_type = "GodlikeMessage"; + } + else + { + message_type = "EstateOwnerMessage"; + } + + godlike_request_t data(request, param); + if(!mSelectedObjects->getRootObjectCount()) + { + LLMessageSystem* msg = gMessageSystem; + msg->newMessage(message_type.c_str()); + LLSelectMgr::packGodlikeHead(&data); + gAgent.sendReliableMessage(); + } + else + { + sendListToRegions(message_type, packGodlikeHead, packObjectIDAsParam, &data, SEND_ONLY_ROOTS); + } +} + +void LLSelectMgr::packGodlikeHead(void* user_data) +{ + LLMessageSystem* msg = gMessageSystem; + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->addUUID("TransactionID", LLUUID::null); + godlike_request_t* data = (godlike_request_t*)user_data; + msg->nextBlock("MethodData"); + msg->addString("Method", data->first); + msg->addUUID("Invoice", LLUUID::null); + + // The parameters used to be restricted to either string or + // integer. This mimics that behavior under the new 'string-only' + // parameter list by not packing a string if there wasn't one + // specified. The object ids will be packed in the + // packObjectIDAsParam() method. + if(data->second.size() > 0) + { + msg->nextBlock("ParamList"); + msg->addString("Parameter", data->second); + } +} + +// static +void LLSelectMgr::packObjectIDAsParam(LLSelectNode* node, void *) +{ + std::string buf = llformat("%u", node->getObject()->getLocalID()); + gMessageSystem->nextBlock("ParamList"); + gMessageSystem->addString("Parameter", buf); +} + +//----------------------------------------------------------------------------- +// Rotation options +//----------------------------------------------------------------------------- +void LLSelectMgr::selectionResetRotation() +{ + struct f : public LLSelectedObjectFunctor + { + virtual bool apply(LLViewerObject* object) + { + LLQuaternion identity(0.f, 0.f, 0.f, 1.f); + object->setRotation(identity); + if (object->mDrawable.notNull()) + { + gPipeline.markMoved(object->mDrawable, TRUE); + } + object->sendRotationUpdate(); + return true; + } + } func; + getSelection()->applyToRootObjects(&func); +} + +void LLSelectMgr::selectionRotateAroundZ(F32 degrees) +{ + LLQuaternion rot( degrees * DEG_TO_RAD, LLVector3(0,0,1) ); + struct f : public LLSelectedObjectFunctor + { + LLQuaternion mRot; + f(const LLQuaternion& rot) : mRot(rot) {} + virtual bool apply(LLViewerObject* object) + { + object->setRotation( object->getRotationEdit() * mRot ); + if (object->mDrawable.notNull()) + { + gPipeline.markMoved(object->mDrawable, TRUE); + } + object->sendRotationUpdate(); + return true; + } + } func(rot); + getSelection()->applyToRootObjects(&func); +} + + +//----------------------------------------------------------------------------- +// selectionTexScaleAutofit() +//----------------------------------------------------------------------------- +void LLSelectMgr::selectionTexScaleAutofit(F32 repeats_per_meter) +{ + struct f : public LLSelectedTEFunctor + { + F32 mRepeatsPerMeter; + f(const F32& t) : mRepeatsPerMeter(t) {} + bool apply(LLViewerObject* object, S32 te) + { + + if (object->permModify()) + { + // Compute S,T to axis mapping + U32 s_axis, t_axis; + if (!LLPrimitive::getTESTAxes(te, &s_axis, &t_axis)) + { + return TRUE; + } + + F32 new_s = object->getScale().mV[s_axis] * mRepeatsPerMeter; + F32 new_t = object->getScale().mV[t_axis] * mRepeatsPerMeter; + + object->setTEScale(te, new_s, new_t); + } + return true; + } + } setfunc(repeats_per_meter); + getSelection()->applyToTEs(&setfunc); + + LLSelectMgrSendFunctor sendfunc; + getSelection()->applyToObjects(&sendfunc); +} + + + +// Called at the end of a scale operation, this adjusts the textures to attempt to +// maintain a constant repeats per meter. +// BUG: Only works for flex boxes. +//----------------------------------------------------------------------------- +// adjustTexturesByScale() +//----------------------------------------------------------------------------- +void LLSelectMgr::adjustTexturesByScale(BOOL send_to_sim, BOOL stretch) +{ + for (LLObjectSelection::iterator iter = getSelection()->begin(); + iter != getSelection()->end(); iter++) + { + LLSelectNode* selectNode = *iter; + LLViewerObject* object = selectNode->getObject(); + + if (!object) + { + continue; + } + + if (!object->permModify()) + { + continue; + } + + if (object->getNumTEs() == 0) + { + continue; + } + + BOOL send = FALSE; + + for (U8 te_num = 0; te_num < object->getNumTEs(); te_num++) + { + const LLTextureEntry* tep = object->getTE(te_num); + + BOOL planar = tep->getTexGen() == LLTextureEntry::TEX_GEN_PLANAR; + if (planar == stretch) + { + // Figure out how S,T changed with scale operation + U32 s_axis, t_axis; + if (!LLPrimitive::getTESTAxes(te_num, &s_axis, &t_axis)) + { + continue; + } + + LLVector3 scale_ratio = selectNode->mTextureScaleRatios[te_num]; + LLVector3 object_scale = object->getScale(); + + // Apply new scale to face + if (planar) + { + object->setTEScale(te_num, 1.f/object_scale.mV[s_axis]*scale_ratio.mV[s_axis], + 1.f/object_scale.mV[t_axis]*scale_ratio.mV[t_axis]); + } + else + { + object->setTEScale(te_num, scale_ratio.mV[s_axis]*object_scale.mV[s_axis], + scale_ratio.mV[t_axis]*object_scale.mV[t_axis]); + } + send = send_to_sim; + } + } + + if (send) + { + object->sendTEUpdate(); + } + } +} + +//----------------------------------------------------------------------------- +// selectGetAllRootsValid() +// Returns TRUE if the viewer has information on all selected objects +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetAllRootsValid() +{ + for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); + iter != getSelection()->root_end(); ++iter ) + { + LLSelectNode* node = *iter; + if( !node->mValid ) + { + return FALSE; + } + } + return TRUE; +} + + +//----------------------------------------------------------------------------- +// selectGetAllValid() +// Returns TRUE if the viewer has information on all selected objects +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetAllValid() +{ + for (LLObjectSelection::iterator iter = getSelection()->begin(); + iter != getSelection()->end(); ++iter ) + { + LLSelectNode* node = *iter; + if( !node->mValid ) + { + return FALSE; + } + } + return TRUE; +} + + +//----------------------------------------------------------------------------- +// selectGetModify() - return TRUE if current agent can modify all +// selected objects. +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetModify() +{ + for (LLObjectSelection::iterator iter = getSelection()->begin(); + iter != getSelection()->end(); iter++ ) + { + LLSelectNode* node = *iter; + LLViewerObject* object = node->getObject(); + if( !object || !node->mValid ) + { + return FALSE; + } + if( !object->permModify() ) + { + return FALSE; + } + } + return TRUE; +} + +//----------------------------------------------------------------------------- +// selectGetRootsModify() - return TRUE if current agent can modify all +// selected root objects. +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetRootsModify() +{ + for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); + iter != getSelection()->root_end(); iter++ ) + { + LLSelectNode* node = *iter; + LLViewerObject* object = node->getObject(); + if( !node->mValid ) + { + return FALSE; + } + if( !object->permModify() ) + { + return FALSE; + } + } + + return TRUE; +} + + +//----------------------------------------------------------------------------- +// selectGetRootsTransfer() - return TRUE if current agent can transfer all +// selected root objects. +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetRootsTransfer() +{ + for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); + iter != getSelection()->root_end(); iter++ ) + { + LLSelectNode* node = *iter; + LLViewerObject* object = node->getObject(); + if( !node->mValid ) + { + return FALSE; + } + if(!object->permTransfer()) + { + return FALSE; + } + } + return TRUE; +} + +//----------------------------------------------------------------------------- +// selectGetRootsCopy() - return TRUE if current agent can copy all +// selected root objects. +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetRootsCopy() +{ + for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); + iter != getSelection()->root_end(); iter++ ) + { + LLSelectNode* node = *iter; + LLViewerObject* object = node->getObject(); + if( !node->mValid ) + { + return FALSE; + } + if(!object->permCopy()) + { + return FALSE; + } + } + return TRUE; +} + +//----------------------------------------------------------------------------- +// selectGetCreator() +// Creator information only applies to root objects. +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetCreator(LLUUID& result_id, std::string& name) +{ + BOOL identical = TRUE; + BOOL first = TRUE; + LLUUID first_id; + for (LLObjectSelection::root_object_iterator iter = getSelection()->root_object_begin(); + iter != getSelection()->root_object_end(); iter++ ) + { + LLSelectNode* node = *iter; + if (!node->mValid) + { + return FALSE; + } + + if (first) + { + first_id = node->mPermissions->getCreator(); + first = FALSE; + } + else + { + if ( !(first_id == node->mPermissions->getCreator() ) ) + { + identical = FALSE; + break; + } + } + } + if (first_id.isNull()) + { + return FALSE; + } + + result_id = first_id; + + if (identical) + { + gCacheName->getFullName(first_id, name); + } + else + { + name.assign( "(multiple)" ); + } + + return identical; +} + + +//----------------------------------------------------------------------------- +// selectGetOwner() +// Owner information only applies to roots. +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetOwner(LLUUID& result_id, std::string& name) +{ + BOOL identical = TRUE; + BOOL first = TRUE; + BOOL first_group_owned = FALSE; + LLUUID first_id; + for (LLObjectSelection::root_object_iterator iter = getSelection()->root_object_begin(); + iter != getSelection()->root_object_end(); iter++ ) + { + LLSelectNode* node = *iter; + if (!node->mValid) + { + return FALSE; + } + + if (first) + { + node->mPermissions->getOwnership(first_id, first_group_owned); + first = FALSE; + } + else + { + LLUUID owner_id; + BOOL is_group_owned = FALSE; + if (!(node->mPermissions->getOwnership(owner_id, is_group_owned)) + || owner_id != first_id || is_group_owned != first_group_owned) + { + identical = FALSE; + break; + } + } + } + if (first_id.isNull()) + { + return FALSE; + } + + result_id = first_id; + + if (identical) + { + BOOL public_owner = (first_id.isNull() && !first_group_owned); + if (first_group_owned) + { + name.assign( "(Group Owned)"); + } + else if(!public_owner) + { + gCacheName->getFullName(first_id, name); + } + else + { + name.assign("Public"); + } + } + else + { + name.assign( "(multiple)" ); + } + + return identical; +} + + +//----------------------------------------------------------------------------- +// selectGetLastOwner() +// Owner information only applies to roots. +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetLastOwner(LLUUID& result_id, std::string& name) +{ + BOOL identical = TRUE; + BOOL first = TRUE; + LLUUID first_id; + for (LLObjectSelection::root_object_iterator iter = getSelection()->root_object_begin(); + iter != getSelection()->root_object_end(); iter++ ) + { + LLSelectNode* node = *iter; + if (!node->mValid) + { + return FALSE; + } + + if (first) + { + first_id = node->mPermissions->getLastOwner(); + first = FALSE; + } + else + { + if ( !(first_id == node->mPermissions->getLastOwner() ) ) + { + identical = FALSE; + break; + } + } + } + if (first_id.isNull()) + { + return FALSE; + } + + result_id = first_id; + + if (identical) + { + BOOL public_owner = (first_id.isNull()); + if(!public_owner) + { + gCacheName->getFullName(first_id, name); + } + else + { + name.assign("Public or Group"); + } + } + else + { + name.assign( "" ); + } + + return identical; +} + + +//----------------------------------------------------------------------------- +// selectGetGroup() +// Group information only applies to roots. +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetGroup(LLUUID& result_id) +{ + BOOL identical = TRUE; + BOOL first = TRUE; + LLUUID first_id; + for (LLObjectSelection::root_object_iterator iter = getSelection()->root_object_begin(); + iter != getSelection()->root_object_end(); iter++ ) + { + LLSelectNode* node = *iter; + if (!node->mValid) + { + return FALSE; + } + + if (first) + { + first_id = node->mPermissions->getGroup(); + first = FALSE; + } + else + { + if ( !(first_id == node->mPermissions->getGroup() ) ) + { + identical = FALSE; + break; + } + } + } + + result_id = first_id; + + return identical; +} + +//----------------------------------------------------------------------------- +// selectIsGroupOwned() +// Only operates on root nodes. +// Returns TRUE if all have valid data and they are all group owned. +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectIsGroupOwned() +{ + BOOL found_one = FALSE; + for (LLObjectSelection::root_object_iterator iter = getSelection()->root_object_begin(); + iter != getSelection()->root_object_end(); iter++ ) + { + LLSelectNode* node = *iter; + if (!node->mValid) + { + return FALSE; + } + found_one = TRUE; + if (!node->mPermissions->isGroupOwned()) + { + return FALSE; + } + } + return found_one ? TRUE : FALSE; +} + +//----------------------------------------------------------------------------- +// selectGetPerm() +// Only operates on root nodes. +// Returns TRUE if all have valid data. +// mask_on has bits set to TRUE where all permissions are TRUE +// mask_off has bits set to TRUE where all permissions are FALSE +// if a bit is off both in mask_on and mask_off, the values differ within +// the selection. +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetPerm(U8 which_perm, U32* mask_on, U32* mask_off) +{ + U32 mask; + U32 mask_and = 0xffffffff; + U32 mask_or = 0x00000000; + BOOL all_valid = FALSE; + + for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); + iter != getSelection()->root_end(); iter++) + { + LLSelectNode* node = *iter; + + if (!node->mValid) + { + all_valid = FALSE; + break; + } + + all_valid = TRUE; + + switch( which_perm ) + { + case PERM_BASE: + mask = node->mPermissions->getMaskBase(); + break; + case PERM_OWNER: + mask = node->mPermissions->getMaskOwner(); + break; + case PERM_GROUP: + mask = node->mPermissions->getMaskGroup(); + break; + case PERM_EVERYONE: + mask = node->mPermissions->getMaskEveryone(); + break; + case PERM_NEXT_OWNER: + mask = node->mPermissions->getMaskNextOwner(); + break; + default: + mask = 0x0; + break; + } + mask_and &= mask; + mask_or |= mask; + } + + if (all_valid) + { + // ...TRUE through all ANDs means all TRUE + *mask_on = mask_and; + + // ...FALSE through all ORs means all FALSE + *mask_off = ~mask_or; + return TRUE; + } + else + { + *mask_on = 0; + *mask_off = 0; + return FALSE; + } +} + + + +BOOL LLSelectMgr::selectGetOwnershipCost(S32* out_cost) +{ + return mSelectedObjects->getOwnershipCost(*out_cost); +} + +BOOL LLSelectMgr::selectGetPermissions(LLPermissions& result_perm) +{ + BOOL first = TRUE; + LLPermissions perm; + for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); + iter != getSelection()->root_end(); iter++ ) + { + LLSelectNode* node = *iter; + if (!node->mValid) + { + return FALSE; + } + + if (first) + { + perm = *(node->mPermissions); + first = FALSE; + } + else + { + perm.accumulate(*(node->mPermissions)); + } + } + + result_perm = perm; + + return TRUE; +} + + +void LLSelectMgr::selectDelete() +{ + S32 deleteable_count = 0; + + BOOL locked_but_deleteable_object = FALSE; + BOOL no_copy_but_deleteable_object = FALSE; + BOOL all_owned_by_you = TRUE; + + for (LLObjectSelection::iterator iter = getSelection()->begin(); + iter != getSelection()->end(); iter++) + { + LLViewerObject* obj = (*iter)->getObject(); + + if( obj->isAttachment() ) + { + continue; + } + + deleteable_count++; + + // Check to see if you can delete objects which are locked. + if(!obj->permMove()) + { + locked_but_deleteable_object = TRUE; + } + if(!obj->permCopy()) + { + no_copy_but_deleteable_object = TRUE; + } + if(!obj->permYouOwner()) + { + all_owned_by_you = FALSE; + } + } + + if( 0 == deleteable_count ) + { + make_ui_sound("UISndInvalidOp"); + return; + } + + LLNotification::Params params("ConfirmObjectDeleteLock"); + params.functor(boost::bind(&LLSelectMgr::confirmDelete, _1, _2, getSelection())); + + if(locked_but_deleteable_object || + no_copy_but_deleteable_object || + !all_owned_by_you) + { + // convert any transient pie-menu selections to full selection so this operation + // has some context + // NOTE: if user cancels delete operation, this will potentially leave objects selected outside of build mode + // but this is ok, if not ideal + convertTransient(); + + //This is messy, but needed to get all english our of the UI. + if(locked_but_deleteable_object && !no_copy_but_deleteable_object && all_owned_by_you) + { + //Locked only + params.name("ConfirmObjectDeleteLock"); + } + else if(!locked_but_deleteable_object && no_copy_but_deleteable_object && all_owned_by_you) + { + //No Copy only + params.name("ConfirmObjectDeleteNoCopy"); + } + else if(!locked_but_deleteable_object && !no_copy_but_deleteable_object && !all_owned_by_you) + { + //not owned only + params.name("ConfirmObjectDeleteNoOwn"); + } + else if(locked_but_deleteable_object && no_copy_but_deleteable_object && all_owned_by_you) + { + //locked and no copy + params.name("ConfirmObjectDeleteLockNoCopy"); + } + else if(locked_but_deleteable_object && !no_copy_but_deleteable_object && !all_owned_by_you) + { + //locked and not owned + params.name("ConfirmObjectDeleteLockNoOwn"); + } + else if(!locked_but_deleteable_object && no_copy_but_deleteable_object && !all_owned_by_you) + { + //no copy and not owned + params.name("ConfirmObjectDeleteNoCopyNoOwn"); + } + else + { + //locked, no copy and not owned + params.name("ConfirmObjectDeleteLockNoCopyNoOwn"); + } + + LLNotifications::instance().add(params); + } + else + { + LLNotifications::instance().forceResponse(params, 0); + } +} + +// static +bool LLSelectMgr::confirmDelete(const LLSD& notification, const LLSD& response, LLObjectSelectionHandle handle) +{ + S32 option = LLNotification::getSelectedOption(notification, response); + if (!handle->getObjectCount()) + { + llwarns << "Nothing to delete!" << llendl; + return false; + } + + switch(option) + { + case 0: + { + // TODO: Make sure you have delete permissions on all of them. + LLUUID trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH); + // attempt to derez into the trash. + LLDeRezInfo* info = new LLDeRezInfo(DRD_TRASH, trash_id); + LLSelectMgr::getInstance()->sendListToRegions("DeRezObject", + packDeRezHeader, + packObjectLocalID, + (void*)info, + SEND_ONLY_ROOTS); + // VEFFECT: Delete Object - one effect for all deletes + if(!gSavedSettings.getBOOL("DisablePointAtAndBeam")) + { + if (LLSelectMgr::getInstance()->mSelectedObjects->mSelectType != SELECT_TYPE_HUD) + { + LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); + effectp->setPositionGlobal( LLSelectMgr::getInstance()->getSelectionCenterGlobal() ); + effectp->setColor(LLColor4U(gAgent.getEffectColor())); + F32 duration = 0.5f; + duration += LLSelectMgr::getInstance()->mSelectedObjects->getObjectCount() / 64.f; + effectp->setDuration(duration); + } + } + + gAgent.setLookAt(LOOKAT_TARGET_CLEAR); + + // Keep track of how many objects have been deleted. + F64 obj_delete_count = LLViewerStats::getInstance()->getStat(LLViewerStats::ST_OBJECT_DELETE_COUNT); + obj_delete_count += LLSelectMgr::getInstance()->mSelectedObjects->getObjectCount(); + LLViewerStats::getInstance()->setStat(LLViewerStats::ST_OBJECT_DELETE_COUNT, obj_delete_count ); + } + break; + case 1: + default: + break; + } + return false; +} + + +void LLSelectMgr::selectForceDelete() +{ + sendListToRegions( + "ObjectDelete", + packDeleteHeader, + packObjectLocalID, + (void*)TRUE, + SEND_ONLY_ROOTS); +} + +void LLSelectMgr::selectGetAggregateSaleInfo(U32 &num_for_sale, + BOOL &is_for_sale_mixed, + BOOL &is_sale_price_mixed, + S32 &total_sale_price, + S32 &individual_sale_price) +{ + num_for_sale = 0; + is_for_sale_mixed = FALSE; + is_sale_price_mixed = FALSE; + total_sale_price = 0; + individual_sale_price = 0; + + + // Empty set. + if (getSelection()->root_begin() == getSelection()->root_end()) + return; + + LLSelectNode *node = *(getSelection()->root_begin()); + const BOOL first_node_for_sale = node->mSaleInfo.isForSale(); + const S32 first_node_sale_price = node->mSaleInfo.getSalePrice(); + + for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); + iter != getSelection()->root_end(); iter++) + { + LLSelectNode* node = *iter; + const BOOL node_for_sale = node->mSaleInfo.isForSale(); + const S32 node_sale_price = node->mSaleInfo.getSalePrice(); + + // Set mixed if the fields don't match the first node's fields. + if (node_for_sale != first_node_for_sale) + is_for_sale_mixed = TRUE; + if (node_sale_price != first_node_sale_price) + is_sale_price_mixed = TRUE; + + if (node_for_sale) + { + total_sale_price += node_sale_price; + num_for_sale ++; + } + } + + individual_sale_price = first_node_sale_price; + if (is_for_sale_mixed) + { + is_sale_price_mixed = TRUE; + individual_sale_price = 0; + } +} + +// returns TRUE if all nodes are valid. method also stores an +// accumulated sale info. +BOOL LLSelectMgr::selectGetSaleInfo(LLSaleInfo& result_sale_info) +{ + BOOL first = TRUE; + LLSaleInfo sale_info; + for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); + iter != getSelection()->root_end(); iter++ ) + { + LLSelectNode* node = *iter; + if (!node->mValid) + { + return FALSE; + } + + if (first) + { + sale_info = node->mSaleInfo; + first = FALSE; + } + else + { + sale_info.accumulate(node->mSaleInfo); + } + } + + result_sale_info = sale_info; + + return TRUE; +} + +BOOL LLSelectMgr::selectGetAggregatePermissions(LLAggregatePermissions& result_perm) +{ + BOOL first = TRUE; + LLAggregatePermissions perm; + for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); + iter != getSelection()->root_end(); iter++ ) + { + LLSelectNode* node = *iter; + if (!node->mValid) + { + return FALSE; + } + + if (first) + { + perm = node->mAggregatePerm; + first = FALSE; + } + else + { + perm.aggregate(node->mAggregatePerm); + } + } + + result_perm = perm; + + return TRUE; +} + +BOOL LLSelectMgr::selectGetAggregateTexturePermissions(LLAggregatePermissions& result_perm) +{ + BOOL first = TRUE; + LLAggregatePermissions perm; + for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); + iter != getSelection()->root_end(); iter++ ) + { + LLSelectNode* node = *iter; + if (!node->mValid) + { + return FALSE; + } + + LLAggregatePermissions t_perm = node->getObject()->permYouOwner() ? node->mAggregateTexturePermOwner : node->mAggregateTexturePerm; + if (first) + { + perm = t_perm; + first = FALSE; + } + else + { + perm.aggregate(t_perm); + } + } + + result_perm = perm; + + return TRUE; +} + + +//-------------------------------------------------------------------- +// Duplicate objects +//-------------------------------------------------------------------- + +// JC - If this doesn't work right, duplicate the selection list +// before doing anything, do a deselect, then send the duplicate +// messages. +struct LLDuplicateData +{ + LLVector3 offset; + U32 flags; +}; + +void LLSelectMgr::selectDuplicate(const LLVector3& offset, BOOL select_copy) +{ + if (mSelectedObjects->isAttachment()) + { + //RN: do not duplicate attachments + make_ui_sound("UISndInvalidOp"); + return; + } + LLDuplicateData data; + + data.offset = offset; + data.flags = (select_copy ? FLAGS_CREATE_SELECTED : 0x0); + + sendListToRegions("ObjectDuplicate", packDuplicateHeader, packDuplicate, &data, SEND_ONLY_ROOTS); + + if (select_copy) + { + // the new copy will be coming in selected + deselectAll(); + } + else + { + for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); + iter != getSelection()->root_end(); iter++ ) + { + LLSelectNode* node = *iter; + node->mDuplicated = TRUE; + node->mDuplicatePos = node->getObject()->getPositionGlobal(); + node->mDuplicateRot = node->getObject()->getRotation(); + } + } +} + +void LLSelectMgr::repeatDuplicate() +{ + if (mSelectedObjects->isAttachment()) + { + //RN: do not duplicate attachments + make_ui_sound("UISndInvalidOp"); + return; + } + + std::vector non_duplicated_objects; + + for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); + iter != getSelection()->root_end(); iter++ ) + { + LLSelectNode* node = *iter; + if (!node->mDuplicated) + { + non_duplicated_objects.push_back(node->getObject()); + } + } + + // make sure only previously duplicated objects are selected + for (std::vector::iterator iter = non_duplicated_objects.begin(); + iter != non_duplicated_objects.end(); ++iter) + { + LLViewerObject* objectp = *iter; + deselectObjectAndFamily(objectp); + } + + // duplicate objects in place + LLDuplicateData data; + + data.offset = LLVector3::zero; + data.flags = 0x0; + + sendListToRegions("ObjectDuplicate", packDuplicateHeader, packDuplicate, &data, SEND_ONLY_ROOTS); + + // move current selection based on delta from duplication position and update duplication position + for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); + iter != getSelection()->root_end(); iter++ ) + { + LLSelectNode* node = *iter; + if (node->mDuplicated) + { + LLQuaternion cur_rot = node->getObject()->getRotation(); + LLQuaternion rot_delta = (~node->mDuplicateRot * cur_rot); + LLQuaternion new_rot = cur_rot * rot_delta; + LLVector3d cur_pos = node->getObject()->getPositionGlobal(); + LLVector3d new_pos = cur_pos + ((cur_pos - node->mDuplicatePos) * rot_delta); + + node->mDuplicatePos = node->getObject()->getPositionGlobal(); + node->mDuplicateRot = node->getObject()->getRotation(); + node->getObject()->setPositionGlobal(new_pos); + node->getObject()->setRotation(new_rot); + } + } + + sendMultipleUpdate(UPD_ROTATION | UPD_POSITION); +} + +// static +void LLSelectMgr::packDuplicate( LLSelectNode* node, void *duplicate_data ) +{ + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID()); +} + + +//-------------------------------------------------------------------- +// Duplicate On Ray +//-------------------------------------------------------------------- + +// Duplicates the selected objects, but places the copy along a cast +// ray. +struct LLDuplicateOnRayData +{ + LLVector3 mRayStartRegion; + LLVector3 mRayEndRegion; + BOOL mBypassRaycast; + BOOL mRayEndIsIntersection; + LLUUID mRayTargetID; + BOOL mCopyCenters; + BOOL mCopyRotates; + U32 mFlags; +}; + +void LLSelectMgr::selectDuplicateOnRay(const LLVector3 &ray_start_region, + const LLVector3 &ray_end_region, + BOOL bypass_raycast, + BOOL ray_end_is_intersection, + const LLUUID &ray_target_id, + BOOL copy_centers, + BOOL copy_rotates, + BOOL select_copy) +{ + if (mSelectedObjects->isAttachment()) + { + // do not duplicate attachments + make_ui_sound("UISndInvalidOp"); + return; + } + + LLDuplicateOnRayData data; + + data.mRayStartRegion = ray_start_region; + data.mRayEndRegion = ray_end_region; + data.mBypassRaycast = bypass_raycast; + data.mRayEndIsIntersection = ray_end_is_intersection; + data.mRayTargetID = ray_target_id; + data.mCopyCenters = copy_centers; + data.mCopyRotates = copy_rotates; + data.mFlags = (select_copy ? FLAGS_CREATE_SELECTED : 0x0); + + sendListToRegions("ObjectDuplicateOnRay", + packDuplicateOnRayHead, packObjectLocalID, &data, SEND_ONLY_ROOTS); + + if (select_copy) + { + // the new copy will be coming in selected + deselectAll(); + } +} + +// static +void LLSelectMgr::packDuplicateOnRayHead(void *user_data) +{ + LLMessageSystem *msg = gMessageSystem; + LLDuplicateOnRayData *data = (LLDuplicateOnRayData *)user_data; + + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() ); + msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID() ); + msg->addVector3Fast(_PREHASH_RayStart, data->mRayStartRegion ); + msg->addVector3Fast(_PREHASH_RayEnd, data->mRayEndRegion ); + msg->addBOOLFast(_PREHASH_BypassRaycast, data->mBypassRaycast ); + msg->addBOOLFast(_PREHASH_RayEndIsIntersection, data->mRayEndIsIntersection ); + msg->addBOOLFast(_PREHASH_CopyCenters, data->mCopyCenters ); + msg->addBOOLFast(_PREHASH_CopyRotates, data->mCopyRotates ); + msg->addUUIDFast(_PREHASH_RayTargetID, data->mRayTargetID ); + msg->addU32Fast(_PREHASH_DuplicateFlags, data->mFlags ); +} + + + +//------------------------------------------------------------------------ +// Object position, scale, rotation update, all-in-one +//------------------------------------------------------------------------ + +void LLSelectMgr::sendMultipleUpdate(U32 type) +{ + if (type == UPD_NONE) return; + // send individual updates when selecting textures or individual objects + ESendType send_type = (!gSavedSettings.getBOOL("EditLinkedParts") && !getTEMode()) ? SEND_ONLY_ROOTS : SEND_ROOTS_FIRST; + if (send_type == SEND_ONLY_ROOTS) + { + // tell simulator to apply to whole linked sets + type |= UPD_LINKED_SETS; + } + + sendListToRegions( + "MultipleObjectUpdate", + packAgentAndSessionID, + packMultipleUpdate, + &type, + send_type); +} + +// static +void LLSelectMgr::packMultipleUpdate(LLSelectNode* node, void *user_data) +{ + LLViewerObject* object = node->getObject(); + U32 *type32 = (U32 *)user_data; + U8 type = (U8)*type32; + U8 data[256]; + + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID() ); + gMessageSystem->addU8Fast(_PREHASH_Type, type ); + + S32 offset = 0; + + // JC: You MUST pack the data in this order. The receiving + // routine process_multiple_update_message on simulator will + // extract them in this order. + + if (type & UPD_POSITION) + { + htonmemcpy(&data[offset], &(object->getPosition().mV), MVT_LLVector3, 12); + offset += 12; + } + if (type & UPD_ROTATION) + { + LLQuaternion quat = object->getRotation(); + LLVector3 vec = quat.packToVector3(); + htonmemcpy(&data[offset], &(vec.mV), MVT_LLQuaternion, 12); + offset += 12; + } + if (type & UPD_SCALE) + { + //llinfos << "Sending object scale " << object->getScale() << llendl; + htonmemcpy(&data[offset], &(object->getScale().mV), MVT_LLVector3, 12); + offset += 12; + } + gMessageSystem->addBinaryDataFast(_PREHASH_Data, data, offset); +} + +//------------------------------------------------------------------------ +// Ownership +//------------------------------------------------------------------------ +struct LLOwnerData +{ + LLUUID owner_id; + LLUUID group_id; + BOOL override; +}; + +void LLSelectMgr::sendOwner(const LLUUID& owner_id, + const LLUUID& group_id, + BOOL override) +{ + LLOwnerData data; + + data.owner_id = owner_id; + data.group_id = group_id; + data.override = override; + + sendListToRegions("ObjectOwner", packOwnerHead, packObjectLocalID, &data, SEND_ONLY_ROOTS); +} + +// static +void LLSelectMgr::packOwnerHead(void *user_data) +{ + LLOwnerData *data = (LLOwnerData *)user_data; + + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() ); + gMessageSystem->nextBlockFast(_PREHASH_HeaderData); + gMessageSystem->addBOOLFast(_PREHASH_Override, data->override); + gMessageSystem->addUUIDFast(_PREHASH_OwnerID, data->owner_id); + gMessageSystem->addUUIDFast(_PREHASH_GroupID, data->group_id); +} + +//------------------------------------------------------------------------ +// Group +//------------------------------------------------------------------------ + +void LLSelectMgr::sendGroup(const LLUUID& group_id) +{ + LLUUID local_group_id(group_id); + sendListToRegions("ObjectGroup", packAgentAndSessionAndGroupID, packObjectLocalID, &local_group_id, SEND_ONLY_ROOTS); +} + + +//------------------------------------------------------------------------ +// Buy +//------------------------------------------------------------------------ + +struct LLBuyData +{ + std::vector mObjectsSent; + LLUUID mCategoryID; + LLSaleInfo mSaleInfo; +}; + +// *NOTE: does not work for multiple object buy, which UI does not +// currently support sale info is used for verification only, if it +// doesn't match region info then sale is canceled Need to get sale +// info -as displayed in the UI- for every item. +void LLSelectMgr::sendBuy(const LLUUID& buyer_id, const LLUUID& category_id, const LLSaleInfo sale_info) +{ + LLBuyData buy; + buy.mCategoryID = category_id; + buy.mSaleInfo = sale_info; + sendListToRegions("ObjectBuy", packAgentGroupAndCatID, packBuyObjectIDs, &buy, SEND_ONLY_ROOTS); +} + +// static +void LLSelectMgr::packBuyObjectIDs(LLSelectNode* node, void* data) +{ + LLBuyData* buy = (LLBuyData*)data; + + LLViewerObject* object = node->getObject(); + if (std::find(buy->mObjectsSent.begin(), buy->mObjectsSent.end(), object) == buy->mObjectsSent.end()) + { + buy->mObjectsSent.push_back(object); + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID() ); + gMessageSystem->addU8Fast(_PREHASH_SaleType, buy->mSaleInfo.getSaleType()); + gMessageSystem->addS32Fast(_PREHASH_SalePrice, buy->mSaleInfo.getSalePrice()); + } +} + +//------------------------------------------------------------------------ +// Permissions +//------------------------------------------------------------------------ + +struct LLPermData +{ + U8 mField; + BOOL mSet; + U32 mMask; + BOOL mOverride; +}; + +// TODO: Make this able to fail elegantly. +void LLSelectMgr::selectionSetObjectPermissions(U8 field, + BOOL set, + U32 mask, + BOOL override) +{ + LLPermData data; + + data.mField = field; + data.mSet = set; + data.mMask = mask; + data.mOverride = override; + + sendListToRegions("ObjectPermissions", packPermissionsHead, packPermissions, &data, SEND_ONLY_ROOTS); +} + +void LLSelectMgr::packPermissionsHead(void* user_data) +{ + LLPermData* data = (LLPermData*)user_data; + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->nextBlockFast(_PREHASH_HeaderData); + gMessageSystem->addBOOLFast(_PREHASH_Override, data->mOverride); +} + + +// Now that you've added a bunch of objects, send a select message +// on the entire list for efficiency. +/* +void LLSelectMgr::sendSelect() +{ + llerrs << "Not implemented" << llendl; +} +*/ + +void LLSelectMgr::deselectAll() +{ + if (!mSelectedObjects->getNumNodes()) + { + return; + } + + // Zap the angular velocity, as the sim will set it to zero + for (LLObjectSelection::iterator iter = mSelectedObjects->begin(); + iter != mSelectedObjects->end(); iter++ ) + { + LLViewerObject *objectp = (*iter)->getObject(); + objectp->setAngularVelocity( 0,0,0 ); + objectp->setVelocity( 0,0,0 ); + } + + sendListToRegions( + "ObjectDeselect", + packAgentAndSessionID, + packObjectLocalID, + NULL, + SEND_INDIVIDUALS); + + removeAll(); + + mLastSentSelectionCenterGlobal.clearVec(); + + updatePointAt(); +} + +void LLSelectMgr::deselectAllForStandingUp() +{ + /* + This function is similar deselectAll() except for the first if statement + which was removed. This is needed as a workaround for DEV-2854 + */ + + // Zap the angular velocity, as the sim will set it to zero + for (LLObjectSelection::iterator iter = mSelectedObjects->begin(); + iter != mSelectedObjects->end(); iter++ ) + { + LLViewerObject *objectp = (*iter)->getObject(); + objectp->setAngularVelocity( 0,0,0 ); + objectp->setVelocity( 0,0,0 ); + } + + sendListToRegions( + "ObjectDeselect", + packAgentAndSessionID, + packObjectLocalID, + NULL, + SEND_INDIVIDUALS); + + removeAll(); + + mLastSentSelectionCenterGlobal.clearVec(); + + updatePointAt(); +} + +void LLSelectMgr::deselectUnused() +{ + // no more outstanding references to this selection + if (mSelectedObjects->getNumRefs() == 1) + { + deselectAll(); + } +} + +void LLSelectMgr::convertTransient() +{ + LLObjectSelection::iterator node_it; + for (node_it = mSelectedObjects->begin(); node_it != mSelectedObjects->end(); ++node_it) + { + LLSelectNode *nodep = *node_it; + nodep->setTransient(FALSE); + } +} + +void LLSelectMgr::deselectAllIfTooFar() +{ + if (mSelectedObjects->isEmpty() || mSelectedObjects->mSelectType == SELECT_TYPE_HUD) + { + return; + } + + // HACK: Don't deselect when we're navigating to rate an object's + // owner or creator. JC + if (gPieObject->getVisible() || gPieRate->getVisible() ) + { + return; + } + + LLVector3d selectionCenter = getSelectionCenterGlobal(); + if (gSavedSettings.getBOOL("LimitSelectDistance") + && (!mSelectedObjects->getPrimaryObject() || !mSelectedObjects->getPrimaryObject()->isAvatar()) + && !mSelectedObjects->isAttachment() + && !selectionCenter.isExactlyZero()) + { + F32 deselect_dist = gSavedSettings.getF32("MaxSelectDistance"); + F32 deselect_dist_sq = deselect_dist * deselect_dist; + + LLVector3d select_delta = gAgent.getPositionGlobal() - selectionCenter; + F32 select_dist_sq = (F32) select_delta.magVecSquared(); + + if (select_dist_sq > deselect_dist_sq) + { + if (gDebugSelectMgr) + { + llinfos << "Selection manager: auto-deselecting, select_dist = " << fsqrtf(select_dist_sq) << llendl; + llinfos << "agent pos global = " << gAgent.getPositionGlobal() << llendl; + llinfos << "selection pos global = " << selectionCenter << llendl; + } + + deselectAll(); + } + } +} + + +void LLSelectMgr::selectionSetObjectName(const std::string& name) +{ + // we only work correctly if 1 object is selected. + if(mSelectedObjects->getRootObjectCount() == 1) + { + sendListToRegions("ObjectName", + packAgentAndSessionID, + packObjectName, + (void*)(new std::string(name)), + SEND_ONLY_ROOTS); + } + else if(mSelectedObjects->getObjectCount() == 1) + { + sendListToRegions("ObjectName", + packAgentAndSessionID, + packObjectName, + (void*)(new std::string(name)), + SEND_INDIVIDUALS); + } +} + +void LLSelectMgr::selectionSetObjectDescription(const std::string& desc) +{ + // we only work correctly if 1 object is selected. + if(mSelectedObjects->getRootObjectCount() == 1) + { + sendListToRegions("ObjectDescription", + packAgentAndSessionID, + packObjectDescription, + (void*)(new std::string(desc)), + SEND_ONLY_ROOTS); + } + else if(mSelectedObjects->getObjectCount() == 1) + { + sendListToRegions("ObjectDescription", + packAgentAndSessionID, + packObjectDescription, + (void*)(new std::string(desc)), + SEND_INDIVIDUALS); + } +} + +void LLSelectMgr::selectionSetObjectCategory(const LLCategory& category) +{ + // for now, we only want to be able to set one root category at + // a time. + if(mSelectedObjects->getRootObjectCount() != 1) return; + sendListToRegions("ObjectCategory", + packAgentAndSessionID, + packObjectCategory, + (void*)(&category), + SEND_ONLY_ROOTS); +} + +void LLSelectMgr::selectionSetObjectSaleInfo(const LLSaleInfo& sale_info) +{ + sendListToRegions("ObjectSaleInfo", + packAgentAndSessionID, + packObjectSaleInfo, + (void*)(&sale_info), + SEND_ONLY_ROOTS); +} + +//---------------------------------------------------------------------- +// Attachments +//---------------------------------------------------------------------- + +void LLSelectMgr::sendAttach(U8 attachment_point) +{ + LLViewerObject* attach_object = mSelectedObjects->getFirstRootObject(); + + if (!attach_object || !gAgent.getAvatarObject() || mSelectedObjects->mSelectType != SELECT_TYPE_WORLD) + { + return; + } + + BOOL build_mode = LLToolMgr::getInstance()->inEdit(); + // Special case: Attach to default location for this object. + if (0 == attachment_point || + get_if_there(gAgent.getAvatarObject()->mAttachmentPoints, (S32)attachment_point, (LLViewerJointAttachment*)NULL)) + { + if (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", + packAgentIDAndSessionAndAttachment, + packObjectIDAndRotation, + &attachment_point, + SEND_ONLY_ROOTS ); + if (!build_mode) + { + deselectAll(); + } + } +} + +void LLSelectMgr::sendDetach() +{ + if (!mSelectedObjects->getNumNodes() || mSelectedObjects->mSelectType == SELECT_TYPE_WORLD) + { + return; + } + + sendListToRegions( + "ObjectDetach", + packAgentAndSessionID, + packObjectLocalID, + NULL, + SEND_ONLY_ROOTS ); +} + + +void LLSelectMgr::sendDropAttachment() +{ + if (!mSelectedObjects->getNumNodes() || mSelectedObjects->mSelectType == SELECT_TYPE_WORLD) + { + return; + } + + sendListToRegions( + "ObjectDrop", + packAgentAndSessionID, + packObjectLocalID, + NULL, + SEND_ONLY_ROOTS); +} + +//---------------------------------------------------------------------- +// Links +//---------------------------------------------------------------------- + +void LLSelectMgr::sendLink() +{ + if (!mSelectedObjects->getNumNodes()) + { + return; + } + + sendListToRegions( + "ObjectLink", + packAgentAndSessionID, + packObjectLocalID, + NULL, + SEND_ONLY_ROOTS); +} + +void LLSelectMgr::sendDelink() +{ + if (!mSelectedObjects->getNumNodes()) + { + return; + } + + // Delink needs to send individuals so you can unlink a single object from + // a linked set. + sendListToRegions( + "ObjectDelink", + packAgentAndSessionID, + packObjectLocalID, + NULL, + SEND_INDIVIDUALS); +} + + +//---------------------------------------------------------------------- +// Hinges +//---------------------------------------------------------------------- + +/* +void LLSelectMgr::sendHinge(U8 type) +{ + if (!mSelectedObjects->getNumNodes()) + { + return; + } + + sendListToRegions( + "ObjectHinge", + packHingeHead, + packObjectLocalID, + &type, + SEND_ONLY_ROOTS); +} + + +void LLSelectMgr::sendDehinge() +{ + if (!mSelectedObjects->getNumNodes()) + { + return; + } + + sendListToRegions( + "ObjectDehinge", + packAgentAndSessionID, + packObjectLocalID, + NULL, + SEND_ONLY_ROOTS); +}*/ + +void LLSelectMgr::sendSelect() +{ + if (!mSelectedObjects->getNumNodes()) + { + return; + } + + sendListToRegions( + "ObjectSelect", + packAgentAndSessionID, + packObjectLocalID, + NULL, + SEND_INDIVIDUALS); +} + +// static +void LLSelectMgr::packHingeHead(void *user_data) +{ + U8 *type = (U8 *)user_data; + + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() ); + gMessageSystem->nextBlockFast(_PREHASH_JointType); + gMessageSystem->addU8Fast(_PREHASH_Type, *type ); +} + + +void LLSelectMgr::selectionDump() +{ + struct f : public LLSelectedObjectFunctor + { + virtual bool apply(LLViewerObject* object) + { + object->dump(); + return true; + } + } func; + getSelection()->applyToObjects(&func); +} + +void LLSelectMgr::saveSelectedObjectColors() +{ + struct f : public LLSelectedNodeFunctor + { + virtual bool apply(LLSelectNode* node) + { + node->saveColors(); + return true; + } + } func; + getSelection()->applyToNodes(&func); +} + +void LLSelectMgr::saveSelectedObjectTextures() +{ + // invalidate current selection so we update saved textures + struct f : public LLSelectedNodeFunctor + { + virtual bool apply(LLSelectNode* node) + { + node->mValid = FALSE; + return true; + } + } func; + getSelection()->applyToNodes(&func); + + // request object properties message to get updated permissions data + sendSelect(); +} + + +// This routine should be called whenever a drag is initiated. +// also need to know to which simulator to send update message +void LLSelectMgr::saveSelectedObjectTransform(EActionType action_type) +{ + if (mSelectedObjects->isEmpty()) + { + // nothing selected, so nothing to save + return; + } + + struct f : public LLSelectedNodeFunctor + { + EActionType mActionType; + f(EActionType a) : mActionType(a) {} + virtual bool apply(LLSelectNode* selectNode) + { + LLViewerObject* object = selectNode->getObject(); + if (!object) + { + return true; // skip + } + selectNode->mSavedPositionLocal = object->getPosition(); + if (object->isAttachment()) + { + if (object->isRootEdit()) + { + LLXform* parent_xform = object->mDrawable->getXform()->getParent(); + if (parent_xform) + { + selectNode->mSavedPositionGlobal = gAgent.getPosGlobalFromAgent((object->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition()); + } + else + { + selectNode->mSavedPositionGlobal = object->getPositionGlobal(); + } + } + else + { + LLViewerObject* attachment_root = (LLViewerObject*)object->getParent(); + LLXform* parent_xform = attachment_root ? attachment_root->mDrawable->getXform()->getParent() : NULL; + if (parent_xform) + { + LLVector3 root_pos = (attachment_root->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition(); + LLQuaternion root_rot = (attachment_root->getRotation() * parent_xform->getWorldRotation()); + selectNode->mSavedPositionGlobal = gAgent.getPosGlobalFromAgent((object->getPosition() * root_rot) + root_pos); + } + else + { + selectNode->mSavedPositionGlobal = object->getPositionGlobal(); + } + } + selectNode->mSavedRotation = object->getRenderRotation(); + } + else + { + selectNode->mSavedPositionGlobal = object->getPositionGlobal(); + selectNode->mSavedRotation = object->getRotationRegion(); + } + + selectNode->mSavedScale = object->getScale(); + selectNode->saveTextureScaleRatios(); + return true; + } + } func(action_type); + getSelection()->applyToNodes(&func); + + mSavedSelectionBBox = getBBoxOfSelection(); +} + +struct LLSelectMgrApplyFlags : public LLSelectedObjectFunctor +{ + LLSelectMgrApplyFlags(U32 flags, BOOL state) : mFlags(flags), mState(state) {} + U32 mFlags; + BOOL mState; + virtual bool apply(LLViewerObject* object) + { + if ( object->permModify() && // preemptive permissions check + object->isRoot() && // don't send for child objects + !object->isJointChild()) + { + object->setFlags( mFlags, mState); + } + return true; + } +}; + +void LLSelectMgr::selectionUpdatePhysics(BOOL physics) +{ + LLSelectMgrApplyFlags func( FLAGS_USE_PHYSICS, physics); + getSelection()->applyToObjects(&func); +} + +void LLSelectMgr::selectionUpdateTemporary(BOOL is_temporary) +{ + LLSelectMgrApplyFlags func( FLAGS_TEMPORARY_ON_REZ, is_temporary); + getSelection()->applyToObjects(&func); +} + +void LLSelectMgr::selectionUpdatePhantom(BOOL is_phantom) +{ + LLSelectMgrApplyFlags func( FLAGS_PHANTOM, is_phantom); + getSelection()->applyToObjects(&func); +} + +void LLSelectMgr::selectionUpdateCastShadows(BOOL cast_shadows) +{ + LLSelectMgrApplyFlags func( FLAGS_CAST_SHADOWS, cast_shadows); + getSelection()->applyToObjects(&func); +} + + +//---------------------------------------------------------------------- +// Helpful packing functions for sendObjectMessage() +//---------------------------------------------------------------------- + +// static +void LLSelectMgr::packAgentIDAndSessionAndAttachment( void *user_data) +{ + U8 *attachment_point = (U8*)user_data; + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->addU8Fast(_PREHASH_AttachmentPoint, *attachment_point); +} + +// static +void LLSelectMgr::packAgentID( void *user_data) +{ + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); +} + +// static +void LLSelectMgr::packAgentAndSessionID(void* user_data) +{ + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); +} + +// static +void LLSelectMgr::packAgentAndGroupID(void* user_data) +{ + LLOwnerData *data = (LLOwnerData *)user_data; + + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, data->owner_id ); + gMessageSystem->addUUIDFast(_PREHASH_GroupID, data->group_id ); +} + +// static +void LLSelectMgr::packAgentAndSessionAndGroupID(void* user_data) +{ + LLUUID* group_idp = (LLUUID*) user_data; + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->addUUIDFast(_PREHASH_GroupID, *group_idp); +} + +// static +void LLSelectMgr::packDuplicateHeader(void* data) +{ + LLUUID group_id(gAgent.getGroupID()); + packAgentAndSessionAndGroupID(&group_id); + + LLDuplicateData* dup_data = (LLDuplicateData*) data; + + gMessageSystem->nextBlockFast(_PREHASH_SharedData); + gMessageSystem->addVector3Fast(_PREHASH_Offset, dup_data->offset); + gMessageSystem->addU32Fast(_PREHASH_DuplicateFlags, dup_data->flags); +} + +// static +void LLSelectMgr::packDeleteHeader(void* userdata) +{ + BOOL force = (BOOL)(intptr_t)userdata; + + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->addBOOLFast(_PREHASH_Force, force); +} + +// static +void LLSelectMgr::packAgentGroupAndCatID(void* user_data) +{ + LLBuyData* buy = (LLBuyData*)user_data; + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID()); + gMessageSystem->addUUIDFast(_PREHASH_CategoryID, buy->mCategoryID); +} + +//static +void LLSelectMgr::packDeRezHeader(void* user_data) +{ + LLDeRezInfo* info = (LLDeRezInfo*)user_data; + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->nextBlockFast(_PREHASH_AgentBlock); + gMessageSystem->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID()); + gMessageSystem->addU8Fast(_PREHASH_Destination, (U8)info->mDestination); + gMessageSystem->addUUIDFast(_PREHASH_DestinationID, info->mDestinationID); + LLUUID tid; + tid.generate(); + gMessageSystem->addUUIDFast(_PREHASH_TransactionID, tid); + const U8 PACKET = 1; + gMessageSystem->addU8Fast(_PREHASH_PacketCount, PACKET); + gMessageSystem->addU8Fast(_PREHASH_PacketNumber, PACKET); +} + +// static +void LLSelectMgr::packObjectID(LLSelectNode* node, void *user_data) +{ + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addUUIDFast(_PREHASH_ObjectID, node->getObject()->mID ); +} + +void LLSelectMgr::packObjectIDAndRotation(LLSelectNode* node, void *user_data) +{ + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID() ); + gMessageSystem->addQuatFast(_PREHASH_Rotation, node->getObject()->getRotation()); +} + +void LLSelectMgr::packObjectClickAction(LLSelectNode* node, void *user_data) +{ + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID() ); + gMessageSystem->addU8("ClickAction", node->getObject()->getClickAction()); +} + +void LLSelectMgr::packObjectIncludeInSearch(LLSelectNode* node, void *user_data) +{ + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID() ); + gMessageSystem->addBOOL("IncludeInSearch", node->getObject()->getIncludeInSearch()); +} + +// static +void LLSelectMgr::packObjectLocalID(LLSelectNode* node, void *) +{ + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID()); +} + +// static +void LLSelectMgr::packObjectName(LLSelectNode* node, void* user_data) +{ + const std::string* name = (const std::string*)user_data; + if(!name->empty()) + { + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_LocalID, node->getObject()->getLocalID()); + gMessageSystem->addStringFast(_PREHASH_Name, *name); + } + delete name; +} + +// static +void LLSelectMgr::packObjectDescription(LLSelectNode* node, void* user_data) +{ + const std::string* desc = (const std::string*)user_data; + if(!desc->empty()) + { + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_LocalID, node->getObject()->getLocalID()); + gMessageSystem->addStringFast(_PREHASH_Description, *desc); + } +} + +// static +void LLSelectMgr::packObjectCategory(LLSelectNode* node, void* user_data) +{ + LLCategory* category = (LLCategory*)user_data; + if(!category) return; + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_LocalID, node->getObject()->getLocalID()); + category->packMessage(gMessageSystem); +} + +// static +void LLSelectMgr::packObjectSaleInfo(LLSelectNode* node, void* user_data) +{ + LLSaleInfo* sale_info = (LLSaleInfo*)user_data; + if(!sale_info) return; + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_LocalID, node->getObject()->getLocalID()); + sale_info->packMessage(gMessageSystem); +} + +// static +void LLSelectMgr::packPhysics(LLSelectNode* node, void *user_data) +{ +} + +// static +void LLSelectMgr::packShape(LLSelectNode* node, void *user_data) +{ +} + +// static +void LLSelectMgr::packPermissions(LLSelectNode* node, void *user_data) +{ + LLPermData *data = (LLPermData *)user_data; + + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID()); + + gMessageSystem->addU8Fast(_PREHASH_Field, data->mField); + gMessageSystem->addBOOLFast(_PREHASH_Set, data->mSet); + gMessageSystem->addU32Fast(_PREHASH_Mask, data->mMask); +} + +// Utility function to send some information to every region containing +// an object on the selection list. We want to do this to reduce the total +// number of packets sent by the viewer. +void LLSelectMgr::sendListToRegions(const std::string& message_name, + void (*pack_header)(void *user_data), + void (*pack_body)(LLSelectNode* node, void *user_data), + void *user_data, + ESendType send_type) +{ + LLSelectNode* node; + LLViewerRegion* last_region; + LLViewerRegion* current_region; + + S32 objects_sent = 0; + S32 packets_sent = 0; + S32 objects_in_this_packet = 0; + + //clear update override data (allow next update through) + struct f : public LLSelectedNodeFunctor + { + virtual bool apply(LLSelectNode* node) + { + node->mLastPositionLocal.setVec(0,0,0); + node->mLastRotation = LLQuaternion(); + node->mLastScale.setVec(0,0,0); + return true; + } + } func; + getSelection()->applyToNodes(&func); + + std::queue nodes_to_send; + + struct push_all : public LLSelectedNodeFunctor + { + std::queue& nodes_to_send; + push_all(std::queue& n) : nodes_to_send(n) {} + virtual bool apply(LLSelectNode* node) + { + if (node->getObject()) + { + nodes_to_send.push(node); + } + return true; + } + }; + struct push_some : public LLSelectedNodeFunctor + { + std::queue& nodes_to_send; + bool mRoots; + push_some(std::queue& n, bool roots) : nodes_to_send(n), mRoots(roots) {} + virtual bool apply(LLSelectNode* node) + { + if (node->getObject()) + { + BOOL is_root = node->getObject()->isRootEdit(); + if ((mRoots && is_root) || (!mRoots && !is_root)) + { + nodes_to_send.push(node); + } + } + return true; + } + }; + struct push_all pushall(nodes_to_send); + struct push_some pushroots(nodes_to_send, TRUE); + struct push_some pushnonroots(nodes_to_send, FALSE); + + switch(send_type) + { + case SEND_ONLY_ROOTS: + if(message_name == "ObjectBuy") + getSelection()->applyToRootNodes(&pushroots); + else + getSelection()->applyToRootNodes(&pushall); + + break; + case SEND_INDIVIDUALS: + getSelection()->applyToNodes(&pushall); + break; + case SEND_ROOTS_FIRST: + // first roots... + getSelection()->applyToNodes(&pushroots); + // then children... + getSelection()->applyToNodes(&pushnonroots); + break; + case SEND_CHILDREN_FIRST: + // first children... + getSelection()->applyToNodes(&pushnonroots); + // then roots... + getSelection()->applyToNodes(&pushroots); + break; + + default: + llerrs << "Bad send type " << send_type << " passed to SendListToRegions()" << llendl; + } + + // bail if nothing selected + if (nodes_to_send.empty()) + { + return; + } + + node = nodes_to_send.front(); + nodes_to_send.pop(); + + // cache last region information + current_region = node->getObject()->getRegion(); + + // Start duplicate message + // CRO: this isn't + gMessageSystem->newMessage(message_name.c_str()); + (*pack_header)(user_data); + + // For each object + while (node != NULL) + { + // remember the last region, look up the current one + last_region = current_region; + current_region = node->getObject()->getRegion(); + + // if to same simulator and message not too big + if ((current_region == last_region) + && (! gMessageSystem->isSendFull(NULL)) + && (objects_in_this_packet < MAX_OBJECTS_PER_PACKET)) + { + // add another instance of the body of the data + (*pack_body)(node, user_data); + ++objects_sent; + ++objects_in_this_packet; + + // and on to the next object + if(nodes_to_send.empty()) + { + node = NULL; + } + else + { + node = nodes_to_send.front(); + nodes_to_send.pop(); + } + } + else + { + // otherwise send current message and start new one + gMessageSystem->sendReliable( last_region->getHost()); + packets_sent++; + objects_in_this_packet = 0; + + gMessageSystem->newMessage(message_name.c_str()); + (*pack_header)(user_data); + + // don't move to the next object, we still need to add the + // body data. + } + } + + // flush messages + if (gMessageSystem->getCurrentSendTotal() > 0) + { + gMessageSystem->sendReliable( current_region->getHost()); + packets_sent++; + } + else + { + gMessageSystem->clearMessage(); + } + + // llinfos << "sendListToRegions " << message_name << " obj " << objects_sent << " pkt " << packets_sent << llendl; +} + + +// +// Network communications +// + +void LLSelectMgr::requestObjectPropertiesFamily(LLViewerObject* object) +{ + // Remember that we asked the properties of this object. + sObjectPropertiesFamilyRequests.insert(object->mID); + //llinfos << "Registered an ObjectPropertiesFamily request for object " << object->mID << llendl; + + LLMessageSystem* msg = gMessageSystem; + + msg->newMessageFast(_PREHASH_RequestObjectPropertiesFamily); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_ObjectData); + msg->addU32Fast(_PREHASH_RequestFlags, 0x0 ); + msg->addUUIDFast(_PREHASH_ObjectID, object->mID ); + + LLViewerRegion* regionp = object->getRegion(); + msg->sendReliable( regionp->getHost() ); +} + + +// static +void LLSelectMgr::processObjectProperties(LLMessageSystem* msg, void** user_data) +{ + S32 i; + S32 count = msg->getNumberOfBlocksFast(_PREHASH_ObjectData); + for (i = 0; i < count; i++) + { + LLUUID id; + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, id, i); + + LLUUID creator_id; + LLUUID owner_id; + LLUUID group_id; + LLUUID last_owner_id; + U64 creation_date; + LLUUID extra_id; + U32 base_mask, owner_mask, group_mask, everyone_mask, next_owner_mask; + LLSaleInfo sale_info; + LLCategory category; + LLAggregatePermissions ag_perms; + LLAggregatePermissions ag_texture_perms; + LLAggregatePermissions ag_texture_perms_owner; + + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_CreatorID, creator_id, i); + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_OwnerID, owner_id, i); + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_GroupID, group_id, i); + msg->getU64Fast(_PREHASH_ObjectData, _PREHASH_CreationDate, creation_date, i); + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_BaseMask, base_mask, i); + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_OwnerMask, owner_mask, i); + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_GroupMask, group_mask, i); + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_EveryoneMask, everyone_mask, i); + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_NextOwnerMask, next_owner_mask, i); + sale_info.unpackMultiMessage(msg, _PREHASH_ObjectData, i); + + ag_perms.unpackMessage(msg, _PREHASH_ObjectData, _PREHASH_AggregatePerms, i); + ag_texture_perms.unpackMessage(msg, _PREHASH_ObjectData, _PREHASH_AggregatePermTextures, i); + ag_texture_perms_owner.unpackMessage(msg, _PREHASH_ObjectData, _PREHASH_AggregatePermTexturesOwner, i); + category.unpackMultiMessage(msg, _PREHASH_ObjectData, i); + + S16 inv_serial = 0; + msg->getS16Fast(_PREHASH_ObjectData, _PREHASH_InventorySerial, inv_serial, i); + + LLUUID item_id; + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ItemID, item_id, i); + LLUUID folder_id; + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_FolderID, folder_id, i); + LLUUID from_task_id; + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_FromTaskID, from_task_id, i); + + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_LastOwnerID, last_owner_id, i); + + std::string name; + msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Name, name, i); + std::string desc; + msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Description, desc, i); + + std::string touch_name; + msg->getStringFast(_PREHASH_ObjectData, _PREHASH_TouchName, touch_name, i); + std::string sit_name; + msg->getStringFast(_PREHASH_ObjectData, _PREHASH_SitName, sit_name, i); + + //unpack TE IDs + std::vector texture_ids; + S32 size = msg->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_TextureID); + if (size > 0) + { + S8 packed_buffer[SELECT_MAX_TES * UUID_BYTES]; + msg->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextureID, packed_buffer, 0, i, SELECT_MAX_TES * UUID_BYTES); + + for (S32 buf_offset = 0; buf_offset < size; buf_offset += UUID_BYTES) + { + LLUUID tid; + memcpy(tid.mData, packed_buffer + buf_offset, UUID_BYTES); /* Flawfinder: ignore */ + texture_ids.push_back(tid); + } + } + + // Send to export floaters + LLFloaterExport::receiveObjectProperties(id, name, desc); + // + + // Iterate through nodes at end, since it can be on both the regular AND hover list + struct f : public LLSelectedNodeFunctor + { + LLUUID mID; + f(const LLUUID& id) : mID(id) {} + virtual bool apply(LLSelectNode* node) + { + return (node->getObject() && node->getObject()->mID == mID); + } + } func(id); + LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode(&func); + + if (node) + { + if (node->mInventorySerial != inv_serial) + { + node->getObject()->dirtyInventory(); + } + + // save texture data as soon as we get texture perms first time + if (!node->mValid) + { + BOOL can_copy = FALSE; + BOOL can_transfer = FALSE; + + LLAggregatePermissions::EValue value = LLAggregatePermissions::AP_NONE; + if(node->getObject()->permYouOwner()) + { + value = ag_texture_perms_owner.getValue(PERM_COPY); + if (value == LLAggregatePermissions::AP_EMPTY || value == LLAggregatePermissions::AP_ALL) + { + can_copy = TRUE; + } + value = ag_texture_perms_owner.getValue(PERM_TRANSFER); + if (value == LLAggregatePermissions::AP_EMPTY || value == LLAggregatePermissions::AP_ALL) + { + can_transfer = TRUE; + } + } + else + { + value = ag_texture_perms.getValue(PERM_COPY); + if (value == LLAggregatePermissions::AP_EMPTY || value == LLAggregatePermissions::AP_ALL) + { + can_copy = TRUE; + } + value = ag_texture_perms.getValue(PERM_TRANSFER); + if (value == LLAggregatePermissions::AP_EMPTY || value == LLAggregatePermissions::AP_ALL) + { + can_transfer = TRUE; + } + } + + if (can_copy && can_transfer) + { + // this should be the only place that saved textures is called + node->saveTextures(texture_ids); + } + } + + node->mValid = TRUE; + node->mPermissions->init(creator_id, owner_id, + last_owner_id, group_id); + node->mPermissions->initMasks(base_mask, owner_mask, everyone_mask, group_mask, next_owner_mask); + node->mCreationDate = creation_date; + node->mItemID = item_id; + node->mFolderID = folder_id; + node->mFromTaskID = from_task_id; + node->mName.assign(name); + node->mDescription.assign(desc); + node->mSaleInfo = sale_info; + node->mAggregatePerm = ag_perms; + node->mAggregateTexturePerm = ag_texture_perms; + node->mAggregateTexturePermOwner = ag_texture_perms_owner; + node->mCategory = category; + node->mInventorySerial = inv_serial; + node->mSitName.assign(sit_name); + node->mTouchName.assign(touch_name); + } + } + + dialog_refresh_all(); + + // silly hack to allow 'save into inventory' + if(gPopupMenuView->getVisible()) + { + gPopupMenuView->setItemEnabled(SAVE_INTO_INVENTORY, + enable_save_into_inventory(NULL)); + } + + // hack for left-click buy object + LLToolPie::selectionPropertiesReceived(); +} + +// static +void LLSelectMgr::processObjectPropertiesFamily(LLMessageSystem* msg, void** user_data) +{ + LLUUID id; + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, id); + if (sObjectPropertiesFamilyRequests.count(id) == 0) + { + // This reply is not for us. + return; + } + // We got the reply, so remove the object from the list of pending requests + sObjectPropertiesFamilyRequests.erase(id); + //llinfos << "Got ObjectPropertiesFamily reply for object " << id << llendl; + + U32 request_flags; + LLUUID creator_id; + LLUUID owner_id; + LLUUID group_id; + LLUUID extra_id; + U32 base_mask, owner_mask, group_mask, everyone_mask, next_owner_mask; + LLSaleInfo sale_info; + LLCategory category; + + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_RequestFlags, request_flags ); + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_OwnerID, owner_id ); + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_GroupID, group_id ); + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_BaseMask, base_mask ); + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_OwnerMask, owner_mask ); + msg->getU32Fast(_PREHASH_ObjectData,_PREHASH_GroupMask, group_mask ); + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_EveryoneMask, everyone_mask ); + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_NextOwnerMask, next_owner_mask); + sale_info.unpackMessage(msg, _PREHASH_ObjectData); + category.unpackMessage(msg, _PREHASH_ObjectData); + + LLUUID last_owner_id; + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_LastOwnerID, last_owner_id ); + + // unpack name & desc + std::string name; + msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Name, name); + + std::string desc; + msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Description, desc); + + // Send to export floaters + LLFloaterExport::receiveObjectProperties(id, name, desc); + // + // the reporter widget askes the server for info about picked objects + if (request_flags & (COMPLAINT_REPORT_REQUEST | BUG_REPORT_REQUEST)) + { + EReportType report_type = (COMPLAINT_REPORT_REQUEST & request_flags) ? COMPLAINT_REPORT : BUG_REPORT; + LLFloaterReporter *reporterp = LLFloaterReporter::getReporter(report_type); + if (reporterp) + { + std::string fullname; + gCacheName->getFullName(owner_id, fullname); + reporterp->setPickedObjectProperties(name, fullname, owner_id); + } + } + else if (request_flags & OBJECT_PAY_REQUEST) + { + // check if the owner of the paid object is muted + LLMuteList::getInstance()->autoRemove(owner_id, LLMuteList::AR_MONEY); + } + + // Now look through all of the hovered nodes + struct f : public LLSelectedNodeFunctor + { + LLUUID mID; + f(const LLUUID& id) : mID(id) {} + virtual bool apply(LLSelectNode* node) + { + return (node->getObject() && node->getObject()->mID == mID); + } + } func(id); + LLSelectNode* node = LLSelectMgr::getInstance()->getHoverObjects()->getFirstNode(&func); + + if (node) + { + node->mValid = TRUE; + node->mPermissions->init(LLUUID::null, owner_id, + last_owner_id, group_id); + node->mPermissions->initMasks(base_mask, owner_mask, everyone_mask, group_mask, next_owner_mask); + node->mSaleInfo = sale_info; + node->mCategory = category; + node->mName.assign(name); + node->mDescription.assign(desc); + } + + dialog_refresh_all(); +} + + +// static +void LLSelectMgr::processForceObjectSelect(LLMessageSystem* msg, void**) +{ + BOOL reset_list; + msg->getBOOL("Header", "ResetList", reset_list); + + if (reset_list) + { + LLSelectMgr::getInstance()->deselectAll(); + } + + LLUUID full_id; + S32 local_id; + LLViewerObject* object; + std::vector objects; + S32 i; + S32 block_count = msg->getNumberOfBlocks("Data"); + + for (i = 0; i < block_count; i++) + { + msg->getS32("Data", "LocalID", local_id, i); + + gObjectList.getUUIDFromLocal(full_id, + local_id, + msg->getSenderIP(), + msg->getSenderPort()); + object = gObjectList.findObject(full_id); + if (object) + { + objects.push_back(object); + } + } + + // Don't select, just highlight + LLSelectMgr::getInstance()->highlightObjectAndFamily(objects); +} + + +extern LLGLdouble gGLModelView[16]; + +void LLSelectMgr::updateSilhouettes() +{ + S32 num_sils_genned = 0; + + LLVector3d cameraPos = gAgent.getCameraPositionGlobal(); + F32 currentCameraZoom = gAgent.getCurrentCameraBuildOffset(); + + if (!mSilhouetteImagep) + { + mSilhouetteImagep = gImageList.getImageFromFile("silhouette.j2c", TRUE, TRUE); + } + + mHighlightedObjects->cleanupNodes(); + + if((cameraPos - mLastCameraPos).magVecSquared() > SILHOUETTE_UPDATE_THRESHOLD_SQUARED * currentCameraZoom * currentCameraZoom) + { + struct f : public LLSelectedObjectFunctor + { + virtual bool apply(LLViewerObject* object) + { + object->setChanged(LLXform::SILHOUETTE); + return true; + } + } func; + getSelection()->applyToObjects(&func); + + mLastCameraPos = gAgent.getCameraPositionGlobal(); + } + + std::vector changed_objects; + + updateSelectionSilhouette(mSelectedObjects, num_sils_genned, changed_objects); + if (mRectSelectedObjects.size() > 0) + { + //gGLSPipelineSelection.set(); + + //mSilhouetteImagep->bindTexture(); + //glAlphaFunc(GL_GREATER, sHighlightAlphaTest); + + std::set roots; + + // sync mHighlightedObjects with mRectSelectedObjects since the latter is rebuilt every frame and former + // persists from frame to frame to avoid regenerating object silhouettes + // mHighlightedObjects includes all siblings of rect selected objects + + BOOL select_linked_set = !gSavedSettings.getBOOL("EditLinkedParts"); + + // generate list of roots from current object selection + for (std::set >::iterator iter = mRectSelectedObjects.begin(); + iter != mRectSelectedObjects.end(); iter++) + { + LLViewerObject *objectp = *iter; + if (select_linked_set) + { + LLViewerObject *rootp = (LLViewerObject*)objectp->getRoot(); + roots.insert(rootp); + } + else + { + roots.insert(objectp); + } + } + + // remove highlight nodes not in roots list + std::vector remove_these_nodes; + std::vector remove_these_roots; + + for (LLObjectSelection::iterator iter = mHighlightedObjects->begin(); + iter != mHighlightedObjects->end(); iter++) + { + LLSelectNode* node = *iter; + LLViewerObject* objectp = node->getObject(); + if (!objectp) + continue; + if (objectp->isRoot() || !select_linked_set) + { + if (roots.count(objectp) == 0) + { + remove_these_nodes.push_back(node); + } + else + { + remove_these_roots.push_back(objectp); + } + } + else + { + LLViewerObject* rootp = (LLViewerObject*)objectp->getRoot(); + + if (roots.count(rootp) == 0) + { + remove_these_nodes.push_back(node); + } + } + } + + // remove all highlight nodes no longer in rectangle selection + for (std::vector::iterator iter = remove_these_nodes.begin(); + iter != remove_these_nodes.end(); ++iter) + { + LLSelectNode* nodep = *iter; + mHighlightedObjects->removeNode(nodep); + } + + // remove all root objects already being highlighted + for (std::vector::iterator iter = remove_these_roots.begin(); + iter != remove_these_roots.end(); ++iter) + { + LLViewerObject* objectp = *iter; + roots.erase(objectp); + } + + // add all new objects in rectangle selection + for (std::set::iterator iter = roots.begin(); + iter != roots.end(); iter++) + { + LLViewerObject* objectp = *iter; + if (!canSelectObject(objectp)) + { + continue; + } + + LLSelectNode* rect_select_root_node = new LLSelectNode(objectp, TRUE); + rect_select_root_node->selectAllTEs(TRUE); + + if (!select_linked_set) + { + rect_select_root_node->mIndividualSelection = TRUE; + } + else + { + LLViewerObject::const_child_list_t& child_list = objectp->getChildren(); + for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); + iter != child_list.end(); iter++) + { + LLViewerObject* child_objectp = *iter; + + if (!canSelectObject(child_objectp)) + { + continue; + } + + LLSelectNode* rect_select_node = new LLSelectNode(child_objectp, TRUE); + rect_select_node->selectAllTEs(TRUE); + mHighlightedObjects->addNodeAtEnd(rect_select_node); + } + } + + // Add the root last, to preserve order for link operations. + mHighlightedObjects->addNodeAtEnd(rect_select_root_node); + } + + num_sils_genned = 0; + + // render silhouettes for highlighted objects + //BOOL subtracting_from_selection = (gKeyboard->currentMask(TRUE) == MASK_CONTROL); + for (S32 pass = 0; pass < 2; pass++) + { + for (LLObjectSelection::iterator iter = mHighlightedObjects->begin(); + iter != mHighlightedObjects->end(); iter++) + { + LLSelectNode* node = *iter; + LLViewerObject* objectp = node->getObject(); + if (!objectp) + continue; + + // do roots first, then children so that root flags are cleared ASAP + BOOL roots_only = (pass == 0); + BOOL is_root = objectp->isRootEdit(); + if (roots_only != is_root) + { + continue; + } + + if (!node->mSilhouetteExists + || objectp->isChanged(LLXform::SILHOUETTE) + || (objectp->getParent() && objectp->getParent()->isChanged(LLXform::SILHOUETTE))) + { + if (num_sils_genned++ < MAX_SILS_PER_FRAME) + { + generateSilhouette(node, LLViewerCamera::getInstance()->getOrigin()); + changed_objects.push_back(objectp); + } + else if (objectp->isAttachment() && objectp->getRootEdit()->mDrawable.notNull()) + { + //RN: hack for orthogonal projection of HUD attachments + LLViewerJointAttachment* attachment_pt = (LLViewerJointAttachment*)objectp->getRootEdit()->mDrawable->getParent(); + if (attachment_pt && attachment_pt->getIsHUDAttachment()) + { + LLVector3 camera_pos = LLVector3(-10000.f, 0.f, 0.f); + generateSilhouette(node, camera_pos); + } + } + } + //LLColor4 highlight_color; + // + //if (subtracting_from_selection) + //{ + // node->renderOneSilhouette(LLColor4::red); + //} + //else if (!objectp->isSelected()) + //{ + // highlight_color = objectp->isRoot() ? sHighlightParentColor : sHighlightChildColor; + // node->renderOneSilhouette(highlight_color); + //} + } + } + //mSilhouetteImagep->unbindTexture(0, GL_TEXTURE_2D); + } + else + { + mHighlightedObjects->deleteAllNodes(); + } + + for (std::vector::iterator iter = changed_objects.begin(); + iter != changed_objects.end(); ++iter) + { + // clear flags after traversing node list (as child objects need to refer to parent flags, etc) + LLViewerObject* objectp = *iter; + objectp->clearChanged(LLXform::MOVED | LLXform::SILHOUETTE); + } + + //gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); +} + +void LLSelectMgr::updateSelectionSilhouette(LLObjectSelectionHandle object_handle, S32& num_sils_genned, std::vector& changed_objects) +{ + if (object_handle->getNumNodes()) + { + //gGLSPipelineSelection.set(); + + //mSilhouetteImagep->bindTexture(); + //glAlphaFunc(GL_GREATER, sHighlightAlphaTest); + + for (S32 pass = 0; pass < 2; pass++) + { + for (LLObjectSelection::iterator iter = object_handle->begin(); + iter != object_handle->end(); iter++) + { + LLSelectNode* node = *iter; + LLViewerObject* objectp = node->getObject(); + if (!objectp) + continue; + // do roots first, then children so that root flags are cleared ASAP + BOOL roots_only = (pass == 0); + BOOL is_root = (objectp->isRootEdit()); + if (roots_only != is_root || objectp->mDrawable.isNull()) + { + continue; + } + + if (!node->mSilhouetteExists + || objectp->isChanged(LLXform::SILHOUETTE) + || (objectp->getParent() && objectp->getParent()->isChanged(LLXform::SILHOUETTE))) + { + if (num_sils_genned++ < MAX_SILS_PER_FRAME)// && objectp->mDrawable->isVisible()) + { + generateSilhouette(node, LLViewerCamera::getInstance()->getOrigin()); + changed_objects.push_back(objectp); + } + else if (objectp->isAttachment()) + { + //RN: hack for orthogonal projection of HUD attachments + LLViewerJointAttachment* attachment_pt = (LLViewerJointAttachment*)objectp->getRootEdit()->mDrawable->getParent(); + if (attachment_pt && attachment_pt->getIsHUDAttachment()) + { + LLVector3 camera_pos = LLVector3(-10000.f, 0.f, 0.f); + generateSilhouette(node, camera_pos); + } + } + } + } + } + } +} +void LLSelectMgr::renderSilhouettes(BOOL for_hud) +{ + if (!mRenderSilhouettes || !LLSelectMgr::sRenderSelectionHighlights) + { + return; + } + + gGL.getTexUnit(0)->bind(mSilhouetteImagep.get()); + LLGLSPipelineSelection gls_select; + gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.f); + LLGLEnable blend(GL_BLEND); + LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); + + LLVOAvatar* avatar = gAgent.getAvatarObject(); + if (for_hud && avatar) + { + LLBBox hud_bbox = avatar->getHUDBBox(); + + F32 cur_zoom = gAgent.mHUDCurZoom; + + // set up transform to encompass bounding box of HUD + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + F32 depth = llmax(1.f, hud_bbox.getExtentLocal().mV[VX] * 1.1f); + glOrtho(-0.5f * LLViewerCamera::getInstance()->getAspect(), 0.5f * LLViewerCamera::getInstance()->getAspect(), -0.5f, 0.5f, 0.f, depth); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glLoadMatrixf(OGL_TO_CFR_ROTATION); // Load Cory's favorite reference frame + glTranslatef(-hud_bbox.getCenterLocal().mV[VX] + (depth *0.5f), 0.f, 0.f); + glScalef(cur_zoom, cur_zoom, cur_zoom); + } + if (mSelectedObjects->getNumNodes()) + { + LLUUID inspect_item_id = LLFloaterInspect::getSelectedUUID(); + + // + //for (S32 pass = 0; pass < 2; pass++) + //{ + // + for (LLObjectSelection::iterator iter = mSelectedObjects->begin(); + iter != mSelectedObjects->end(); iter++) + { + LLSelectNode* node = *iter; + LLViewerObject* objectp = node->getObject(); + if (!objectp) + continue; + if (objectp->isHUDAttachment() != for_hud) + { + continue; + } + if(objectp->getID() == inspect_item_id) + { + node->renderOneSilhouette(sHighlightInspectColor); + } + else if (node->isTransient()) + { + BOOL oldHidden = LLSelectMgr::sRenderHiddenSelections; + LLSelectMgr::sRenderHiddenSelections = FALSE; + node->renderOneSilhouette(sContextSilhouetteColor); + LLSelectMgr::sRenderHiddenSelections = oldHidden; + } + else if (objectp->isRootEdit()) + { + node->renderOneSilhouette(sSilhouetteParentColor); + } + else + { + node->renderOneSilhouette(sSilhouetteChildColor); + } + } + // + //} + // + } + + if (mHighlightedObjects->getNumNodes()) + { + // render silhouettes for highlighted objects + BOOL subtracting_from_selection = (gKeyboard->currentMask(TRUE) == MASK_CONTROL); + for (S32 pass = 0; pass < 2; pass++) + { + for (LLObjectSelection::iterator iter = mHighlightedObjects->begin(); + iter != mHighlightedObjects->end(); iter++) + { + LLSelectNode* node = *iter; + LLViewerObject* objectp = node->getObject(); + if (!objectp) + continue; + if (objectp->isHUDAttachment() != for_hud) + { + continue; + } + + if (subtracting_from_selection) + { + node->renderOneSilhouette(LLColor4::red); + } + else if (!objectp->isSelected()) + { + LLColor4 highlight_color = objectp->isRoot() ? sHighlightParentColor : sHighlightChildColor; + node->renderOneSilhouette(highlight_color); + } + } + } + } + + if (for_hud && avatar) + { + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + stop_glerror(); + } + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); +} + +void LLSelectMgr::generateSilhouette(LLSelectNode* nodep, const LLVector3& view_point) +{ + LLViewerObject* objectp = nodep->getObject(); + + if (objectp && objectp->getPCode() == LL_PCODE_VOLUME) + { + ((LLVOVolume*)objectp)->generateSilhouette(nodep, view_point); + } +} + +// +// Utility classes +// +LLSelectNode::LLSelectNode(LLViewerObject* object, BOOL glow) +: mObject(object), + mIndividualSelection(FALSE), + mTransient(FALSE), + mValid(FALSE), + mPermissions(new LLPermissions()), + mInventorySerial(0), + mSilhouetteExists(FALSE), + mDuplicated(FALSE), + mTESelectMask(0), + mLastTESelected(0), + mName(LLStringUtil::null), + mDescription(LLStringUtil::null), + mTouchName(LLStringUtil::null), + mSitName(LLStringUtil::null), + mCreationDate(0) +{ + selectAllTEs(FALSE); + saveColors(); +} + +LLSelectNode::LLSelectNode(const LLSelectNode& nodep) +{ + mTESelectMask = nodep.mTESelectMask; + mLastTESelected = nodep.mLastTESelected; + + mIndividualSelection = nodep.mIndividualSelection; + + mValid = nodep.mValid; + mTransient = nodep.mTransient; + mPermissions = new LLPermissions(*nodep.mPermissions); + mSaleInfo = nodep.mSaleInfo;; + mAggregatePerm = nodep.mAggregatePerm; + mAggregateTexturePerm = nodep.mAggregateTexturePerm; + mAggregateTexturePermOwner = nodep.mAggregateTexturePermOwner; + mName = nodep.mName; + mDescription = nodep.mDescription; + mCategory = nodep.mCategory; + mSavedPositionLocal = nodep.mSavedPositionLocal; + mSavedPositionGlobal = nodep.mSavedPositionGlobal; + mSavedScale = nodep.mSavedScale; + mSavedRotation = nodep.mSavedRotation; + mDuplicated = nodep.mDuplicated; + mDuplicatePos = nodep.mDuplicatePos; + mDuplicateRot = nodep.mDuplicateRot; + mItemID = nodep.mItemID; + mFolderID = nodep.mFolderID; + mFromTaskID = nodep.mFromTaskID; + mTouchName = nodep.mTouchName; + mSitName = nodep.mSitName; + mCreationDate = nodep.mCreationDate; + + mSilhouetteVertices = nodep.mSilhouetteVertices; + mSilhouetteNormals = nodep.mSilhouetteNormals; + mSilhouetteSegments = nodep.mSilhouetteSegments; + mSilhouetteExists = nodep.mSilhouetteExists; + mObject = nodep.mObject; + + std::vector::const_iterator color_iter; + mSavedColors.clear(); + for (color_iter = nodep.mSavedColors.begin(); color_iter != nodep.mSavedColors.end(); ++color_iter) + { + mSavedColors.push_back(*color_iter); + } + + saveTextures(nodep.mSavedTextures); +} + +LLSelectNode::~LLSelectNode() +{ + delete mPermissions; + mPermissions = NULL; +} + +void LLSelectNode::selectAllTEs(BOOL b) +{ + mTESelectMask = b ? 0xFFFFFFFF : 0x0; + mLastTESelected = 0; +} + +void LLSelectNode::selectTE(S32 te_index, BOOL selected) +{ + if (te_index < 0 || te_index >= SELECT_MAX_TES) + { + return; + } + if (selected) + { + mTESelectMask |= (0x1 << te_index); + } + else + { + mTESelectMask &= ~(0x1 << te_index); + } + mLastTESelected = te_index; +} + +BOOL LLSelectNode::isTESelected(S32 te_index) +{ + if (te_index < 0 || te_index >= mObject->getNumTEs()) + { + return FALSE; + } + return (mTESelectMask & (0x1 << te_index)) != 0; +} + +S32 LLSelectNode::getLastSelectedTE() +{ + if (!isTESelected(mLastTESelected)) + { + return -1; + } + return mLastTESelected; +} + +LLViewerObject* LLSelectNode::getObject() +{ + if (!mObject) + { + return NULL; + } + else if (mObject->isDead()) + { + mObject = NULL; + } + return mObject; +} + +void LLSelectNode::setObject(LLViewerObject* object) +{ + mObject = object; +} + +void LLSelectNode::saveColors() +{ + if (mObject.notNull()) + { + mSavedColors.clear(); + for (S32 i = 0; i < mObject->getNumTEs(); i++) + { + const LLTextureEntry* tep = mObject->getTE(i); + mSavedColors.push_back(tep->getColor()); + } + } +} + +void LLSelectNode::saveTextures(const std::vector& textures) +{ + if (mObject.notNull()) + { + mSavedTextures.clear(); + + for (std::vector::const_iterator texture_it = textures.begin(); + texture_it != textures.end(); ++texture_it) + { + mSavedTextures.push_back(*texture_it); + } + } +} + +void LLSelectNode::saveTextureScaleRatios() +{ + mTextureScaleRatios.clear(); + if (mObject.notNull()) + { + for (U8 i = 0; i < mObject->getNumTEs(); i++) + { + F32 s,t; + const LLTextureEntry* tep = mObject->getTE(i); + tep->getScale(&s,&t); + U32 s_axis = 0; + U32 t_axis = 0; + + LLPrimitive::getTESTAxes(i, &s_axis, &t_axis); + + LLVector3 v; + LLVector3 scale = mObject->getScale(); + + if (tep->getTexGen() == LLTextureEntry::TEX_GEN_PLANAR) + { + v.mV[s_axis] = s*scale.mV[s_axis]; + v.mV[t_axis] = t*scale.mV[t_axis]; + } + else + { + v.mV[s_axis] = s/scale.mV[s_axis]; + v.mV[t_axis] = t/scale.mV[t_axis]; + } + + mTextureScaleRatios.push_back(v); + } + } +} + + +// This implementation should be similar to LLTask::allowOperationOnTask +BOOL LLSelectNode::allowOperationOnNode(PermissionBit op, U64 group_proxy_power) const +{ + // Extract ownership. + BOOL object_is_group_owned = FALSE; + LLUUID object_owner_id; + mPermissions->getOwnership(object_owner_id, object_is_group_owned); + + // Operations on invalid or public objects is not allowed. + if (!mObject || (mObject->isDead()) || !mPermissions->isOwned()) + { + return FALSE; + } + + // The transfer permissions can never be given through proxy. + if (PERM_TRANSFER == op) + { + // The owner of an agent-owned object can transfer to themselves. + if ( !object_is_group_owned + && (gAgent.getID() == object_owner_id) ) + { + return TRUE; + } + else + { + // Otherwise check aggregate permissions. + return mObject->permTransfer(); + } + } + + if (PERM_MOVE == op + || PERM_MODIFY == op) + { + // only owners can move or modify their attachments + // no proxy allowed. + if (mObject->isAttachment() && object_owner_id != gAgent.getID()) + { + return FALSE; + } + } + + // Calculate proxy_agent_id and group_id to use for permissions checks. + // proxy_agent_id may be set to the object owner through group powers. + // group_id can only be set to the object's group, if the agent is in that group. + LLUUID group_id = LLUUID::null; + LLUUID proxy_agent_id = gAgent.getID(); + + // Gods can always operate. + if (gAgent.isGodlike()) + { + return TRUE; + } + + // Check if the agent is in the same group as the object. + LLUUID object_group_id = mPermissions->getGroup(); + if (object_group_id.notNull() && + gAgent.isInGroup(object_group_id)) + { + // Assume the object's group during this operation. + group_id = object_group_id; + } + + // Only allow proxy powers for PERM_COPY if the actual agent can + // receive the item (ie has PERM_TRANSFER permissions). + // NOTE: op == PERM_TRANSFER has already been handled, but if + // that ever changes we need to BLOCK proxy powers for PERM_TRANSFER. DK 03/28/06 + if (PERM_COPY != op || mPermissions->allowTransferTo(gAgent.getID())) + { + // Check if the agent can assume ownership through group proxy or agent-granted proxy. + if ( ( object_is_group_owned + && gAgent.hasPowerInGroup(object_owner_id, group_proxy_power)) + // Only allow proxy for move, modify, and copy. + || ( (PERM_MOVE == op || PERM_MODIFY == op || PERM_COPY == op) + && (!object_is_group_owned + && gAgent.isGrantedProxy(*mPermissions)))) + { + // This agent is able to assume the ownership role for this operation. + proxy_agent_id = object_owner_id; + } + } + + // We now have max ownership information. + if (PERM_OWNER == op) + { + // This this was just a check for ownership, we can now return the answer. + return (proxy_agent_id == object_owner_id ? TRUE : FALSE); + } + + // check permissions to see if the agent can operate + return (mPermissions->allowOperationBy(op, proxy_agent_id, group_id)); +} + +//----------------------------------------------------------------------------- +// renderOneSilhouette() +//----------------------------------------------------------------------------- +void LLSelectNode::renderOneSilhouette(const LLColor4 &color) +{ + LLViewerObject* objectp = getObject(); + if (!objectp) + { + return; + } + + LLDrawable* drawable = objectp->mDrawable; + if(!drawable) + { + return; + } + + if (!mSilhouetteExists) + { + return; + } + + BOOL is_hud_object = objectp->isHUDAttachment(); + + if (mSilhouetteVertices.size() == 0 || mSilhouetteNormals.size() != mSilhouetteVertices.size()) + { + return; + } + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + if (!is_hud_object) + { + glLoadIdentity(); + glMultMatrixd(gGLModelView); + } + + + if (drawable->isActive()) + { + glMultMatrixf((F32*) objectp->getRenderMatrix().mMatrix); + } + + LLVolume *volume = objectp->getVolume(); + if (volume) + { + F32 silhouette_thickness; + if (is_hud_object && gAgent.getAvatarObject()) + { + silhouette_thickness = LLSelectMgr::sHighlightThickness / gAgent.mHUDCurZoom; + } + else + { + LLVector3 view_vector = LLViewerCamera::getInstance()->getOrigin() - objectp->getRenderPosition(); + silhouette_thickness = view_vector.magVec() * LLSelectMgr::sHighlightThickness * (LLViewerCamera::getInstance()->getView() / LLViewerCamera::getInstance()->getDefaultFOV()); + } + F32 animationTime = (F32)LLFrameTimer::getElapsedSeconds(); + + F32 u_coord = fmod(animationTime * LLSelectMgr::sHighlightUAnim, 1.f); + F32 v_coord = 1.f - fmod(animationTime * LLSelectMgr::sHighlightVAnim, 1.f); + F32 u_divisor = 1.f / ((F32)(mSilhouetteVertices.size() - 1)); + + if (LLSelectMgr::sRenderHiddenSelections) // && gFloaterTools && gFloaterTools->getVisible()) + { + gGL.flush(); + gGL.blendFunc(LLRender::BF_SOURCE_COLOR, LLRender::BF_ONE); + LLGLEnable fog(GL_FOG); + glFogi(GL_FOG_MODE, GL_LINEAR); + float d = (LLViewerCamera::getInstance()->getPointOfInterest()-LLViewerCamera::getInstance()->getOrigin()).magVec(); + LLColor4 fogCol = color * (F32)llclamp((LLSelectMgr::getInstance()->getSelectionCenterGlobal()-gAgent.getCameraPositionGlobal()).magVec()/(LLSelectMgr::getInstance()->getBBoxOfSelection().getExtentLocal().magVec()*4), 0.0, 1.0); + glFogf(GL_FOG_START, d); + glFogf(GL_FOG_END, d*(1 + (LLViewerCamera::getInstance()->getView() / LLViewerCamera::getInstance()->getDefaultFOV()))); + glFogfv(GL_FOG_COLOR, fogCol.mV); + + LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_GEQUAL); + gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); + gGL.begin(LLRender::LINES); + { + S32 i = 0; + for (S32 seg_num = 0; seg_num < (S32)mSilhouetteSegments.size(); seg_num++) + { + for(; i < mSilhouetteSegments[seg_num]; i++) + { + u_coord += u_divisor * LLSelectMgr::sHighlightUScale; + + gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f); + gGL.texCoord2f( u_coord, v_coord ); + gGL.vertex3fv( mSilhouetteVertices[i].mV ); + } + } + } + gGL.end(); + u_coord = fmod(animationTime * LLSelectMgr::sHighlightUAnim, 1.f); + } + + gGL.flush(); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + gGL.begin(LLRender::TRIANGLES); + { + S32 i = 0; + for (S32 seg_num = 0; seg_num < (S32)mSilhouetteSegments.size(); seg_num++) + { + S32 first_i = i; + LLVector3 v; + LLVector2 t; + + for(; i < mSilhouetteSegments[seg_num]; i++) + { + + if (i == first_i) { + LLVector3 vert = (mSilhouetteNormals[i]) * silhouette_thickness; + vert += mSilhouetteVertices[i]; + + gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.0f); //LLSelectMgr::sHighlightAlpha); + gGL.texCoord2f( u_coord, v_coord + LLSelectMgr::sHighlightVScale ); + gGL.vertex3fv( vert.mV ); + + u_coord += u_divisor * LLSelectMgr::sHighlightUScale; + + gGL.color4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2); + gGL.texCoord2f( u_coord, v_coord ); + gGL.vertex3fv( mSilhouetteVertices[i].mV ); + + v = mSilhouetteVertices[i]; + t = LLVector2(u_coord, v_coord); + } + else { + LLVector3 vert = (mSilhouetteNormals[i]) * silhouette_thickness; + vert += mSilhouetteVertices[i]; + + gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.0f); //LLSelectMgr::sHighlightAlpha); + gGL.texCoord2f( u_coord, v_coord + LLSelectMgr::sHighlightVScale ); + gGL.vertex3fv( vert.mV ); + gGL.vertex3fv( vert.mV ); + + gGL.texCoord2fv(t.mV); + u_coord += u_divisor * LLSelectMgr::sHighlightUScale; + gGL.color4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2); + gGL.vertex3fv(v.mV); + gGL.texCoord2f( u_coord, v_coord ); + gGL.vertex3fv( mSilhouetteVertices[i].mV ); + + } + } + } + } + gGL.end(); + gGL.flush(); + } + glPopMatrix(); +} + +// +// Utility Functions +// + +// Update everyone who cares about the selection list +void dialog_refresh_all() +{ + if (gNoRender) + { + return; + } + + //could refresh selected object info in toolbar here + + gFloaterTools->dirty(); + + if( gPieObject->getVisible() ) + { + gPieObject->arrange(); + } + + if( gPieAttachment->getVisible() ) + { + gPieAttachment->arrange(); + } + + LLFloaterProperties::dirtyAll(); + LLFloaterInspect::dirty(); +} + +S32 get_family_count(LLViewerObject *parent) +{ + if (!parent) + { + llwarns << "Trying to get_family_count on null parent!" << llendl; + } + S32 count = 1; // for this object + LLViewerObject::const_child_list_t& child_list = parent->getChildren(); + for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); + iter != child_list.end(); iter++) + { + LLViewerObject* child = *iter; + + if (!child) + { + llwarns << "Family object has NULL child! Show Doug." << llendl; + } + else if (child->isDead()) + { + llwarns << "Family object has dead child object. Show Doug." << llendl; + } + else + { + if (LLSelectMgr::getInstance()->canSelectObject(child)) + { + count += get_family_count( child ); + } + } + } + return count; +} + +//----------------------------------------------------------------------------- +// updateSelectionCenter +//----------------------------------------------------------------------------- +void LLSelectMgr::updateSelectionCenter() +{ + const F32 MOVE_SELECTION_THRESHOLD = 1.f; // Movement threshold in meters for updating selection + // center (tractor beam) + + //override any object updates received + //for selected objects + overrideObjectUpdates(); + + LLViewerObject* object = mSelectedObjects->getFirstObject(); + if (!object) + { + // nothing selected, probably grabbing + // Ignore by setting to avatar origin. + mSelectionCenterGlobal.clearVec(); + mShowSelection = FALSE; + mSelectionBBox = LLBBox(); + mPauseRequest = NULL; + resetAgentHUDZoom(); + + } + else + { + mSelectedObjects->mSelectType = getSelectTypeForObject(object); + + if (mSelectedObjects->mSelectType == SELECT_TYPE_ATTACHMENT && gAgent.getAvatarObject()) + { + mPauseRequest = gAgent.getAvatarObject()->requestPause(); + } + else + { + mPauseRequest = NULL; + } + + if (mSelectedObjects->mSelectType != SELECT_TYPE_HUD && gAgent.getAvatarObject()) + { + // reset hud ZOOM + gAgent.mHUDTargetZoom = 1.f; + gAgent.mHUDCurZoom = 1.f; + } + + mShowSelection = FALSE; + LLBBox bbox; + + // have stuff selected + LLVector3d select_center; + // keep a list of jointed objects for showing the joint HUDEffects + + // Initialize the bounding box to the root prim, so the BBox orientation + // matches the root prim's (affecting the orientation of the manipulators). + bbox.addBBoxAgent( (mSelectedObjects->getFirstRootObject(TRUE))->getBoundingBoxAgent() ); + + std::vector < LLViewerObject *> jointed_objects; + + for (LLObjectSelection::iterator iter = mSelectedObjects->begin(); + iter != mSelectedObjects->end(); iter++) + { + LLSelectNode* node = *iter; + LLViewerObject* object = node->getObject(); + if (!object) + continue; + LLViewerObject *myAvatar = gAgent.getAvatarObject(); + LLViewerObject *root = object->getRootEdit(); + if (mSelectedObjects->mSelectType == SELECT_TYPE_WORLD && // not an attachment + !root->isChild(myAvatar) && // not the object you're sitting on + !object->isAvatar()) // not another avatar + { + mShowSelection = TRUE; + } + + bbox.addBBoxAgent( object->getBoundingBoxAgent() ); + + if (object->isJointChild()) + { + jointed_objects.push_back(object); + } + } + + LLVector3 bbox_center_agent = bbox.getCenterAgent(); + mSelectionCenterGlobal = gAgent.getPosGlobalFromAgent(bbox_center_agent); + mSelectionBBox = bbox; + + } + + if ( !(gAgentID == LLUUID::null)) + { + LLTool *tool = LLToolMgr::getInstance()->getCurrentTool(); + if (mShowSelection) + { + LLVector3d select_center_global; + + if( tool->isEditing() ) + { + select_center_global = tool->getEditingPointGlobal(); + } + else + { + select_center_global = mSelectionCenterGlobal; + } + + // Send selection center if moved beyond threshold (used to animate tractor beam) + LLVector3d diff; + diff = select_center_global - mLastSentSelectionCenterGlobal; + + if ( diff.magVecSquared() > MOVE_SELECTION_THRESHOLD*MOVE_SELECTION_THRESHOLD ) + { + // Transmit updated selection center + mLastSentSelectionCenterGlobal = select_center_global; + } + } + } + + // give up edit menu if no objects selected + if (gEditMenuHandler == this && mSelectedObjects->getObjectCount() == 0) + { + gEditMenuHandler = NULL; + } +} + +void LLSelectMgr::updatePointAt() +{ + if (mShowSelection) + { + if (mSelectedObjects->getObjectCount()) + { + LLVector3 select_offset; + const LLPickInfo& pick = gViewerWindow->getLastPick(); + LLViewerObject *click_object = pick.getObject(); + if (click_object && click_object->isSelected()) + { + // clicked on another object in our selection group, use that as target + select_offset.setVec(pick.mObjectOffset); + select_offset.rotVec(~click_object->getRenderRotation()); + + gAgent.setPointAt(POINTAT_TARGET_SELECT, click_object, select_offset); + gAgent.setLookAt(LOOKAT_TARGET_SELECT, click_object, select_offset); + } + else + { + // didn't click on an object this time, revert to pointing at center of first object + gAgent.setPointAt(POINTAT_TARGET_SELECT, mSelectedObjects->getFirstObject()); + gAgent.setLookAt(LOOKAT_TARGET_SELECT, mSelectedObjects->getFirstObject()); + } + } + else + { + gAgent.setPointAt(POINTAT_TARGET_CLEAR); + gAgent.setLookAt(LOOKAT_TARGET_CLEAR); + } + } + else + { + gAgent.setPointAt(POINTAT_TARGET_CLEAR); + gAgent.setLookAt(LOOKAT_TARGET_CLEAR); + } +} + +//----------------------------------------------------------------------------- +// getBBoxOfSelection() +//----------------------------------------------------------------------------- +LLBBox LLSelectMgr::getBBoxOfSelection() const +{ + return mSelectionBBox; +} + + +//----------------------------------------------------------------------------- +// canUndo() +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::canUndo() const +{ + return const_cast(this)->mSelectedObjects->getFirstEditableObject() != NULL; // HACK: casting away constness - MG +} + +//----------------------------------------------------------------------------- +// undo() +//----------------------------------------------------------------------------- +void LLSelectMgr::undo() +{ + BOOL select_linked_set = !gSavedSettings.getBOOL("EditLinkedParts"); + LLUUID group_id(gAgent.getGroupID()); + sendListToRegions("Undo", packAgentAndSessionAndGroupID, packObjectID, &group_id, select_linked_set ? SEND_ONLY_ROOTS : SEND_CHILDREN_FIRST); +} + +//----------------------------------------------------------------------------- +// canRedo() +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::canRedo() const +{ + return const_cast(this)->mSelectedObjects->getFirstEditableObject() != NULL; // HACK: casting away constness - MG +} + +//----------------------------------------------------------------------------- +// redo() +//----------------------------------------------------------------------------- +void LLSelectMgr::redo() +{ + BOOL select_linked_set = !gSavedSettings.getBOOL("EditLinkedParts"); + LLUUID group_id(gAgent.getGroupID()); + sendListToRegions("Redo", packAgentAndSessionAndGroupID, packObjectID, &group_id, select_linked_set ? SEND_ONLY_ROOTS : SEND_CHILDREN_FIRST); +} + +//----------------------------------------------------------------------------- +// canDoDelete() +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::canDoDelete() const +{ + // Note: Can only delete root objects (see getFirstDeleteableObject() for more info) + return const_cast(this)->mSelectedObjects->getFirstDeleteableObject() != NULL; // HACK: casting away constness - MG +} + +//----------------------------------------------------------------------------- +// doDelete() +//----------------------------------------------------------------------------- +void LLSelectMgr::doDelete() +{ + selectDelete(); +} + +//----------------------------------------------------------------------------- +// canDeselect() +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::canDeselect() const +{ + return !mSelectedObjects->isEmpty(); +} + +//----------------------------------------------------------------------------- +// deselect() +//----------------------------------------------------------------------------- +void LLSelectMgr::deselect() +{ + deselectAll(); +} +//----------------------------------------------------------------------------- +// canDuplicate() +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::canDuplicate() const +{ + return const_cast(this)->mSelectedObjects->getFirstCopyableObject() != NULL; // HACK: casting away constness - MG +} +//----------------------------------------------------------------------------- +// duplicate() +//----------------------------------------------------------------------------- +void LLSelectMgr::duplicate() +{ + LLVector3 offset(0.5f, 0.5f, 0.f); + selectDuplicate(offset, TRUE); +} + +ESelectType LLSelectMgr::getSelectTypeForObject(LLViewerObject* object) +{ + if (!object) + { + return SELECT_TYPE_WORLD; + } + if (object->isHUDAttachment()) + { + return SELECT_TYPE_HUD; + } + else if (object->isAttachment()) + { + return SELECT_TYPE_ATTACHMENT; + } + else + { + return SELECT_TYPE_WORLD; + } +} + +void LLSelectMgr::validateSelection() +{ + struct f : public LLSelectedObjectFunctor + { + virtual bool apply(LLViewerObject* object) + { + if (!LLSelectMgr::getInstance()->canSelectObject(object)) + { + LLSelectMgr::getInstance()->deselectObjectOnly(object); + } + return true; + } + } func; + getSelection()->applyToObjects(&func); +} + +BOOL LLSelectMgr::canSelectObject(LLViewerObject* object) +{ + // Never select dead objects + if (!object || object->isDead()) + { + return FALSE; + } + + if (mForceSelection) + { + return TRUE; + } + + if ((gSavedSettings.getBOOL("SelectOwnedOnly") && !object->permYouOwner()) || + (gSavedSettings.getBOOL("SelectMovableOnly") && !object->permMove())) + { + // only select my own objects + return FALSE; + } + + // Can't select orphans + if (object->isOrphaned()) return FALSE; + + // Can't select avatars + if (object->isAvatar()) return FALSE; + + // Can't select land + if (object->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH) return FALSE; + + // + //ESelectType selection_type = getSelectTypeForObject(object); + //if (mSelectedObjects->getObjectCount() > 0 && mSelectedObjects->mSelectType != selection_type) return FALSE; + // + + return TRUE; +} + +BOOL LLSelectMgr::setForceSelection(BOOL force) +{ + std::swap(mForceSelection,force); + return force; +} + +void LLSelectMgr::resetAgentHUDZoom() +{ + gAgent.mHUDTargetZoom = 1.f; + gAgent.mHUDCurZoom = 1.f; +} + +void LLSelectMgr::getAgentHUDZoom(F32 &target_zoom, F32 ¤t_zoom) const +{ + target_zoom = gAgent.mHUDTargetZoom; + current_zoom = gAgent.mHUDCurZoom; +} + +void LLSelectMgr::setAgentHUDZoom(F32 target_zoom, F32 current_zoom) +{ + gAgent.mHUDTargetZoom = target_zoom; + gAgent.mHUDCurZoom = current_zoom; +} + +LLObjectSelection::LLObjectSelection() : + LLRefCount(), + mSelectType(SELECT_TYPE_WORLD) +{ +} + +LLObjectSelection::~LLObjectSelection() +{ + deleteAllNodes(); +} + +void LLObjectSelection::cleanupNodes() +{ + for (list_t::iterator iter = mList.begin(); iter != mList.end(); ) + { + list_t::iterator curiter = iter++; + LLSelectNode* node = *curiter; + if (node->getObject() == NULL || node->getObject()->isDead()) + { + mList.erase(curiter); + delete node; + } + } +} + +void LLObjectSelection::updateEffects() +{ +} + +S32 LLObjectSelection::getNumNodes() +{ + return mList.size(); +} + +void LLObjectSelection::addNode(LLSelectNode *nodep) +{ + llassert_always(nodep->getObject() && !nodep->getObject()->isDead()); + mList.push_front(nodep); + mSelectNodeMap[nodep->getObject()] = nodep; +} + +void LLObjectSelection::addNodeAtEnd(LLSelectNode *nodep) +{ + llassert_always(nodep->getObject() && !nodep->getObject()->isDead()); + mList.push_back(nodep); + mSelectNodeMap[nodep->getObject()] = nodep; +} + +void LLObjectSelection::moveNodeToFront(LLSelectNode *nodep) +{ + mList.remove(nodep); + mList.push_front(nodep); +} + +void LLObjectSelection::removeNode(LLSelectNode *nodep) +{ + mSelectNodeMap.erase(nodep->getObject()); + if (nodep->getObject() == mPrimaryObject) + { + mPrimaryObject = NULL; + } + nodep->setObject(NULL); // Will get erased in cleanupNodes() + mList.remove(nodep); +} + +void LLObjectSelection::deleteAllNodes() +{ + std::for_each(mList.begin(), mList.end(), DeletePointer()); + mList.clear(); + mSelectNodeMap.clear(); + mPrimaryObject = NULL; +} + +LLSelectNode* LLObjectSelection::findNode(LLViewerObject* objectp) +{ + std::map, LLSelectNode*>::iterator found_it = mSelectNodeMap.find(objectp); + if (found_it != mSelectNodeMap.end()) + { + return found_it->second; + } + return NULL; +} + +//----------------------------------------------------------------------------- +// isEmpty() +//----------------------------------------------------------------------------- +BOOL LLObjectSelection::isEmpty() const +{ + return (mList.size() == 0); +} + +//----------------------------------------------------------------------------- +// getOwnershipCost() +//----------------------------------------------------------------------------- +BOOL LLObjectSelection::getOwnershipCost(S32 &cost) +{ + S32 count = getObjectCount(); + cost = count * OWNERSHIP_COST_PER_OBJECT; + return (count > 0); +} + + +//----------------------------------------------------------------------------- +// getObjectCount() - returns number of non null objects +//----------------------------------------------------------------------------- +S32 LLObjectSelection::getObjectCount() +{ + cleanupNodes(); + S32 count = mList.size(); + return count; +} + + +//----------------------------------------------------------------------------- +// getTECount() +//----------------------------------------------------------------------------- +S32 LLObjectSelection::getTECount() +{ + S32 count = 0; + for (LLObjectSelection::iterator iter = begin(); iter != end(); iter++) + { + LLSelectNode* node = *iter; + LLViewerObject* object = node->getObject(); + if (!object) + continue; + S32 num_tes = object->getNumTEs(); + for (S32 te = 0; te < num_tes; te++) + { + if (node->isTESelected(te)) + { + ++count; + } + } + } + return count; +} + +//----------------------------------------------------------------------------- +// getRootObjectCount() +//----------------------------------------------------------------------------- +S32 LLObjectSelection::getRootObjectCount() +{ + S32 count = 0; + for (LLObjectSelection::root_iterator iter = root_begin(); iter != root_end(); iter++) + { + ++count; + } + return count; +} + +bool LLObjectSelection::applyToObjects(LLSelectedObjectFunctor* func) +{ + bool result = true; + for (iterator iter = begin(); iter != end(); ) + { + iterator nextiter = iter++; + LLViewerObject* object = (*nextiter)->getObject(); + if (!object) + continue; + bool r = func->apply(object); + result = result && r; + } + return result; +} + +bool LLObjectSelection::applyToRootObjects(LLSelectedObjectFunctor* func, bool firstonly) +{ + bool result = firstonly ? false : true; + for (root_iterator iter = root_begin(); iter != root_end(); ) + { + root_iterator nextiter = iter++; + LLViewerObject* object = (*nextiter)->getObject(); + if (!object) + continue; + bool r = func->apply(object); + if (firstonly && r) + return true; + else + result = result && r; + } + return result; +} + +bool LLObjectSelection::applyToTEs(LLSelectedTEFunctor* func, bool firstonly) +{ + bool result = firstonly ? false : true; + for (iterator iter = begin(); iter != end(); ) + { + iterator nextiter = iter++; + LLSelectNode* node = *nextiter; + LLViewerObject* object = (*nextiter)->getObject(); + if (!object) + continue; + S32 num_tes = llmin((S32)object->getNumTEs(), (S32)object->getNumFaces()); // avatars have TEs but no faces + for (S32 te = 0; te < num_tes; ++te) + { + if (node->isTESelected(te)) + { + bool r = func->apply(object, te); + if (firstonly && r) + return true; + else + result = result && r; + } + } + } + return result; +} + +bool LLObjectSelection::applyToNodes(LLSelectedNodeFunctor *func, bool firstonly) +{ + bool result = firstonly ? false : true; + for (iterator iter = begin(); iter != end(); ) + { + iterator nextiter = iter++; + LLSelectNode* node = *nextiter; + bool r = func->apply(node); + if (firstonly && r) + return true; + else + result = result && r; + } + return result; +} + +bool LLObjectSelection::applyToRootNodes(LLSelectedNodeFunctor *func, bool firstonly) +{ + bool result = firstonly ? false : true; + for (root_iterator iter = root_begin(); iter != root_end(); ) + { + root_iterator nextiter = iter++; + LLSelectNode* node = *nextiter; + bool r = func->apply(node); + if (firstonly && r) + return true; + else + result = result && r; + } + return result; +} + +//----------------------------------------------------------------------------- +// contains() +//----------------------------------------------------------------------------- +BOOL LLObjectSelection::contains(LLViewerObject* object) +{ + return findNode(object) != NULL; +} + + +//----------------------------------------------------------------------------- +// contains() +//----------------------------------------------------------------------------- +BOOL LLObjectSelection::contains(LLViewerObject* object, S32 te) +{ + if (te == SELECT_ALL_TES) + { + // ...all faces + for (LLObjectSelection::iterator iter = begin(); + iter != end(); iter++) + { + LLSelectNode* nodep = *iter; + if (nodep->getObject() == object) + { + BOOL all_selected = TRUE; + for (S32 i = 0; i < SELECT_MAX_TES; i++) + { + all_selected = all_selected && nodep->isTESelected(i); + } + return all_selected; + } + } + return FALSE; + } + else + { + // ...one face + for (LLObjectSelection::iterator iter = begin(); iter != end(); iter++) + { + LLSelectNode* nodep = *iter; + if (nodep->getObject() == object && nodep->isTESelected(te)) + { + return TRUE; + } + } + return FALSE; + } +} + +// returns TRUE is any node is currenly worn as an attachment +BOOL LLObjectSelection::isAttachment() +{ + return (mSelectType == SELECT_TYPE_ATTACHMENT || mSelectType == SELECT_TYPE_HUD); +} + +//----------------------------------------------------------------------------- +// getSelectedParentObject() +//----------------------------------------------------------------------------- +LLViewerObject* getSelectedParentObject(LLViewerObject *object) +{ + LLViewerObject *parent; + while (object && (parent = (LLViewerObject*)object->getParent())) + { + if (parent->isSelected()) + { + object = parent; + } + else + { + break; + } + } + return object; +} + +//----------------------------------------------------------------------------- +// getFirstNode +//----------------------------------------------------------------------------- +LLSelectNode* LLObjectSelection::getFirstNode(LLSelectedNodeFunctor* func) +{ + for (iterator iter = begin(); iter != end(); ++iter) + { + LLSelectNode* node = *iter; + if (func == NULL || func->apply(node)) + { + return node; + } + } + return NULL; +} + +LLSelectNode* LLObjectSelection::getFirstRootNode(LLSelectedNodeFunctor* func, BOOL non_root_ok) +{ + for (root_iterator iter = root_begin(); iter != root_end(); ++iter) + { + LLSelectNode* node = *iter; + if (func == NULL || func->apply(node)) + { + return node; + } + } + if (non_root_ok) + { + // Get non root + return getFirstNode(func); + } + return NULL; +} + + +//----------------------------------------------------------------------------- +// getFirstSelectedObject +//----------------------------------------------------------------------------- +LLViewerObject* LLObjectSelection::getFirstSelectedObject(LLSelectedNodeFunctor* func, BOOL get_parent) +{ + LLSelectNode* res = getFirstNode(func); + if (res && get_parent) + { + return getSelectedParentObject(res->getObject()); + } + else if (res) + { + return res->getObject(); + } + return NULL; +} + +//----------------------------------------------------------------------------- +// getFirstObject() +//----------------------------------------------------------------------------- +LLViewerObject* LLObjectSelection::getFirstObject() +{ + LLSelectNode* res = getFirstNode(NULL); + return res ? res->getObject() : NULL; +} + +//----------------------------------------------------------------------------- +// getFirstRootObject() +//----------------------------------------------------------------------------- +LLViewerObject* LLObjectSelection::getFirstRootObject(BOOL non_root_ok) +{ + LLSelectNode* res = getFirstRootNode(NULL, non_root_ok); + return res ? res->getObject() : NULL; +} + +//----------------------------------------------------------------------------- +// getFirstMoveableNode() +//----------------------------------------------------------------------------- +LLSelectNode* LLObjectSelection::getFirstMoveableNode(BOOL get_root_first) +{ + struct f : public LLSelectedNodeFunctor + { + bool apply(LLSelectNode* node) + { + LLViewerObject* obj = node->getObject(); + return obj && obj->permMove(); + } + } func; + LLSelectNode* res = get_root_first ? getFirstRootNode(&func, TRUE) : getFirstNode(&func); + return res; +} + +//----------------------------------------------------------------------------- +// getFirstCopyableObject() +//----------------------------------------------------------------------------- +LLViewerObject* LLObjectSelection::getFirstCopyableObject(BOOL get_parent) +{ + struct f : public LLSelectedNodeFunctor + { + bool apply(LLSelectNode* node) + { + LLViewerObject* obj = node->getObject(); + return obj && obj->permCopy() && !obj->isAttachment(); + } + } func; + return getFirstSelectedObject(&func, get_parent); +} + +//----------------------------------------------------------------------------- +// getFirstDeleteableObject() +//----------------------------------------------------------------------------- +LLViewerObject* LLObjectSelection::getFirstDeleteableObject() +{ + //RN: don't currently support deletion of child objects, as that requires separating them first + // then derezzing to trash + + struct f : public LLSelectedNodeFunctor + { + bool apply(LLSelectNode* node) + { + LLViewerObject* obj = node->getObject(); + // you can delete an object if you are the owner + // or you have permission to modify it. + if( obj && ( (obj->permModify()) || + (obj->permYouOwner()) || + (!obj->permAnyOwner()) )) // public + { + if( !obj->isAttachment() ) + { + return true; + } + } + return false; + } + } func; + LLSelectNode* node = getFirstNode(&func); + return node ? node->getObject() : NULL; +} + +//----------------------------------------------------------------------------- +// getFirstEditableObject() +//----------------------------------------------------------------------------- +LLViewerObject* LLObjectSelection::getFirstEditableObject(BOOL get_parent) +{ + struct f : public LLSelectedNodeFunctor + { + bool apply(LLSelectNode* node) + { + LLViewerObject* obj = node->getObject(); + return obj && obj->permModify(); + } + } func; + return getFirstSelectedObject(&func, get_parent); +} + +//----------------------------------------------------------------------------- +// getFirstMoveableObject() +//----------------------------------------------------------------------------- +LLViewerObject* LLObjectSelection::getFirstMoveableObject(BOOL get_parent) +{ + struct f : public LLSelectedNodeFunctor + { + bool apply(LLSelectNode* node) + { + LLViewerObject* obj = node->getObject(); + return obj && obj->permMove(); + } + } func; + return getFirstSelectedObject(&func, get_parent); +} + +//----------------------------------------------------------------------------- +// Position + Rotation update methods called from LLViewerJoystick +//----------------------------------------------------------------------------- +bool LLSelectMgr::selectionMove(const LLVector3& displ, + F32 roll, F32 pitch, F32 yaw, U32 update_type) +{ + if (update_type == UPD_NONE) + { + return false; + } + + LLVector3 displ_global; + bool update_success = true; + bool update_position = update_type & UPD_POSITION; + bool update_rotation = update_type & UPD_ROTATION; + const bool noedit_linked_parts = !gSavedSettings.getBOOL("EditLinkedParts"); + + if (update_position) + { + // calculate the distance of the object closest to the camera origin + F32 min_dist = 1e+30f; + LLVector3 obj_pos; + for (LLObjectSelection::root_iterator it = getSelection()->root_begin(); + it != getSelection()->root_end(); ++it) + { + obj_pos = (*it)->getObject()->getPositionEdit(); + + F32 obj_dist = dist_vec(obj_pos, LLViewerCamera::getInstance()->getOrigin()); + if (obj_dist < min_dist) + { + min_dist = obj_dist; + } + } + + // factor the distance inside the displacement vector. This will get us + // equally visible movements for both close and far away selections. + min_dist = sqrt(min_dist) / 2; + displ_global.setVec(displ.mV[0]*min_dist, + displ.mV[1]*min_dist, + displ.mV[2]*min_dist); + + // equates to: Displ_global = Displ * M_cam_axes_in_global_frame + displ_global = LLViewerCamera::getInstance()->rotateToAbsolute(displ_global); + } + + LLQuaternion new_rot; + if (update_rotation) + { + // let's calculate the rotation around each camera axes + LLQuaternion qx(roll, LLViewerCamera::getInstance()->getAtAxis()); + LLQuaternion qy(pitch, LLViewerCamera::getInstance()->getLeftAxis()); + LLQuaternion qz(yaw, LLViewerCamera::getInstance()->getUpAxis()); + new_rot.setQuat(qx * qy * qz); + } + + LLViewerObject *obj; + S32 obj_count = getSelection()->getObjectCount(); + for (LLObjectSelection::root_iterator it = getSelection()->root_begin(); + it != getSelection()->root_end(); ++it ) + { + obj = (*it)->getObject(); + bool enable_pos = false, enable_rot = false; + bool perm_move = obj->permMove(); + bool perm_mod = obj->permModify(); + + LLVector3d sel_center(getSelectionCenterGlobal()); + + if (update_rotation) + { + enable_rot = perm_move + && ((perm_mod && !obj->isAttachment()) || noedit_linked_parts); + + if (enable_rot) + { + int children_count = obj->getChildren().size(); + if (obj_count > 1 && children_count > 0) + { + // for linked sets, rotate around the group center + const LLVector3 t(obj->getPositionGlobal() - sel_center); + + // Ra = T x R x T^-1 + LLMatrix4 mt; mt.setTranslation(t); + const LLMatrix4 mnew_rot(new_rot); + LLMatrix4 mt_1; mt_1.setTranslation(-t); + mt *= mnew_rot; + mt *= mt_1; + + // Rfin = Rcur * Ra + obj->setRotation(obj->getRotationEdit() * mt.quaternion()); + displ_global += mt.getTranslation(); + } + else + { + obj->setRotation(obj->getRotationEdit() * new_rot); + } + } + else + { + update_success = false; + } + } + + if (update_position) + { + // establish if object can be moved or not + enable_pos = perm_move && !obj->isAttachment() + && (perm_mod || noedit_linked_parts); + + if (enable_pos) + { + obj->setPosition(obj->getPositionEdit() + displ_global); + } + else + { + update_success = false; + } + } + + if (enable_pos && enable_rot && obj->mDrawable.notNull()) + { + gPipeline.markMoved(obj->mDrawable, TRUE); + } + } + + if (update_position && update_success && obj_count > 1) + { + updateSelectionCenter(); + } + + return update_success; +} + +void LLSelectMgr::sendSelectionMove() +{ + LLSelectNode *node = mSelectedObjects->getFirstRootNode(); + if (node == NULL) + { + return; + } + + //saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); + + U32 update_type = UPD_POSITION | UPD_ROTATION; + LLViewerRegion *last_region, *curr_region = node->getObject()->getRegion(); + S32 objects_in_this_packet = 0; + + // apply to linked objects if unable to select their individual parts + if (!gSavedSettings.getBOOL("EditLinkedParts") && !getTEMode()) + { + // tell simulator to apply to whole linked sets + update_type |= UPD_LINKED_SETS; + } + + // prepare first bulk message + gMessageSystem->newMessage("MultipleObjectUpdate"); + packAgentAndSessionID(&update_type); + + LLViewerObject *obj = NULL; + for (LLObjectSelection::root_iterator it = getSelection()->root_begin(); + it != getSelection()->root_end(); ++it) + { + obj = (*it)->getObject(); + + // note: following code adapted from sendListToRegions() (@3924) + last_region = curr_region; + curr_region = obj->getRegion(); + + // if not simulator or message too big + if (curr_region != last_region + || gMessageSystem->isSendFull(NULL) + || objects_in_this_packet >= MAX_OBJECTS_PER_PACKET) + { + // send sim the current message and start new one + gMessageSystem->sendReliable(last_region->getHost()); + objects_in_this_packet = 0; + gMessageSystem->newMessage("MultipleObjectUpdate"); + packAgentAndSessionID(&update_type); + } + + // add another instance of the body of data + packMultipleUpdate(*it, &update_type); + ++objects_in_this_packet; + } + + // flush remaining messages + if (gMessageSystem->getCurrentSendTotal() > 0) + { + gMessageSystem->sendReliable(curr_region->getHost()); + } + else + { + gMessageSystem->clearMessage(); + } + + //saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); +} diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index b9f2cdd9d..e939a47e2 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -47,6 +47,7 @@ #include "llgesturemgr.h" #include "llhudeffecttrail.h" #include "llhudmanager.h" +#include "llinventorybridge.h" #include "llinventorymodel.h" #include "llinventoryview.h" #include "llmutelist.h" @@ -477,6 +478,15 @@ LLToolDragAndDrop::dragOrDrop3dImpl LLToolDragAndDrop::sDragAndDrop3d[DAD_COUNT] &LLToolDragAndDrop::dad3dUpdateInventory, // Dest: DT_OBJECT &LLToolDragAndDrop::dad3dNULL,//dad3dAssetOnLand, // Dest: DT_LAND }, + // Source: DAD_LINK + // TODO: gesture on self could play it? edit it? + { + &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE + &LLToolDragAndDrop::dad3dNULL, // Dest: DT_SELF + &LLToolDragAndDrop::dad3dNULL, // Dest: DT_AVATAR + &LLToolDragAndDrop::dad3dNULL, // Dest: DT_OBJECT + &LLToolDragAndDrop::dad3dNULL,//dad3dAssetOnLand, // Dest: DT_LAND + }, }; LLToolDragAndDrop::LLToolDragAndDrop() @@ -1986,6 +1996,7 @@ EAcceptance LLToolDragAndDrop::willObjectAcceptInventory(LLViewerObject* obj, LL //if(!vitem->isComplete()) return ACCEPT_NO; if(!vitem->isComplete() && !(gInventory.isObjectDescendentOf(vitem->getUUID(), gLocalInventoryRoot))) return ACCEPT_NO; // + if (vitem->getIsLinkType()) return ACCEPT_NO; // No giving away links // deny attempts to drop from an object onto itself. This is to // help make sure that drops that are from an object to an object diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index cdbec2304..6585aba49 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -44,6 +44,7 @@ #include "llimview.h" #include "llgesturemgr.h" +#include "llinventorybridge.h" #include "llinventoryview.h" #include "llviewerregion.h" @@ -672,9 +673,10 @@ void WearOnAvatarCallback::fire(const LLUUID& inv_item) } } -RezAttachmentCallback::RezAttachmentCallback(LLViewerJointAttachment *attachmentp) +RezAttachmentCallback::RezAttachmentCallback(LLViewerJointAttachment *attachmentp, bool replace) { mAttach = attachmentp; + mReplace = replace; } RezAttachmentCallback::~RezAttachmentCallback() { @@ -688,7 +690,7 @@ void RezAttachmentCallback::fire(const LLUUID& inv_item) LLViewerInventoryItem *item = gInventory.getItem(inv_item); if (item) { - rez_attachment(item, mAttach); + rez_attachment(item, mAttach, mReplace); } } @@ -772,6 +774,72 @@ void copy_inventory_item( gAgent.sendReliableMessage(); } +void link_inventory_item( + const LLUUID& agent_id, + const LLUUID& item_id, + const LLUUID& parent_id, + const std::string& new_name, + const LLAssetType::EType asset_type, + LLPointer cb) +{ + const LLInventoryObject *baseobj = gInventory.getObject(item_id); + if (!baseobj) + { + llwarns << "attempt to link to unknown item, linked-to-item's itemID " << item_id << llendl; + return; + } + if (baseobj && baseobj->getIsLinkType()) + { + llwarns << "attempt to create a link to a link, linked-to-item's itemID " << item_id << llendl; + return; + } + + if (baseobj && !LLAssetType::lookupCanLink(baseobj->getType())) + { + // Fail if item can be found but is of a type that can't be linked. + // Arguably should fail if the item can't be found too, but that could + // be a larger behavioral change. + llwarns << "attempt to link an unlinkable item, type = " << baseobj->getActualType() << llendl; + return; + } + + LLUUID transaction_id; + std::string desc = "Broken link"; // This should only show if the object can't find its baseobj. + LLInventoryType::EType inv_type = LLInventoryType::IT_NONE; + if (dynamic_cast(baseobj)) + { + inv_type = LLInventoryType::IT_CATEGORY; + } + else + { + const LLViewerInventoryItem *baseitem = dynamic_cast(baseobj); + if (baseitem) + { + inv_type = baseitem->getInventoryType(); + } + } + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_LinkInventoryItem); + msg->nextBlock(_PREHASH_AgentData); + { + msg->addUUIDFast(_PREHASH_AgentID, agent_id); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + } + msg->nextBlock(_PREHASH_InventoryBlock); + { + msg->addU32Fast(_PREHASH_CallbackID, gInventoryCallbacks.registerCB(cb)); + msg->addUUIDFast(_PREHASH_FolderID, parent_id); + msg->addUUIDFast(_PREHASH_TransactionID, transaction_id); + msg->addUUIDFast(_PREHASH_OldItemID, item_id); + msg->addS8Fast(_PREHASH_Type, (S8)asset_type); + msg->addS8Fast(_PREHASH_InvType, (S8)inv_type); + msg->addStringFast(_PREHASH_Name, new_name); + msg->addStringFast(_PREHASH_Description, desc); + } + gAgent.sendReliableMessage(); +} + void move_inventory_item( const LLUUID& agent_id, const LLUUID& session_id, @@ -846,3 +914,139 @@ void copy_inventory_from_notecard(const LLUUID& object_id, const LLUUID& notecar } } } + +LLAssetType::EType LLViewerInventoryItem::getType() const +{ + if (const LLViewerInventoryItem *linked_item = getLinkedItem()) + { + return linked_item->getType(); + } + if (const LLViewerInventoryCategory *linked_category = getLinkedCategory()) + { + return linked_category->getType(); + } + return LLInventoryItem::getType(); +} + +const LLUUID& LLViewerInventoryItem::getAssetUUID() const +{ + if (const LLViewerInventoryItem *linked_item = getLinkedItem()) + { + return linked_item->getAssetUUID(); + } + + return LLInventoryItem::getAssetUUID(); +} + +const std::string& LLViewerInventoryItem::getName() const +{ + if (const LLViewerInventoryItem *linked_item = getLinkedItem()) + { + return linked_item->getName(); + } + if (const LLViewerInventoryCategory *linked_category = getLinkedCategory()) + { + return linked_category->getName(); + } + + return LLInventoryItem::getName(); +} + +const LLPermissions& LLViewerInventoryItem::getPermissions() const +{ + // Use the actual permissions of the symlink, not its parent. + return LLInventoryItem::getPermissions(); +} + +const LLUUID& LLViewerInventoryItem::getCreatorUUID() const +{ + if (const LLViewerInventoryItem *linked_item = getLinkedItem()) + { + return linked_item->getCreatorUUID(); + } + + return LLInventoryItem::getCreatorUUID(); +} + +const std::string& LLViewerInventoryItem::getDescription() const +{ + if (const LLViewerInventoryItem *linked_item = getLinkedItem()) + { + return linked_item->getDescription(); + } + + return LLInventoryItem::getDescription(); +} + +const LLSaleInfo& LLViewerInventoryItem::getSaleInfo() const +{ + if (const LLViewerInventoryItem *linked_item = getLinkedItem()) + { + return linked_item->getSaleInfo(); + } + + return LLInventoryItem::getSaleInfo(); +} + +LLInventoryType::EType LLViewerInventoryItem::getInventoryType() const +{ + if (const LLViewerInventoryItem *linked_item = getLinkedItem()) + { + return linked_item->getInventoryType(); + } + + // Categories don't have types. If this item is an AT_FOLDER_LINK, + // treat it as a category. + if (getLinkedCategory()) + { + return LLInventoryType::IT_CATEGORY; + } + + return LLInventoryItem::getInventoryType(); +} + +U32 LLViewerInventoryItem::getFlags() const +{ + if (const LLViewerInventoryItem *linked_item = getLinkedItem()) + { + return linked_item->getFlags(); + } + return LLInventoryItem::getFlags(); +} + + +// This returns true if the item that this item points to +// doesn't exist in memory (i.e. LLInventoryModel). The baseitem +// might still be in the database but just not loaded yet. +bool LLViewerInventoryItem::getIsBrokenLink() const +{ + // If the item's type resolves to be a link, that means either: + // A. It wasn't able to perform indirection, i.e. the baseobj doesn't exist in memory. + // B. It's pointing to another link, which is illegal. + return LLAssetType::lookupIsLinkType(getType()); +} + +LLViewerInventoryItem *LLViewerInventoryItem::getLinkedItem() const +{ + if (mType == LLAssetType::AT_LINK) + { + LLViewerInventoryItem *linked_item = gInventory.getItem(mAssetUUID); + if (linked_item && linked_item->getIsLinkType()) + { + llwarns << "Warning: Accessing link to link" << llendl; + return NULL; + } + return linked_item; + } + return NULL; +} + +LLViewerInventoryCategory *LLViewerInventoryItem::getLinkedCategory() const +{ + if (mType == LLAssetType::AT_LINK_FOLDER) + { + LLViewerInventoryCategory *linked_category = gInventory.getCategory(mAssetUUID); + return linked_category; + } + return NULL; +} \ No newline at end of file diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 1ddf8a58f..467ebf51c 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -37,6 +37,8 @@ #include "llframetimer.h" #include "llwearable.h" +class LLViewerInventoryCategory; + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLViewerInventoryItem // @@ -53,6 +55,16 @@ protected: ~LLViewerInventoryItem( void ); // ref counted public: + virtual LLAssetType::EType getType() const; + virtual const LLUUID& getAssetUUID() const; + virtual const std::string& getName() const; + virtual const LLPermissions& getPermissions() const; + virtual const LLUUID& getCreatorUUID() const; + virtual const std::string& getDescription() const; + virtual const LLSaleInfo& getSaleInfo() const; + virtual LLInventoryType::EType getInventoryType() const; + virtual U32 getFlags() const; + // construct a complete viewer inventory item LLViewerInventoryItem(const LLUUID& uuid, const LLUUID& parent_uuid, const LLPermissions& permissions, @@ -125,6 +137,10 @@ public: }; LLTransactionID getTransactionID() const { return mTransactionID; } + bool getIsBrokenLink() const; // true if the baseitem this points to doesn't exist in memory. + LLViewerInventoryItem *getLinkedItem() const; + LLViewerInventoryCategory *getLinkedCategory() const; + protected: BOOL mIsComplete; LLTransactionID mTransactionID; @@ -208,7 +224,7 @@ class LLViewerJointAttachment; class RezAttachmentCallback : public LLInventoryCallback { public: - RezAttachmentCallback(LLViewerJointAttachment *attachmentp); + RezAttachmentCallback(LLViewerJointAttachment *attachmentp, bool replace = false); void fire(const LLUUID& inv_item); protected: @@ -216,6 +232,7 @@ protected: private: LLViewerJointAttachment* mAttach; + bool mReplace; }; class ActivateGestureCallback : public LLInventoryCallback @@ -272,6 +289,14 @@ void copy_inventory_item( const std::string& new_name, LLPointer cb); +void link_inventory_item( + const LLUUID& agent_id, + const LLUUID& item_id, + const LLUUID& parent_id, + const std::string& new_name, + const LLAssetType::EType asset_type, + LLPointer cb); + void move_inventory_item( const LLUUID& agent_id, const LLUUID& session_id, diff --git a/indra/newview/llviewerjointattachment.cpp b/indra/newview/llviewerjointattachment.cpp index 63c63e554..fe6726f76 100644 --- a/indra/newview/llviewerjointattachment.cpp +++ b/indra/newview/llviewerjointattachment.cpp @@ -57,7 +57,6 @@ extern LLPipeline gPipeline; // LLViewerJointAttachment() //----------------------------------------------------------------------------- LLViewerJointAttachment::LLViewerJointAttachment() : -mAttachedObject(NULL), mVisibleInFirst(FALSE), mGroup(0), mIsHUDAttachment(FALSE), @@ -65,6 +64,7 @@ mPieSlice(-1) { mValid = FALSE; mUpdateXform = FALSE; + mAttachedObjects.clear(); } //----------------------------------------------------------------------------- @@ -103,12 +103,19 @@ U32 LLViewerJointAttachment::drawShape( F32 pixelArea, BOOL first_pass, BOOL is_ return 0; } -void LLViewerJointAttachment::setupDrawable(LLDrawable* drawablep) +void LLViewerJointAttachment::setupDrawable(LLViewerObject *object) { - drawablep->mXform.setParent(&mXform); // LLViewerJointAttachment::lazyAttach - drawablep->makeActive(); - LLVector3 current_pos = mAttachedObject->getRenderPosition(); - LLQuaternion current_rot = mAttachedObject->getRenderRotation(); + if (!object->mDrawable) + return; + if (object->mDrawable->isActive()) + { + object->mDrawable->makeStatic(FALSE); + } + + object->mDrawable->mXform.setParent(getXform()); // LLViewerJointAttachment::lazyAttach + object->mDrawable->makeActive(); + LLVector3 current_pos = object->getRenderPosition(); + LLQuaternion current_rot = object->getRenderRotation(); LLQuaternion attachment_pt_inv_rot = ~getWorldRotation(); current_pos -= getWorldPosition(); @@ -116,21 +123,21 @@ void LLViewerJointAttachment::setupDrawable(LLDrawable* drawablep) current_rot = current_rot * attachment_pt_inv_rot; - drawablep->mXform.setPosition(current_pos); - drawablep->mXform.setRotation(current_rot); - gPipeline.markMoved(drawablep); - gPipeline.markTextured(drawablep); // face may need to change draw pool to/from POOL_HUD - drawablep->setState(LLDrawable::USE_BACKLIGHT); + object->mDrawable->mXform.setPosition(current_pos); + object->mDrawable->mXform.setRotation(current_rot); + gPipeline.markMoved(object->mDrawable); + gPipeline.markTextured(object->mDrawable); // face may need to change draw pool to/from POOL_HUD + object->mDrawable->setState(LLDrawable::USE_BACKLIGHT); if(mIsHUDAttachment) { - for (S32 face_num = 0; face_num < drawablep->getNumFaces(); face_num++) + for (S32 face_num = 0; face_num < object->mDrawable->getNumFaces(); face_num++) { - drawablep->getFace(face_num)->setState(LLFace::HUD_RENDER); + object->mDrawable->getFace(face_num)->setState(LLFace::HUD_RENDER); } } - LLViewerObject::const_child_list_t& child_list = mAttachedObject->getChildren(); + LLViewerObject::const_child_list_t& child_list = object->getChildren(); for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); iter != child_list.end(); iter++) { @@ -157,54 +164,18 @@ void LLViewerJointAttachment::setupDrawable(LLDrawable* drawablep) //----------------------------------------------------------------------------- BOOL LLViewerJointAttachment::addObject(LLViewerObject* object) { - if (mAttachedObject) - { - llwarns << "Attempted to attach object where an attachment already exists!" << llendl; - - if (mAttachedObject == object) { - llinfos << "(same object re-attached)" << llendl; - removeObject(mAttachedObject); - // Pass through anyway to let setupDrawable() - // re-connect object to the joint correctly - } - else { - llinfos << "(objects differ, removing existing object)" << llendl; - // Rather hacky, but no-one can think of something - // better to do for this case. - gObjectList.killObject(mAttachedObject); - // Proceed with new object attachment - } - } - mAttachedObject = object; - - LLUUID item_id; + object->extractAttachmentItemID(); - // Find the inventory item ID of the attached object - LLNameValue* item_id_nv = object->getNVPair("AttachItemID"); - if( item_id_nv ) + if (isObjectAttached(object)) { - const char* s = item_id_nv->getString(); - if( s ) - { - item_id.set( s ); - lldebugs << "getNVPair( AttachItemID ) = " << item_id << llendl; - } + llinfos << "(same object re-attached)" << llendl; + removeObject(object); + // Pass through anyway to let setupDrawable() + // re-connect object to the joint correctly } - mItemID = item_id; - - LLDrawable* drawablep = object->mDrawable; - - if (drawablep) - { - //if object is active, make it static - if(drawablep->isActive()) - { - drawablep->makeStatic(FALSE) ; - } - - setupDrawable(drawablep); - } + mAttachedObjects.push_back(object); + setupDrawable(object); if (mIsHUDAttachment) { @@ -234,9 +205,28 @@ BOOL LLViewerJointAttachment::addObject(LLViewerObject* object) //----------------------------------------------------------------------------- void LLViewerJointAttachment::removeObject(LLViewerObject *object) { + attachedobjs_vec_t::iterator iter; + for (iter = mAttachedObjects.begin(); + iter != mAttachedObjects.end(); + ++iter) + { + LLViewerObject *attached_object = (*iter); + if (attached_object == object) + { + break; + } + } + if (iter == mAttachedObjects.end()) + { + llwarns << "Could not find object to detach" << llendl; + return; + } + // force object visibile setAttachmentVisibility(TRUE); + mAttachedObjects.erase(iter); + if (object->mDrawable.notNull()) { //if object is active, make it static @@ -300,9 +290,11 @@ void LLViewerJointAttachment::removeObject(LLViewerObject *object) } } - mAttachedObject = NULL; - mUpdateXform = FALSE; - mItemID.setNull(); + if (mAttachedObjects.size() == 0) + { + mUpdateXform = FALSE; + } + object->setAttachmentItemID(LLUUID::null); } //----------------------------------------------------------------------------- @@ -310,20 +302,26 @@ void LLViewerJointAttachment::removeObject(LLViewerObject *object) //----------------------------------------------------------------------------- void LLViewerJointAttachment::setAttachmentVisibility(BOOL visible) { - if (!mAttachedObject || mAttachedObject->mDrawable.isNull() || - !(mAttachedObject->mDrawable->getSpatialBridge())) - return; - - if (visible) + for (attachedobjs_vec_t::const_iterator iter = mAttachedObjects.begin(); + iter != mAttachedObjects.end(); + ++iter) { - // Hack to make attachments not visible by disabling their type mask! - // This will break if you can ever attach non-volumes! - djs 02/14/03 - mAttachedObject->mDrawable->getSpatialBridge()->mDrawableType = - mAttachedObject->isHUDAttachment() ? LLPipeline::RENDER_TYPE_HUD : LLPipeline::RENDER_TYPE_VOLUME; - } - else - { - mAttachedObject->mDrawable->getSpatialBridge()->mDrawableType = 0; + LLViewerObject *attached_obj = (*iter); + if (!attached_obj || attached_obj->mDrawable.isNull() || + !(attached_obj->mDrawable->getSpatialBridge())) + continue; + + if (visible) + { + // Hack to make attachments not visible by disabling their type mask! + // This will break if you can ever attach non-volumes! - djs 02/14/03 + attached_obj->mDrawable->getSpatialBridge()->mDrawableType = + attached_obj->isHUDAttachment() ? LLPipeline::RENDER_TYPE_HUD : LLPipeline::RENDER_TYPE_VOLUME; + } + else + { + attached_obj->mDrawable->getSpatialBridge()->mDrawableType = 0; + } } } @@ -341,14 +339,19 @@ void LLViewerJointAttachment::setOriginalPosition(LLVector3& position) //----------------------------------------------------------------------------- void LLViewerJointAttachment::clampObjectPosition() { - if (mAttachedObject) + for (attachedobjs_vec_t::const_iterator iter = mAttachedObjects.begin(); + iter != mAttachedObjects.end(); + ++iter) { - // *NOTE: object can drift when hitting maximum radius - LLVector3 attachmentPos = mAttachedObject->getPosition(); - F32 dist = attachmentPos.normVec(); - dist = llmin(dist, MAX_ATTACHMENT_DIST); - attachmentPos *= dist; - mAttachedObject->setPosition(attachmentPos); + if (LLViewerObject *attached_object = (*iter)) + { + // *NOTE: object can drift when hitting maximum radius + LLVector3 attachmentPos = attached_object->getPosition(); + F32 dist = attachmentPos.normVec(); + dist = llmin(dist, MAX_ATTACHMENT_DIST); + attachmentPos *= dist; + attached_object->setPosition(attachmentPos); + } } } @@ -357,14 +360,23 @@ void LLViewerJointAttachment::clampObjectPosition() //----------------------------------------------------------------------------- void LLViewerJointAttachment::calcLOD() { - F32 maxarea = mAttachedObject->getMaxScale() * mAttachedObject->getMidScale(); - LLViewerObject::const_child_list_t& child_list = mAttachedObject->getChildren(); - for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); - iter != child_list.end(); iter++) + F32 maxarea = 0; + for (attachedobjs_vec_t::const_iterator iter = mAttachedObjects.begin(); + iter != mAttachedObjects.end(); + ++iter) { - LLViewerObject* childp = *iter; - F32 area = childp->getMaxScale() * childp->getMidScale(); - maxarea = llmax(maxarea, area); + if (LLViewerObject *attached_object = (*iter)) + { + maxarea = llmax(maxarea,attached_object->getMaxScale() * attached_object->getMidScale()); + LLViewerObject::const_child_list_t& child_list = attached_object->getChildren(); + for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); + iter != child_list.end(); ++iter) + { + LLViewerObject* childp = *iter; + F32 area = childp->getMaxScale() * childp->getMidScale(); + maxarea = llmax(maxarea, area); + } + } } maxarea = llclamp(maxarea, .01f*.01f, 1.f); F32 avatar_area = (4.f * 4.f); // pixels for an avatar sized attachment @@ -386,3 +398,47 @@ BOOL LLViewerJointAttachment::updateLOD(F32 pixel_area, BOOL activate) return res; } +BOOL LLViewerJointAttachment::isObjectAttached(const LLViewerObject *viewer_object) const +{ + for (attachedobjs_vec_t::const_iterator iter = mAttachedObjects.begin(); + iter != mAttachedObjects.end(); + ++iter) + { + const LLViewerObject* attached_object = (*iter); + if (attached_object == viewer_object) + { + return TRUE; + } + } + return FALSE; +} + +const LLViewerObject *LLViewerJointAttachment::getAttachedObject(const LLUUID &object_id) const +{ + for (attachedobjs_vec_t::const_iterator iter = mAttachedObjects.begin(); + iter != mAttachedObjects.end(); + ++iter) + { + const LLViewerObject* attached_object = (*iter); + if (attached_object->getAttachmentItemID() == object_id) + { + return attached_object; + } + } + return NULL; +} + +LLViewerObject *LLViewerJointAttachment::getAttachedObject(const LLUUID &object_id) +{ + for (attachedobjs_vec_t::iterator iter = mAttachedObjects.begin(); + iter != mAttachedObjects.end(); + ++iter) + { + LLViewerObject* attached_object = (*iter); + if (attached_object->getAttachmentItemID() == object_id) + { + return attached_object; + } + } + return NULL; +} \ No newline at end of file diff --git a/indra/newview/llviewerjointattachment.h b/indra/newview/llviewerjointattachment.h index 4847ac7a7..ba3ffe84e 100644 --- a/indra/newview/llviewerjointattachment.h +++ b/indra/newview/llviewerjointattachment.h @@ -82,9 +82,7 @@ public: S32 getGroup() { return mGroup; } S32 getPieSlice() { return mPieSlice; } - LLViewerObject *getObject() { return mAttachedObject; } - S32 getNumObjects() { return (mAttachedObject ? 1 : 0); } - const LLUUID& getItemID() { return mItemID; } + S32 getNumObjects() { return mAttachedObjects.size(); } // // unique methods @@ -92,21 +90,30 @@ public: BOOL addObject(LLViewerObject* object); void removeObject(LLViewerObject *object); - void setupDrawable(LLDrawable* drawable); void clampObjectPosition(); + // + // attachments operations + // + BOOL isObjectAttached(const LLViewerObject *viewer_object) const; + const LLViewerObject *getAttachedObject(const LLUUID &object_id) const; + LLViewerObject *getAttachedObject(const LLUUID &object_id); + + // list of attachments for this joint + typedef std::vector attachedobjs_vec_t; + attachedobjs_vec_t mAttachedObjects; + + protected: void calcLOD(); + void setupDrawable(LLViewerObject *object); protected: - // Backlink only; don't make this an LLPointer. - LLViewerObject* mAttachedObject; BOOL mVisibleInFirst; LLVector3 mOriginalPos; S32 mGroup; BOOL mIsHUDAttachment; S32 mPieSlice; - LLUUID mItemID; // Inventory item id of the attached item (null if not in inventory) }; #endif // LL_LLVIEWERJOINTATTACHMENT_H diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index df56c3d63..f8c9b8d6d 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -2208,7 +2208,7 @@ class LLSelfEnableRemoveAllAttachments : public view_listener_t { LLVOAvatar::attachment_map_t::iterator curiter = iter++; LLViewerJointAttachment* attachment = curiter->second; - if (attachment->getObject()) + if (attachment->getNumObjects() > 0) { new_value = true; break; @@ -6636,35 +6636,46 @@ class LLAttachmentDrop : public view_listener_t // called from avatar pie menu void handle_detach_from_avatar(void* user_data) { - LLViewerJointAttachment *attachment = (LLViewerJointAttachment *)user_data; + LLViewerJointAttachment *attachment = (LLViewerJointAttachment*)user_data; - LLViewerObject* attached_object = attachment->getObject(); - - if (attached_object) + if (attachment->getNumObjects() > 0) { gMessageSystem->newMessage("ObjectDetach"); gMessageSystem->nextBlockFast(_PREHASH_AgentData); gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - gMessageSystem->nextBlockFast(_PREHASH_ObjectData); - gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, attached_object->getLocalID()); + for (LLViewerJointAttachment::attachedobjs_vec_t::const_iterator iter = attachment->mAttachedObjects.begin(); + iter != attachment->mAttachedObjects.end(); + iter++) + { + LLViewerObject *attached_object = (*iter); + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, attached_object->getLocalID()); + } gMessageSystem->sendReliable( gAgent.getRegionHost() ); } } void attach_label(std::string& label, void* user_data) { - LLViewerJointAttachment* attachmentp = (LLViewerJointAttachment*)user_data; - if (attachmentp) + LLViewerJointAttachment *attachment = (LLViewerJointAttachment*)user_data; + if (attachment) { - label = attachmentp->getName(); - if (attachmentp->getObject()) + label = attachment->getName(); + for (LLViewerJointAttachment::attachedobjs_vec_t::const_iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) { - LLViewerInventoryItem* itemp = gInventory.getItem(attachmentp->getItemID()); - if (itemp) + const LLViewerObject* attached_object = (*attachment_iter); + if (attached_object) { - label += std::string(" (") + itemp->getName() + std::string(")"); + LLViewerInventoryItem* itemp = gInventory.getItem(attached_object->getAttachmentItemID()); + if (itemp) + { + label += std::string(" (") + itemp->getName() + std::string(")"); + break; + } } } } @@ -6672,16 +6683,23 @@ void attach_label(std::string& label, void* user_data) void detach_label(std::string& label, void* user_data) { - LLViewerJointAttachment* attachmentp = (LLViewerJointAttachment*)user_data; - if (attachmentp) + LLViewerJointAttachment *attachment = (LLViewerJointAttachment*)user_data; + if (attachment) { - label = attachmentp->getName(); - if (attachmentp->getObject()) + label = attachment->getName(); + for (LLViewerJointAttachment::attachedobjs_vec_t::const_iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) { - LLViewerInventoryItem* itemp = gInventory.getItem(attachmentp->getItemID()); - if (itemp) + const LLViewerObject* attached_object = (*attachment_iter); + if (attached_object) { - label += std::string(" (") + itemp->getName() + std::string(")"); + LLViewerInventoryItem* itemp = gInventory.getItem(attached_object->getAttachmentItemID()); + if (itemp) + { + label += std::string(" (") + itemp->getName() + std::string(")"); + break; + } } } } @@ -6784,23 +6802,27 @@ class LLAttachmentEnableDrop : public view_listener_t if ( attachment_pt ) { - // make sure item is in your inventory (it could be a delayed attach message being sent from the sim) - // so check to see if the item is in the inventory already - item = gInventory.getItem(attachment_pt->getItemID()); - - if ( !item ) + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment_pt->mAttachedObjects.begin(); + attachment_iter != attachment_pt->mAttachedObjects.end(); + ++attachment_iter) { - // Item does not exist, make an observer to enable the pie menu - // when the item finishes fetching worst case scenario - // if a fetch is already out there (being sent from a slow sim) - // we refetch and there are 2 fetches - LLWornItemFetchedObserver* wornItemFetched = new LLWornItemFetchedObserver(); - LLInventoryFetchObserver::item_ref_t items; //add item to the inventory item to be fetched + // make sure item is in your inventory (it could be a delayed attach message being sent from the sim) + // so check to see if the item is in the inventory already + item = gInventory.getItem((*attachment_iter)->getAttachmentItemID()); + if (!item) + { + // Item does not exist, make an observer to enable the pie menu + // when the item finishes fetching worst case scenario + // if a fetch is already out there (being sent from a slow sim) + // we refetch and there are 2 fetches + LLWornItemFetchedObserver* wornItemFetched = new LLWornItemFetchedObserver(); + LLInventoryFetchObserver::item_ref_t items; //add item to the inventory item to be fetched - items.push_back(attachment_pt->getItemID()); - - wornItemFetched->fetchItems(items); - gInventory.addObserver(wornItemFetched); + items.push_back((*attachment_iter)->getAttachmentItemID()); + + wornItemFetched->fetchItems(items); + gInventory.addObserver(wornItemFetched); + } } } } @@ -6919,7 +6941,7 @@ BOOL object_attached(void *user_data) { LLViewerJointAttachment *attachment = (LLViewerJointAttachment *)user_data; - return attachment->getObject() != NULL; + return attachment->getNumObjects() > 0; } class LLAvatarSendIM : public view_listener_t @@ -7187,17 +7209,23 @@ void handle_dump_attachments(void*) LLVOAvatar::attachment_map_t::iterator curiter = iter++; LLViewerJointAttachment* attachment = curiter->second; S32 key = curiter->first; - BOOL visible = (attachment->getObject() != NULL && - attachment->getObject()->mDrawable.notNull() && - !attachment->getObject()->mDrawable->isRenderType(0)); - LLVector3 pos; - if (visible) pos = attachment->getObject()->mDrawable->getPosition(); - llinfos << "ATTACHMENT " << key << ": item_id=" << attachment->getItemID() - << (attachment->getObject() ? " present " : " absent ") - << (visible ? "visible " : "invisible ") - << " at " << pos - << " and " << (visible ? attachment->getObject()->getPosition() : LLVector3::zero) - << llendl; + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) + { + LLViewerObject *attached_object = (*attachment_iter); + BOOL visible = (attached_object != NULL && + attached_object->mDrawable.notNull() && + !attached_object->mDrawable->isRenderType(0)); + LLVector3 pos; + if (visible) pos = attached_object->mDrawable->getPosition(); + llinfos << "ATTACHMENT " << key << ": item_id=" << attached_object->getAttachmentItemID() + << (attached_object ? " present " : " absent ") + << (visible ? "visible " : "invisible ") + << " at " << pos + << " and " << (visible ? attached_object->getPosition() : LLVector3::zero) + << llendl; + } } } diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 7d654aef2..557e0e7e0 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -203,7 +203,8 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe mJointInfo(NULL), mState(0), mMedia(NULL), - mClickAction(0) + mClickAction(0), + mAttachmentItemID(LLUUID::null) { if(!is_global) { @@ -504,9 +505,20 @@ BOOL LLViewerObject::isOverGroupOwnedLand() const && mRegionp->getParcelOverlay()->isOwnedGroup(getPositionRegion()); } -void LLViewerObject::setParent(LLViewerObject* parent) +BOOL LLViewerObject::setParent(LLViewerObject* parent) { - LLPrimitive::setParent(parent); + if (mParent != parent) + { + LLViewerObject* old_parent = (LLViewerObject*)mParent ; + BOOL ret = LLPrimitive::setParent(parent); + if (ret && old_parent && parent) + { + old_parent->removeChild(this) ; + } + return ret ; + } + + return FALSE ; } void LLViewerObject::addChild(LLViewerObject *childp) @@ -525,8 +537,10 @@ void LLViewerObject::addChild(LLViewerObject *childp) childp->mbCanSelect = mbCanSelect; } - childp->setParent(this); - mChildList.push_back(childp); + if (childp->setParent(this)) + { + mChildList.push_back(childp); + } } void LLViewerObject::removeChild(LLViewerObject *childp) @@ -622,12 +636,16 @@ BOOL LLViewerObject::setDrawableParent(LLDrawable* parentp) { return FALSE; } + BOOL ret = mDrawable->mXform.setParent(parentp ? &parentp->mXform : NULL); + if (!ret) + { + return FALSE ; + } LLDrawable* old_parent = mDrawable->mParent; mDrawable->mParent = parentp; - BOOL ret = mDrawable->mXform.setParent(parentp ? &parentp->mXform : NULL); gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE); if( (old_parent != parentp && old_parent) || (parentp && parentp->isActive())) @@ -5177,3 +5195,19 @@ std::string LLViewerObject::getAttachmentPointName() return llformat("unsupported point %d", point); } // + +const LLUUID &LLViewerObject::extractAttachmentItemID() +{ + LLUUID item_id = LLUUID::null; + LLNameValue* item_id_nv = getNVPair("AttachItemID"); + if (item_id_nv) + { + const char* s = item_id_nv->getString(); + if (s) + { + item_id.set(s); + } + } + setAttachmentItemID(item_id); + return getAttachmentItemID(); +} \ No newline at end of file diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index ace0c2354..9571dbb55 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -236,7 +236,7 @@ public: BOOL isProbablyModifiable() const; */ - virtual void setParent(LLViewerObject* parent); + virtual BOOL setParent(LLViewerObject* parent); virtual void addChild(LLViewerObject *childp); virtual void removeChild(LLViewerObject *childp); const_child_list_t& getChildren() const { return mChildList; } @@ -662,6 +662,12 @@ protected: private: static S32 sNumObjects; +public: + const LLUUID &getAttachmentItemID() const { return mAttachmentItemID; } + void setAttachmentItemID(const LLUUID &id) { mAttachmentItemID = id; } + const LLUUID &extractAttachmentItemID(); // find&set the inventory item ID of the attached object +private: + LLUUID mAttachmentItemID; // ItemID when item is in user inventory. // public: S32 getAttachmentPoint(); diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index f30cd53fe..3a1b70a03 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -1241,18 +1241,22 @@ void LLViewerObjectList::generatePickList(LLCamera &camera) LLViewerJointAttachment* attachmentp = curiter->second; if (attachmentp->getIsHUDAttachment()) { - LLViewerObject* objectp = attachmentp->getObject(); - if (objectp) + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachmentp->mAttachedObjects.begin(); + attachment_iter != attachmentp->mAttachedObjects.end(); + ++attachment_iter) { - mSelectPickList.insert(objectp); - LLViewerObject::const_child_list_t& child_list = objectp->getChildren(); - for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); - iter != child_list.end(); iter++) + if (LLViewerObject* objectp = (*attachment_iter)) { - LLViewerObject* childp = *iter; - if (childp) + mSelectPickList.insert(objectp); + LLViewerObject::const_child_list_t& child_list = objectp->getChildren(); + for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); + iter != child_list.end(); iter++) { - mSelectPickList.insert(childp); + LLViewerObject* childp = *iter; + if (childp) + { + mSelectPickList.insert(childp); + } } } } diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 6ad5cd0dc..94a5bb653 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -1635,27 +1635,32 @@ void LLVOAvatar::getSpatialExtents(LLVector3& newMin, LLVector3& newMax) continue ; } - LLViewerObject* object = attachment->getObject(); - if (object && !object->isHUDAttachment()) + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) { - LLDrawable* drawable = object->mDrawable; - if (drawable) + const LLViewerObject* attached_object = (*attachment_iter); + if (attached_object && !attached_object->isHUDAttachment()) { - LLSpatialBridge* bridge = drawable->getSpatialBridge(); - if (bridge) + LLDrawable* drawable = attached_object->mDrawable; + if (drawable) { - const LLVector3* ext = bridge->getSpatialExtents(); - LLVector3 distance = (ext[1] - ext[0]); - - // Only add the prim to spatial extents calculations if it isn't a megaprim. - // max_attachment_span calculated at the start of the function - // (currently 5 times our max prim size) - if (distance.mV[0] < max_attachment_span - && distance.mV[1] < max_attachment_span - && distance.mV[2] < max_attachment_span) + LLSpatialBridge* bridge = drawable->getSpatialBridge(); + if (bridge) { - update_min_max(newMin,newMax,ext[0]); - update_min_max(newMin,newMax,ext[1]); + const LLVector3* ext = bridge->getSpatialExtents(); + LLVector3 distance = (ext[1] - ext[0]); + + // Only add the prim to spatial extents calculations if it isn't a megaprim. + // max_attachment_span calculated at the start of the function + // (currently 5 times our max prim size) + if (distance.mV[0] < max_attachment_span + && distance.mV[1] < max_attachment_span + && distance.mV[2] < max_attachment_span) + { + update_min_max(newMin,newMax,ext[0]); + update_min_max(newMin,newMax,ext[1]); + } } } } @@ -2867,30 +2872,32 @@ void LLVOAvatar::idleUpdateMisc(bool detailed_update) { attachment_map_t::iterator curiter = iter++; LLViewerJointAttachment* attachment = curiter->second; - LLViewerObject *attached_object = attachment->getObject(); - - BOOL visibleAttachment = visible || (attached_object && - !(attached_object->mDrawable->getSpatialBridge() && - attached_object->mDrawable->getSpatialBridge()->getRadius() < 2.0)); - - if (visibleAttachment && attached_object && !attached_object->isDead() && attachment->getValid()) + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) { - // if selecting any attachments, update all of them as non-damped - if (LLSelectMgr::getInstance()->getSelection()->getObjectCount() && LLSelectMgr::getInstance()->getSelection()->isAttachment()) - { - gPipeline.updateMoveNormalAsync(attached_object->mDrawable); + LLViewerObject* attached_object = (*attachment_iter); + BOOL visibleAttachment = visible || (attached_object && + !(attached_object->mDrawable->getSpatialBridge() && + attached_object->mDrawable->getSpatialBridge()->getRadius() < 2.0)); + if (visibleAttachment && attached_object && !attached_object->isDead() && attachment->getValid()) + { + // if selecting any attachments, update all of them as non-damped + if (LLSelectMgr::getInstance()->getSelection()->getObjectCount() && LLSelectMgr::getInstance()->getSelection()->isAttachment()) + { + gPipeline.updateMoveNormalAsync(attached_object->mDrawable); + } + else + { + gPipeline.updateMoveDampedAsync(attached_object->mDrawable); + } + LLSpatialBridge* bridge = attached_object->mDrawable->getSpatialBridge(); + if (bridge) + { + gPipeline.updateMoveNormalAsync(bridge); + } + attached_object->updateText(); } - else - { - gPipeline.updateMoveDampedAsync(attached_object->mDrawable); - } - - LLSpatialBridge* bridge = attached_object->mDrawable->getSpatialBridge(); - if (bridge) - { - gPipeline.updateMoveNormalAsync(bridge); - } - attached_object->updateText(); } } } @@ -4727,19 +4734,24 @@ void LLVOAvatar::updateVisibility() /*llinfos << "SPA: " << sel_pos_agent << llendl; llinfos << "WPA: " << wrist_right_pos_agent << llendl;*/ for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); - iter != mAttachmentPoints.end(); ) + iter != mAttachmentPoints.end(); iter++) { - attachment_map_t::iterator curiter = iter++; - LLViewerJointAttachment* attachment = curiter->second; - if (attachment->getObject()) + LLViewerJointAttachment* attachment = iter->second; + + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) { - if(attachment->getObject()->mDrawable->isVisible()) + if (LLViewerObject *attached_object = (*attachment_iter)) { - llinfos << attachment->getName() << " visible" << llendl; - } - else - { - llinfos << attachment->getName() << " not visible at " << mDrawable->getWorldPosition() << " and radius " << mDrawable->getRadius() << llendl; + if (attached_object->mDrawable->isVisible()) + { + llinfos << attachment->getName() << " visible" << llendl; + } + else + { + llinfos << attachment->getName() << " not visible at " << mDrawable->getWorldPosition() << " and radius " << mDrawable->getRadius() << llendl; + } } } } @@ -6882,12 +6894,13 @@ void LLVOAvatar::requestLayerSetUpdate(ETextureIndex index ) } } -void LLVOAvatar::setParent(LLViewerObject* parent) +BOOL LLVOAvatar::setParent(LLViewerObject* parent) { + BOOL ret ; if (parent == NULL) { getOffObject(); - LLViewerObject::setParent(parent); + ret = LLViewerObject::setParent(parent); if (isSelf()) { gAgent.resetCamera(); @@ -6895,13 +6908,18 @@ void LLVOAvatar::setParent(LLViewerObject* parent) } else { - LLViewerObject::setParent(parent); - sitOnObject(parent); - } + ret = LLViewerObject::setParent(parent); + if (ret) + { + sitOnObject(parent); + } + } + return ret ; } void LLVOAvatar::addChild(LLViewerObject *childp) { + childp->extractAttachmentItemID(); // find the inventory item this object is associated with. LLViewerObject::addChild(childp); if (childp->mDrawable) { @@ -6923,11 +6941,20 @@ LLViewerJointAttachment* LLVOAvatar::getTargetAttachmentPoint(LLViewerObject* vi { S32 attachmentID = ATTACHMENT_ID_FROM_STATE(viewer_object->getState()); + // This should never happen unless the server didn't process the attachment point + // correctly, but putting this check in here to be safe. + if (attachmentID & ATTACHMENT_ADD) + { + llwarns << "Got an attachment with ATTACHMENT_ADD mask, removing ( attach pt:" << attachmentID << " )" << llendl; + attachmentID &= ~ATTACHMENT_ADD; + } + LLViewerJointAttachment* attachment = get_if_there(mAttachmentPoints, attachmentID, (LLViewerJointAttachment*)NULL); if (!attachment) { llwarns << "Object attachment point invalid: " << attachmentID << llendl; + attachment = get_if_there(mAttachmentPoints, 1, (LLViewerJointAttachment*)NULL); // Arbitrary using 1 (chest) } return attachment; @@ -6966,7 +6993,7 @@ BOOL LLVOAvatar::attachObject(LLViewerObject *viewer_object) updateAttachmentVisibility(gAgent.getCameraMode()); // Then make sure the inventory is in sync with the avatar. - gInventory.addChangedMask( LLInventoryObserver::LABEL, item_id ); + gInventory.addChangedMask(LLInventoryObserver::LABEL, viewer_object->getAttachmentItemID()); gInventory.notifyObservers(); } } @@ -6990,13 +7017,34 @@ BOOL LLVOAvatar::attachObject(LLViewerObject *viewer_object) updateAttachmentVisibility(gAgent.getCameraMode()); // Then make sure the inventory is in sync with the avatar. - gInventory.addChangedMask( LLInventoryObserver::LABEL, attachment->getItemID() ); + gInventory.addChangedMask(LLInventoryObserver::LABEL, viewer_object->getAttachmentItemID()); gInventory.notifyObservers(); } return TRUE; } +U32 LLVOAvatar::getNumAttachments() const +{ + U32 num_attachments = 0; + for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); + iter != mAttachmentPoints.end(); + ++iter) + { + LLViewerJointAttachment *attachment_pt = (*iter).second; + num_attachments += attachment_pt->getNumObjects(); + } + return num_attachments; +} + +//----------------------------------------------------------------------------- +// canAttachMoreObjects() +//----------------------------------------------------------------------------- +BOOL LLVOAvatar::canAttachMoreObjects() const +{ + return (getNumAttachments() < MAX_AGENT_ATTACHMENTS); +} + //----------------------------------------------------------------------------- // lazyAttach() //----------------------------------------------------------------------------- @@ -7022,16 +7070,20 @@ void LLVOAvatar::lazyAttach() void LLVOAvatar::resetHUDAttachments() { for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); - iter != mAttachmentPoints.end(); ) + iter != mAttachmentPoints.end(); iter++) { - attachment_map_t::iterator curiter = iter++; - LLViewerJointAttachment* attachment = curiter->second; + LLViewerJointAttachment* attachment = iter->second; if (attachment->getIsHUDAttachment()) { - LLViewerObject* obj = attachment->getObject(); - if (obj && obj->mDrawable.notNull()) + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) { - gPipeline.markMoved(obj->mDrawable); + const LLViewerObject* attached_object = (*attachment_iter); + if (attached_object && attached_object->mDrawable.notNull()) + { + gPipeline.markMoved(attached_object->mDrawable); + } } } } @@ -7048,9 +7100,9 @@ BOOL LLVOAvatar::detachObject(LLViewerObject *viewer_object) attachment_map_t::iterator curiter = iter++; LLViewerJointAttachment* attachment = curiter->second; // only one object per attachment point for now - if (attachment->getObject() == viewer_object) + if (attachment->isObjectAttached(viewer_object)) { - LLUUID item_id = attachment->getItemID(); + LLUUID item_id = viewer_object->getAttachmentItemID(); attachment->removeObject(viewer_object); if (mIsSelf) { @@ -7315,14 +7367,15 @@ LLVOAvatar* LLVOAvatar::findAvatarFromAttachment( LLViewerObject* obj ) //----------------------------------------------------------------------------- // isWearingAttachment() //----------------------------------------------------------------------------- -BOOL LLVOAvatar::isWearingAttachment( const LLUUID& inv_item_id ) +BOOL LLVOAvatar::isWearingAttachment(const LLUUID& inv_item_id) { + const LLUUID& base_inv_item_id = gInventory.getLinkedItemID(inv_item_id); for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ) { attachment_map_t::iterator curiter = iter++; LLViewerJointAttachment* attachment = curiter->second; - if( attachment->getItemID() == inv_item_id ) + if(attachment->getAttachedObject(base_inv_item_id)) { return TRUE; } @@ -7346,16 +7399,17 @@ BOOL LLVOAvatar::isWearingUnsupportedAttachment( const LLUUID& inv_item_id ) //----------------------------------------------------------------------------- // getWornAttachment() //----------------------------------------------------------------------------- -LLViewerObject* LLVOAvatar::getWornAttachment( const LLUUID& inv_item_id ) +LLViewerObject* LLVOAvatar::getWornAttachment(const LLUUID& inv_item_id) { + const LLUUID& base_inv_item_id = gInventory.getLinkedItemID(inv_item_id); for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ) { attachment_map_t::iterator curiter = iter++; LLViewerJointAttachment* attachment = curiter->second; - if( attachment->getItemID() == inv_item_id ) + if (LLViewerObject *attached_object = attachment->getAttachedObject(base_inv_item_id)) { - return attachment->getObject(); + return attached_object; } } return NULL; @@ -7363,12 +7417,13 @@ LLViewerObject* LLVOAvatar::getWornAttachment( const LLUUID& inv_item_id ) const std::string LLVOAvatar::getAttachedPointName(const LLUUID& inv_item_id) { + const LLUUID& base_inv_item_id = gInventory.getLinkedItemID(inv_item_id); for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ) { attachment_map_t::iterator curiter = iter++; LLViewerJointAttachment* attachment = curiter->second; - if( attachment->getItemID() == inv_item_id ) + if (attachment->getAttachedObject(base_inv_item_id)) { return attachment->getName(); } @@ -8901,11 +8956,10 @@ void LLVOAvatar::clampAttachmentPositions() BOOL LLVOAvatar::hasHUDAttachment() const { for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); - iter != mAttachmentPoints.end(); ) + iter != mAttachmentPoints.end(); ++iter) { - attachment_map_t::const_iterator curiter = iter++; - LLViewerJointAttachment* attachment = curiter->second; - if (attachment->getIsHUDAttachment() && attachment->getObject()) + LLViewerJointAttachment* attachment = iter->second; + if (attachment->getIsHUDAttachment() && attachment->getNumObjects() > 0) { return TRUE; } @@ -8917,24 +8971,33 @@ LLBBox LLVOAvatar::getHUDBBox() const { LLBBox bbox; for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); - iter != mAttachmentPoints.end(); ) + iter != mAttachmentPoints.end(); ++iter) { - attachment_map_t::const_iterator curiter = iter++; - LLViewerJointAttachment* attachment = curiter->second; - if (attachment->getIsHUDAttachment() && attachment->getObject()) + LLViewerJointAttachment* attachment = iter->second; + if (attachment->getIsHUDAttachment()) { - LLViewerObject* hud_object = attachment->getObject(); - - // initialize bounding box to contain identity orientation and center point for attached object - bbox.addPointLocal(hud_object->getPosition()); - // add rotated bounding box for attached object - bbox.addBBoxAgent(hud_object->getBoundingBoxAgent()); - LLViewerObject::const_child_list_t& child_list = hud_object->getChildren(); - for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); - iter != child_list.end(); iter++) + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) { - LLViewerObject* child_objectp = *iter; - bbox.addBBoxAgent(child_objectp->getBoundingBoxAgent()); + const LLViewerObject* attached_object = (*attachment_iter); + if (attached_object == NULL) + { + llwarns << "HUD attached object is NULL!" << llendl; + continue; + } + // initialize bounding box to contain identity orientation and center point for attached object + bbox.addPointLocal(attached_object->getPosition()); + // add rotated bounding box for attached object + bbox.addBBoxAgent(attached_object->getBoundingBoxAgent()); + LLViewerObject::const_child_list_t& child_list = attached_object->getChildren(); + for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); + iter != child_list.end(); + ++iter) + { + const LLViewerObject* child_objectp = *iter; + bbox.addBBoxAgent(child_objectp->getBoundingBoxAgent()); + } } } } @@ -10367,20 +10430,25 @@ void LLVOAvatar::idleUpdateRenderCost() ++iter) { LLViewerJointAttachment* attachment = iter->second; - LLViewerObject* object = attachment->getObject(); - if (object && !object->isHUDAttachment()) + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) { - LLDrawable* drawable = object->mDrawable; - if (drawable) + const LLViewerObject* attached_object = (*attachment_iter); + if (attached_object && !attached_object->isHUDAttachment()) { - shame += 10; - LLVOVolume* volume = drawable->getVOVolume(); - if (volume) + const LLDrawable* drawable = attached_object->mDrawable; + if (drawable) { - shame += calc_shame(volume, textures); - } - } - } + shame += 10; + LLVOVolume* volume = drawable->getVOVolume(); + if (volume) + { + shame += calc_shame(volume, textures); + } + } + } + } } if(sDoProperArc) diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index cedd20df2..82e135593 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -155,6 +155,7 @@ public: void updateAttachmentVisibility(U32 camera_mode); void clampAttachmentPositions(); S32 getAttachmentCount(); // Warning: order(N) not order(1) + BOOL canAttachMoreObjects() const; // HUD functions BOOL hasHUDAttachment() const; @@ -292,7 +293,7 @@ public: void hideSkirt(); - virtual void setParent(LLViewerObject* parent); + virtual BOOL setParent(LLViewerObject* parent); virtual void addChild(LLViewerObject *childp); virtual void removeChild(LLViewerObject *childp); @@ -587,7 +588,8 @@ public: // std::map mUnsupportedAttachmentPoints; // - +protected: + U32 getNumAttachments() const; // O(N), not O(1) <---- Fix if possible, I guess it's not worst case scenario - HgB //-------------------------------------------------------------------- // static preferences that are controlled by user settings/menus //-------------------------------------------------------------------- diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index d1d4a633b..6132094ae 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -881,17 +881,19 @@ void LLVOVolume::updateFaceFlags() } } -void LLVOVolume::setParent(LLViewerObject* parent) +BOOL LLVOVolume::setParent(LLViewerObject* parent) { + BOOL ret = FALSE; if (parent != getParent()) { - LLViewerObject::setParent(parent); + ret = LLViewerObject::setParent(parent); if (mDrawable) { gPipeline.markMoved(mDrawable); gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE); } } + return ret; } // NOTE: regenFaces() MUST be followed by genTriangles()! diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index 960f6da80..8cfb9698b 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -105,7 +105,7 @@ public: /*virtual*/ BOOL isHUDAttachment() const; void generateSilhouette(LLSelectNode* nodep, const LLVector3& view_point); - /*virtual*/ void setParent(LLViewerObject* parent); + /*virtual*/ BOOL setParent(LLViewerObject* parent); S32 getLOD() const { return mLOD; } const LLVector3 getPivotPositionAgent() const; const LLMatrix4& getRelativeXform() const { return mRelativeXform; } diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 446bb8713..86a561da9 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -3344,39 +3344,41 @@ void LLPipeline::renderForSelect(std::set& objects, BOOL render LLViewerJointAttachment* attachmentp = curiter->second; if (attachmentp->getIsHUDAttachment()) { - LLViewerObject* objectp = attachmentp->getObject(); - if (objectp) + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachmentp->mAttachedObjects.begin(); + attachment_iter != attachmentp->mAttachedObjects.end(); + ++attachment_iter) { - LLDrawable* drawable = objectp->mDrawable; - if (drawable->isDead()) + if (LLViewerObject* objectp = (*attachment_iter)) { - continue; - } - - for (S32 j = 0; j < drawable->getNumFaces(); ++j) - { - LLFace* facep = drawable->getFace(j); - if (!facep->getPool()) + LLDrawable* drawable = objectp->mDrawable; + if (drawable->isDead()) { - facep->renderForSelect(prim_mask); + continue; } - } - - //render child faces - LLViewerObject::const_child_list_t& child_list = objectp->getChildren(); - for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); - iter != child_list.end(); iter++) - { - LLViewerObject* child = *iter; - LLDrawable* child_drawable = child->mDrawable; - for (S32 l = 0; l < child_drawable->getNumFaces(); ++l) + for (S32 j = 0; j < drawable->getNumFaces(); ++j) { - LLFace* facep = child_drawable->getFace(l); + LLFace* facep = drawable->getFace(j); if (!facep->getPool()) { facep->renderForSelect(prim_mask); } } + //render child faces + LLViewerObject::const_child_list_t& child_list = objectp->getChildren(); + for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); + iter != child_list.end(); iter++) + { + LLViewerObject* child = *iter; + LLDrawable* child_drawable = child->mDrawable; + for (S32 l = 0; l < child_drawable->getNumFaces(); ++l) + { + LLFace* facep = child_drawable->getFace(l); + if (!facep->getPool()) + { + facep->renderForSelect(prim_mask); + } + } + } } } } @@ -4846,7 +4848,7 @@ void LLPipeline::resetVertexBuffers(LLDrawable* drawable) void LLPipeline::resetVertexBuffers() { sRenderBump = gSavedSettings.getBOOL("RenderObjectBump"); - LLVertexBuffer::sUseStreamDraw = gSavedSettings.getBOOL("RenderUseStreamVBO"); + LLVertexBuffer::sUseStreamDraw = gSavedSettings.getBOOL("ShyotlRenderUseStreamVBO"); for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); iter != LLWorld::getInstance()->getRegionList().end(); ++iter) { @@ -6508,10 +6510,15 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar) iter != avatar->mAttachmentPoints.end(); ++iter) { - LLViewerObject* object = iter->second->getObject(); - if (object) + LLViewerJointAttachment *attachment = iter->second; + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) { - markVisible(object->mDrawable->getSpatialBridge(), *LLViewerCamera::getInstance()); + if (LLViewerObject* attached_object = (*attachment_iter)) + { + markVisible(attached_object->mDrawable->getSpatialBridge(), *LLViewerCamera::getInstance()); + } } } diff --git a/indra/newview/scriptcounter.cpp b/indra/newview/scriptcounter.cpp index 5bb514083..ddb73a1eb 100644 --- a/indra/newview/scriptcounter.cpp +++ b/indra/newview/scriptcounter.cpp @@ -170,11 +170,16 @@ void ScriptCounter::serializeSelection(bool delScript) LLViewerJointAttachment* attachment = iter->second; if (!attachment->getValid()) continue ; - LLViewerObject* object = attachment->getObject(); - if(object) + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) { - objectArray.put(object); - objectCount++; + LLViewerObject *attached_object = (*attachment_iter); + if (attached_object) + { + objectArray.put(attached_object); + objectCount++; + } } } } diff --git a/indra/newview/skins/default/xui/en-us/floater_new_outfit_dialog.xml b/indra/newview/skins/default/xui/en-us/floater_new_outfit_dialog.xml index 4c9c443bd..b9a17b05b 100644 --- a/indra/newview/skins/default/xui/en-us/floater_new_outfit_dialog.xml +++ b/indra/newview/skins/default/xui/en-us/floater_new_outfit_dialog.xml @@ -24,6 +24,10 @@ + diff --git a/indra/newview/skins/default/xui/en-us/menu_inventory.xml b/indra/newview/skins/default/xui/en-us/menu_inventory.xml index a0250baec..7a1047e05 100644 --- a/indra/newview/skins/default/xui/en-us/menu_inventory.xml +++ b/indra/newview/skins/default/xui/en-us/menu_inventory.xml @@ -129,6 +129,10 @@ name="Sound Open" width="128"> + + + @@ -186,6 +190,10 @@ name="Paste" width="128"> + + + @@ -251,10 +259,6 @@ - - - @@ -264,11 +268,19 @@ name="Object Wear" width="128"> + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/notifications.xml b/indra/newview/skins/default/xui/en-us/notifications.xml index c2386b934..29ddeb53b 100644 --- a/indra/newview/skins/default/xui/en-us/notifications.xml +++ b/indra/newview/skins/default/xui/en-us/notifications.xml @@ -798,6 +798,14 @@ The outfit folder contains no clothing, body parts, or attachments. You can not wear clothes or body parts that are in the trash + +Could not attach object. +Exceeds the attachments limit of [MAX_ATTACHMENTS] objects. Please detach another object first. + + + diff --git a/scripts/messages/message_template.msg b/scripts/messages/message_template.msg index 7460128a0..3b9b5e507 100644 --- a/scripts/messages/message_template.msg +++ b/scripts/messages/message_template.msg @@ -8934,3 +8934,24 @@ version 2.0 { CRC U32 } } } + +// Link inventory +{ + LinkInventoryItem Low 426 NotTrusted Zerocoded + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + } + { + InventoryBlock Single + { CallbackID U32 } // Async Response + { FolderID LLUUID } + { TransactionID LLUUID } // Going to become TransactionID + { OldItemID LLUUID } + { Type S8 } + { InvType S8 } + { Name Variable 1 } + { Description Variable 1 } + } +}