From 9366bc66b344465484128584217e1622d727deed Mon Sep 17 00:00:00 2001 From: cinder Date: Wed, 25 Dec 2024 16:17:37 -0600 Subject: [PATCH] Add Upload, Change, and Remove profile image commands by request. Right click image to activate menu. --- indra/newview/CMakeLists.txt | 4 +- indra/newview/llfloaterprofiletexture.cpp | 141 +--------- indra/newview/llfloaterprofiletexture.h | 43 +-- indra/newview/llpanelprofilelegacy.cpp | 252 +++++++++++++++--- indra/newview/llpanelprofilelegacy.h | 11 + indra/newview/llprofileimagectrl.cpp | 171 ++++++++++++ indra/newview/llprofileimagectrl.h | 73 +++++ .../default/xui/en/menu_profile_image.xml | 38 +++ .../xui/en/panel_profile_legacy_firstlife.xml | 18 +- .../en/panel_profile_legacy_secondlife.xml | 18 +- 10 files changed, 537 insertions(+), 232 deletions(-) create mode 100644 indra/newview/llprofileimagectrl.cpp create mode 100644 indra/newview/llprofileimagectrl.h create mode 100644 indra/newview/skins/default/xui/en/menu_profile_image.xml diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 48a6c314a6..e62fc4355b 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -608,8 +608,9 @@ set(viewer_SOURCE_FILES llpreviewsound.cpp llpreviewtexture.cpp llproductinforequest.cpp - llprogressview.cpp + llprofileimagectrl.cpp llprofileimagepicker.cpp + llprogressview.cpp llrecentpeople.cpp llreflectionmap.cpp llreflectionmapmanager.cpp @@ -1345,6 +1346,7 @@ set(viewer_HEADER_FILES llpreviewsound.h llpreviewtexture.h llproductinforequest.h + llprofileimagectrl.h llprofileimagepicker.h llprogressview.h llrecentpeople.h diff --git a/indra/newview/llfloaterprofiletexture.cpp b/indra/newview/llfloaterprofiletexture.cpp index f2ac806573..c12fb54278 100644 --- a/indra/newview/llfloaterprofiletexture.cpp +++ b/indra/newview/llfloaterprofiletexture.cpp @@ -30,149 +30,10 @@ #include "llbutton.h" #include "llfloaterreg.h" -#include "llpreview.h" // fors constants +#include "llpreview.h" // for constants #include "lltrans.h" #include "llviewercontrol.h" #include "llviewertexture.h" -#include "llviewertexturelist.h" - - - ////////////////////////////////////////////////////////////////////////// - // LLProfileImageCtrl - ////////////////////////////////////////////////////////////////////////// - -static LLDefaultChildRegistry::Register r("profile_image"); - -LLProfileImageCtrl::LLProfileImageCtrl(const LLProfileImageCtrl::Params& p) - : LLIconCtrl(p) - , mImage(NULL) - , mImageOldBoostLevel(LLGLTexture::BOOST_NONE) - , mWasNoDelete(false) - , mImageLoadedSignal(NULL) -{ -} - -LLProfileImageCtrl::~LLProfileImageCtrl() -{ - LLLoadedCallbackEntry::cleanUpCallbackList(&mCallbackTextureList); - releaseTexture(); - - delete mImageLoadedSignal; -} - -void LLProfileImageCtrl::releaseTexture() -{ - if (mImage.notNull()) - { - mImage->setBoostLevel(mImageOldBoostLevel); - if (!mWasNoDelete) - { - // In most cases setBoostLevel marks images as NO_DELETE - mImage->forceActive(); - } - mImage = NULL; - } -} - -void LLProfileImageCtrl::setValue(const LLSD& value) -{ - LLUUID id = value.asUUID(); - setImageAssetId(id); - if (id.isNull()) - { - LLIconCtrl::setValue("Generic_Person_Large", LLGLTexture::BOOST_UI); - } - else - { - // called second to not change priority before it gets saved to mImageOldBoostLevel - LLIconCtrl::setValue(value, LLGLTexture::BOOST_PREVIEW); - } -} - -void LLProfileImageCtrl::draw() -{ - if (mImage.notNull()) - { - // Pump the texture priority - mImage->addTextureStats(MAX_IMAGE_AREA); - mImage->setKnownDrawSize(LLViewerTexture::MAX_IMAGE_SIZE_DEFAULT, LLViewerTexture::MAX_IMAGE_SIZE_DEFAULT); - } - LLIconCtrl::draw(); -} - -boost::signals2::connection LLProfileImageCtrl::setImageLoadedCallback(const image_loaded_signal_t::slot_type& cb) -{ - if (!mImageLoadedSignal) mImageLoadedSignal = new image_loaded_signal_t(); - - return mImageLoadedSignal->connect(cb); -} - -void LLProfileImageCtrl::setImageAssetId(const LLUUID& asset_id) -{ - if (mImageID == asset_id) - { - return; - } - - releaseTexture(); - - mImageID = asset_id; - if (mImageID.notNull()) - { - mImage = LLViewerTextureManager::getFetchedTexture(mImageID, FTT_DEFAULT, MIPMAP_YES, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); - mWasNoDelete = mImage->getTextureState() == LLGLTexture::NO_DELETE; - mImageOldBoostLevel = mImage->getBoostLevel(); - mImage->setBoostLevel(LLGLTexture::BOOST_PREVIEW); - mImage->setKnownDrawSize(LLViewerTexture::MAX_IMAGE_SIZE_DEFAULT, LLViewerTexture::MAX_IMAGE_SIZE_DEFAULT); - mImage->forceToSaveRawImage(0); - - if ((mImage->getFullWidth() * mImage->getFullHeight()) == 0) - { - mImage->setLoadedCallback(LLProfileImageCtrl::onImageLoaded, - 0, TRUE, FALSE, new LLHandle(getHandle()), &mCallbackTextureList); - } - else - { - onImageLoaded(true, mImage); - } - } -} - -void LLProfileImageCtrl::onImageLoaded(bool success, LLViewerFetchedTexture* img) -{ - if (mImageLoadedSignal) - { - (*mImageLoadedSignal)(success, img); - } -} - -// static -void LLProfileImageCtrl::onImageLoaded(BOOL success, - LLViewerFetchedTexture* src_vi, - LLImageRaw* src, - LLImageRaw* aux_src, - S32 discard_level, - BOOL final, - void* userdata) -{ - if (!userdata) return; - - LLHandle* handle = (LLHandle*)userdata; - - if (!handle->isDead()) - { - LLProfileImageCtrl* caller = static_cast(handle->get()); - if (caller && caller->mImageLoadedSignal) - { - (*caller->mImageLoadedSignal)(success, src_vi); - } - } - - if (final || !success) - { - delete handle; - } -} ////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llfloaterprofiletexture.h b/indra/newview/llfloaterprofiletexture.h index 12efbab572..1e1ff8725e 100644 --- a/indra/newview/llfloaterprofiletexture.h +++ b/indra/newview/llfloaterprofiletexture.h @@ -28,50 +28,11 @@ #define LL_LLFLOATERPROFILETEXTURE_H #include "llfloater.h" -#include "lliconctrl.h" -#include "llviewertexture.h" +#include "llprofileimagectrl.h" class LLButton; class LLImageRaw; - -class LLProfileImageCtrl: public LLIconCtrl -{ -public: - struct Params: public LLInitParam::Block - { - }; - - LLProfileImageCtrl(const Params& p); - virtual ~LLProfileImageCtrl(); - - - virtual void setValue(const LLSD& value) override; - LLUUID getImageAssetId() { return mImageID; } - LLPointer getImage() {return mImage;} - void draw() override; - - typedef boost::signals2::signal image_loaded_signal_t; - boost::signals2::connection setImageLoadedCallback(const image_loaded_signal_t::slot_type& cb); -private: - void onImageLoaded(bool success, LLViewerFetchedTexture* src_vi); - static void onImageLoaded(BOOL success, - LLViewerFetchedTexture* src_vi, - LLImageRaw* src, - LLImageRaw* aux_src, - S32 discard_level, - BOOL final, - void* userdata); - void releaseTexture(); - - void setImageAssetId(const LLUUID& asset_id); -private: - LLPointer mImage; - LLUUID mImageID; - S32 mImageOldBoostLevel; - bool mWasNoDelete; - image_loaded_signal_t* mImageLoadedSignal; - LLLoadedCallbackEntry::source_callback_list_t mCallbackTextureList; -}; +class LLViewerFetchedTexture; class LLFloaterProfileTexture : public LLFloater { diff --git a/indra/newview/llpanelprofilelegacy.cpp b/indra/newview/llpanelprofilelegacy.cpp index cdb1128cdf..d82e2ce20b 100644 --- a/indra/newview/llpanelprofilelegacy.cpp +++ b/indra/newview/llpanelprofilelegacy.cpp @@ -52,16 +52,19 @@ #include "llagentdata.h" #include "llagentpicksinfo.h" #include "llavataractions.h" +#include "llavatariconctrl.h" #include "llcallingcard.h" // for LLAvatarTracker #include "llclassifieditem.h" #include "lldateutil.h" #include "lldroptarget.h" +#include "llfloaterprofiletexture.h" #include "llfloaterreporter.h" #include "llfloaterworldmap.h" #include "llgroupactions.h" #include "llpanelclassified.h" #include "llpanelpick.h" #include "llpickitem.h" +#include "llprofileimagepicker.h" #include "llmutelist.h" #include "llsidetraypanelcontainer.h" #include "llslurl.h" @@ -69,7 +72,7 @@ #include "llviewermenu.h" // gMenuHolder static constexpr std::string_view AGENT_PROFILE_CAP("AgentProfile"); -//static constexpr std::string_view UPLOAD_AGENT_PROFILE_CAP("UploadAgentProfileImage"); +static constexpr std::string_view UPLOAD_AGENT_PROFILE_CAP("UploadAgentProfileImage"); // These are order-senstitive so don't fk with 'em! static const std::array sWantCheckboxes{{"wanna_build", "wanna_explore", "wanna_yiff", "wanna_work", "wanna_group", "wanna_buy", "wanna_sell", "wanna_hire"}}; @@ -83,12 +86,15 @@ LLPanelProfileLegacy::LLPanelProfileLegacy() : LLPanelProfileLegacyTab() , mPanelPicks(nullptr) , mPanelGroups(nullptr) +, mPopupMenuHandle() +, mTexturePicker() { mChildStack.setParent(this); //mCommitCallbackRegistrar.add("Profile.CommitInterest", boost::bind(&LLPanelProfileLegacy::onCommitInterest, this)); mCommitCallbackRegistrar.add("Profile.CommitProperties", boost::bind(&LLPanelProfileLegacy::onCommitAvatarProperties, this)); mCommitCallbackRegistrar.add("Profile.CommitRights", boost::bind(&LLPanelProfileLegacy::onCommitRights, this)); mCommitCallbackRegistrar.add("Profile.CommitModifyObjectRights", boost::bind(&LLPanelProfileLegacy::onCommitModifyObjectsRights, this, _1)); + mCommitCallbackRegistrar.add("Profile.UploadAction", boost::bind(&LLPanelProfileLegacy::onCommitImageAction, this, _1, _2)); mCommitCallbackRegistrar.add("Profile.Action", boost::bind(&LLPanelProfileLegacy::onCommitAction, this, _2)); mEnableCallbackRegistrar.add("Profile.Enable", boost::bind(&LLPanelProfileLegacy::isActionEnabled, this, _2)); } @@ -101,6 +107,12 @@ LLPanelProfileLegacy::~LLPanelProfileLegacy() mAvatarNameCacheConnection.disconnect(); if (mNameChangedConnection.connected()) mNameChangedConnection.disconnect(); + auto popup_menu = static_cast(mPopupMenuHandle.get()); + if (popup_menu) + { + popup_menu->die(); + mPopupMenuHandle.markDead(); + } } // virtual @@ -110,6 +122,9 @@ BOOL LLPanelProfileLegacy::postBuild() mPanelPicks = static_cast(getChild("avatar_picks_tab_panel")); mPanelPicks->setProfilePanel(this); + auto popup_menu = LLUICtrlFactory::getInstance()->createFromFile("menu_profile_image.xml", gMenuHolder, child_registry_t::instance()); + if (popup_menu) { mPopupMenuHandle = popup_menu->getHandle(); } + if (dynamic_cast(getParent()) != nullptr) getChild("back")->setCommitCallback(boost::bind(&LLPanelProfileLegacy::onBackBtnClick, this)); else if (dynamic_cast(getParent()) != nullptr) @@ -118,10 +133,12 @@ BOOL LLPanelProfileLegacy::postBuild() getChild("back")->setEnabled(FALSE); getChild("sl_about")->setCommitCallback(boost::bind(&LLPanelProfileLegacy::onCommitAvatarProperties, this)); getChild("fl_about")->setCommitCallback(boost::bind(&LLPanelProfileLegacy::onCommitAvatarProperties, this)); - getChild("sl_profile_pic")->setCommitCallback(boost::bind(&LLPanelProfileLegacy::onCommitAvatarProperties, this)); - getChild("fl_profile_pic")->setCommitCallback(boost::bind(&LLPanelProfileLegacy::onCommitAvatarProperties, this)); getChild("notes")->setCommitCallback(boost::bind(&LLPanelProfileLegacy::onCommitNotes, this, _1)); getChild("avatar_name")->setDoubleClickCallback(boost::bind(&LLPanelProfileLegacy::onDoubleClickName, this)); + //getChild("sl_profile_pic")->setMouseUpCallback(boost::bind(&LLPanelProfileLegacy::onCommitZoomProfileImage, this, _1, _2, _3, _4)); + getChild("sl_profile_pic")->setRightMouseUpCallback(boost::bind(&LLPanelProfileLegacy::onCommitRightClickProfileImage, this, _1, _2, _3, _4)); + //getChild("fl_profile_pic")->setMouseUpCallback(boost::bind(&LLPanelProfileLegacy::onCommitZoomProfileImage, this, _1, _2, _3, _4)); + getChild("fl_profile_pic")->setRightMouseUpCallback(boost::bind(&LLPanelProfileLegacy::onCommitRightClickProfileImage, this, _1, _2, _3, _4)); return TRUE; } @@ -442,8 +459,8 @@ void LLPanelProfileLegacy::processProperties(void* data, EAvatarProcessorType ty { const LLAvatarData* pData = static_cast(data); if (!pData || pData->avatar_id != getAvatarId()) return; - getChild("sl_profile_pic")->setValue(pData->image_id); - getChild("fl_profile_pic")->setValue(pData->fl_image_id); + getChild("sl_profile_pic")->setValue(pData->image_id); + getChild("fl_profile_pic")->setValue(pData->fl_image_id); if (pData->partner_id.notNull()) { getChild("partner_info")->setText(LLSLURL("agent", pData->partner_id, "inspect").getSLURLString()); @@ -558,9 +575,40 @@ LLPanel* LLPanelProfileLegacy::getExpandedTab() const return tab ? tab->getChild(tab->getName() + "_panel") : nullptr; } +void LLPanelProfileLegacy::onCommitImageAction(LLUICtrl* ctrl, const LLSD& userdata) +{ + auto* menu = ctrl->getParentByType(); + if (menu == NULL) { + LL_WARNS("LegacyProfiles") << "Command did not come from context menu: " << ctrl->getName() << LL_ENDL; + return; + } + auto* spawning_view = menu->getSpawningView(); + if (spawning_view == NULL || !spawning_view->isAvailable()) + { + LL_WARNS("LegacyProfiles") << "Profile image is not available" << LL_ENDL; + return; + } + auto* view = static_cast(spawning_view); + if (view == NULL) + { + LL_WARNS("LegacyProfiles") << spawning_view->getName() << " is not a profile image control." << LL_ENDL; + return; + } + + const std::string& action = userdata.asString(); + if (action == "upload_profile_pic") + LLPanelProfileLegacy::onCommitUploadImage(view); + else if (action == "change_profile_pic") + LLPanelProfileLegacy::onCommitChangeImage(view); + else if (action == "remove_profile_pic") + LLPanelProfileLegacy::onCommitRemoveImage(view); + else + LL_WARNS("LegacyProfiles") << "Unhandled action: " << action << LL_ENDL; +} + void LLPanelProfileLegacy::onCommitAction(const LLSD& userdata) { - const std::string action = userdata.asString(); + const std::string& action = userdata.asString(); if (action == "friend") { if (LLAvatarTracker::instance().getBuddyInfo(getAvatarId()) == nullptr) @@ -590,14 +638,6 @@ void LLPanelProfileLegacy::onCommitAction(const LLSD& userdata) LLAvatarActions::pay(getAvatarId()); else if (action == "report_abuse") LLFloaterReporter::showFromObject(getAvatarId()); - else if (action == "upload_sl") - { - // *TODO: - } - else if (action == "upload_fl") - { - // *TODO: - } else if (action == "webprofile") ALAvatarActions::showWebProfile(getAvatarId()); else @@ -608,29 +648,27 @@ bool LLPanelProfileLegacy::isActionEnabled(const LLSD& userdata) { bool action_enabled = false; const std::string check = userdata.asString(); - if (check == "can_has_telefono") + if (check == "can_has_telefono") { action_enabled = (LLAvatarActions::canCall() && getAvatarId() != gAgentID); - else if (check == "can_has_teleport") + } else if (check == "can_has_teleport") { action_enabled = (LLAvatarActions::canOfferTeleport(getAvatarId()) && getAvatarId() != gAgentID); - else if (check == "can_has_map") + } else if (check == "can_has_map") { action_enabled = (LLAvatarTracker::instance().isBuddyOnline(getAvatarId()) && LLAvatarActions::isAgentMappable(getAvatarId())) || gAgent.isGodlike(); } - else if (check == "can_has_pay") + else if (check == "can_has_pay") { action_enabled = (getAvatarId() != gAgentID); - else if (check == "can_share") + } else if (check == "can_share") { action_enabled = (getAvatarId() != gAgentID); - else if (check == "can_drama") + } else if (check == "can_drama") { action_enabled = (getAvatarId() != gAgentID); - else if (check == "can_upload_pic") - { - action_enabled = getAvatarId() == gAgentID - && !gAgent.getRegionCapability("UploadAgentProfileImage").empty(); - } - else + } else if (check == "can_upload_pic") { + action_enabled = getAvatarId() == gAgentID && !gAgent.getRegionCapability(UPLOAD_AGENT_PROFILE_CAP).empty(); + } else { LL_INFOS("LegacyProfiles") << "Unhandled check " << check << LL_ENDL; + } return action_enabled; } @@ -638,20 +676,21 @@ void LLPanelProfileLegacy::onCommitAvatarProperties() { if (getAvatarId() != gAgentID) return; - std::string cap = gAgent.getRegionCapability(AGENT_PROFILE_CAP); - if (!cap.empty()) + if (cap.empty()) { - LLSD data; - data["sl_about_text"] = getChild("sl_about")->getText(); - data["fl_about_text"] = getChild("fl_about")->getText(); - data["profile_url"] = getChild("www_edit")->getText(); - data["allow_publish"] = getChild("allow_publish")->getValue().asBoolean(); - - LLCoros::instance().launch( - "sendAvatarProfileCoro", - boost::bind(&LLPanelProfileLegacy::sendAvatarProfileCoro, this, cap, data)); + LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL; } + + LLSD data; + data["sl_about_text"] = getChild("sl_about")->getText(); + data["fl_about_text"] = getChild("fl_about")->getText(); + data["profile_url"] = getChild("www_edit")->getText(); + data["allow_publish"] = getChild("allow_publish")->getValue().asBoolean(); + + LLCoros::instance().launch( + "sendAvatarProfileCoro", + boost::bind(&LLPanelProfileLegacy::sendAvatarProfileCoro, this, cap, data)); } void LLPanelProfileLegacy::onCommitNotes(LLUICtrl* ctrl) @@ -680,6 +719,99 @@ void LLPanelProfileLegacy::onNameChanged() boost::bind(&LLPanelProfileLegacy::onAvatarNameCache, this, _1, _2)); } +void LLPanelProfileLegacy::onCommitUploadImage(LLProfileImageCtrl* ctrl) +{ + // I really hate this, but I gotta make work with what's there lest my changes get backed out again. lol. -CR + auto const& ctrl_name = ctrl->getName(); + EProfileImageType type; + if (ctrl_name == "sl_profile_pic") + { + type = PROFILE_IMAGE_SL; + } + else if (ctrl_name == "fl_profile_pic") + { + type = PROFILE_IMAGE_FL; + } + else + { + LL_INFOS("LegacyProfiles") << "Invalid control name for updating profile pic: " << ctrl_name << LL_ENDL; + return; + } + (new LLProfileImagePicker(type, new LLHandle(LLPanel::getHandle()), + [this, ctrl](LLUUID const& id) { onProfileImageChanged(id, ctrl); }))->getFile(); + + LLFloater* picker = mTexturePicker.get(); + if (picker) + { + picker->closeFloater(); + } +} + +void LLPanelProfileLegacy::onCommitChangeImage(LLProfileImageCtrl* ctrl) +{ + LLFloater* existing = mTexturePicker.get(); + if (existing) + { + existing->setMinimized(FALSE); + existing->setFocus(TRUE); + } + else + { + LLFloater* parent = gFloaterView->getParentFloater(this); + if (!parent) { return; } + + getWindow()->setCursor(UI_CURSOR_WAIT); + LLFloaterTexturePicker* picker = + new LLFloaterTexturePicker(this, ctrl->getImageAssetId(), LLUUID::null, LLUUID::null, ctrl->getImageAssetId(), FALSE, FALSE, + "SELECT IMAGE", PERM_NONE, PERM_NONE, FALSE, NULL, PICK_TEXTURE); + mTexturePicker = picker->getHandle(); + picker->setOnFloaterCommitCallback( + [this, ctrl, picker](LLTextureCtrl::ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID&, + const LLUUID&) + { + if (op == LLTextureCtrl::TEXTURE_SELECT) + { + onProfileImageChanged(asset_id, ctrl); + } + }); + picker->setLocalTextureEnabled(FALSE); + picker->setCanApply(false, true, false); + + parent->addDependentFloater(picker); + + picker->openFloater(); + picker->setFocus(TRUE); + } +} + +void LLPanelProfileLegacy::onCommitRemoveImage(LLProfileImageCtrl* ctrl) +{ + onProfileImageChanged(LLUUID::null, ctrl); + + LLFloater* picker = mTexturePicker.get(); + if (picker) + { + picker->closeFloater(); + } +} + +void LLPanelProfileLegacy::onProfileImageChanged(const LLUUID& id, LLProfileImageCtrl* ctrl) +{ + if (getAvatarId() != gAgentID) { return; } + + ctrl->setValue(id); + + std::string cap = gAgent.getRegionCapability(AGENT_PROFILE_CAP); + if (cap.empty()) { return; } + + const std::string_view img = ctrl->getName() == "sl_profile_pic" ? "sl_image_id" : "fl_image_id"; + // *TODO: Do we want to verify that the changes were put? + LLCoros::instance().launch("sendAvatarProfileImage", + boost::bind(&LLPanelProfileLegacy::sendAvatarProfileCoro, this, cap, LLSD().with(img, id))); + LLAvatarIconIDCache::getInstance()->add(gAgentID, id); + LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest(gAgentID); +} + void LLPanelProfileLegacy::onBackBtnClick() { LLSideTrayPanelContainer* parent = dynamic_cast(getParent()); @@ -717,6 +849,50 @@ bool LLPanelProfileLegacy::handleConfirmModifyRightsCallback(const LLSD& notific return false; } +void LLPanelProfileLegacy::onCommitRightClickProfileImage(LLUICtrl* ctrl, S32 x, S32 y, MASK mask) +{ + auto menu = static_cast(mPopupMenuHandle.get()); + if (menu) + { + menu->buildDrawLabels(); + menu->show(x, y, ctrl); + LLMenuGL::showPopup(ctrl, menu, x, y); + } +} + +void LLPanelProfileLegacy::onCommitZoomProfileImage(LLUICtrl* item, S32 x, S32 y, MASK mask) +{ + LLFloater* existing = mProfileSnooper.get(); + if (existing) + { + LLFloaterProfileTexture* tex_view = static_cast(existing); + tex_view->setMinimized(FALSE); + tex_view->setVisibleAndFrontmost(TRUE); + + LLUUID const& id = static_cast(item)->getImageAssetId(); + if (id.notNull()) + { + tex_view->loadAsset(id); + } + else + { + tex_view->resetAsset(); + } + } + else + { + LLFloater* parent = gFloaterView->getParentFloater(this); + if (!parent) { return; } + + LLFloaterProfileTexture* picker = new LLFloaterProfileTexture(this); + mProfileSnooper = picker->getHandle(); + parent->addDependentFloater(picker); + picker->loadAsset(static_cast(item)->getImageAssetId()); + picker->openFloater(); + picker->setVisibleAndFrontmost(TRUE); + } +} + void LLPanelProfileLegacy::closeParentFloater() { LLFloater* floater = dynamic_cast(getParent()); @@ -889,7 +1065,7 @@ void LLPanelProfileLegacy::LLPanelProfilePicks::processProperties(void* data, EA mPicksList->clear(); for (const LLAvatarPicks::pick_data_t& pick : avatar_data->picks_list) { - LL_INFOS() << "Porcessing pick " << pick.second << LL_ENDL; + LL_INFOS("LegacyProfiles") << "Processing pick " << pick.second << LL_ENDL; processPick(pick); } showAccordion("tab_picks", mPicksList->size()); diff --git a/indra/newview/llpanelprofilelegacy.h b/indra/newview/llpanelprofilelegacy.h index d81a86af3a..3c54553dd9 100644 --- a/indra/newview/llpanelprofilelegacy.h +++ b/indra/newview/llpanelprofilelegacy.h @@ -43,6 +43,7 @@ class LLPanelPickInfo; class LLPanelClassifiedInfo; class LLPanelClassifiedEdit; class LLPickItem; +class LLProfileImageCtrl; class LLTextBase; class LLToggleableMenu; @@ -77,14 +78,24 @@ class LLPanelProfileLegacy final : public LLPanelProfileLegacyTab void onCommitRights(); void onBackBtnClick(); void onCommitModifyObjectsRights(LLUICtrl* ctrl); + void onCommitImageAction(LLUICtrl* ctrl, const LLSD& userdata); void onCommitAction(const LLSD& userdata); void onNameChanged(); + void onCommitUploadImage(LLProfileImageCtrl* ctrl); + void onCommitChangeImage(LLProfileImageCtrl* ctrl); + void onCommitRemoveImage(LLProfileImageCtrl* ctrl); + void onProfileImageChanged(const LLUUID& id, LLProfileImageCtrl* ctrl); bool isActionEnabled(const LLSD& userdata); bool handleConfirmModifyRightsCallback(const LLSD& notification, const LLSD& response); + void onCommitRightClickProfileImage(LLUICtrl* item, S32 x, S32 y, MASK mask); + void onCommitZoomProfileImage(LLUICtrl* item, S32 x, S32 y, MASK mask); void closeParentFloater(); boost::signals2::connection mAvatarNameCacheConnection; boost::signals2::connection mNameChangedConnection; + LLHandle mPopupMenuHandle; + LLHandle mTexturePicker; + LLHandle mProfileSnooper; class ChildStack { diff --git a/indra/newview/llprofileimagectrl.cpp b/indra/newview/llprofileimagectrl.cpp new file mode 100644 index 0000000000..6ff38a6c05 --- /dev/null +++ b/indra/newview/llprofileimagectrl.cpp @@ -0,0 +1,171 @@ +/** + * @file llprofileimagectrl.cpp + * @brief LLProfileImageCtrl class declaration + * + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2022, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llprofileimagectrl.h" + +#include "llviewertexture.h" +#include "llviewertexturelist.h" + +////////////////////////////////////////////////////////////////////////// +// LLProfileImageCtrl +////////////////////////////////////////////////////////////////////////// + +static LLDefaultChildRegistry::Register r("profile_image"); + +LLProfileImageCtrl::LLProfileImageCtrl(const LLProfileImageCtrl::Params& p) : + LLIconCtrl(p), + mImage(NULL), + mImageOldBoostLevel(LLGLTexture::BOOST_NONE), + mWasNoDelete(false), + mImageLoadedSignal(NULL) +{ +} + +LLProfileImageCtrl::~LLProfileImageCtrl() +{ + LLLoadedCallbackEntry::cleanUpCallbackList(&mCallbackTextureList); + releaseTexture(); + + delete mImageLoadedSignal; +} + +void LLProfileImageCtrl::releaseTexture() +{ + if (mImage.notNull()) + { + mImage->setBoostLevel(mImageOldBoostLevel); + if (!mWasNoDelete) + { + // In most cases setBoostLevel marks images as NO_DELETE + mImage->forceActive(); + } + mImage = NULL; + } +} + +void LLProfileImageCtrl::setValue(const LLSD& value) +{ + LLUUID id = value.asUUID(); + setImageAssetId(id); + if (id.isNull()) + { + LLIconCtrl::setValue("Generic_Person_Large", LLGLTexture::BOOST_UI); + } + else + { + // called second to not change priority before it gets saved to mImageOldBoostLevel + LLIconCtrl::setValue(value, LLGLTexture::BOOST_PREVIEW); + } +} + +void LLProfileImageCtrl::draw() +{ + if (mImage.notNull()) + { + // Pump the texture priority + mImage->addTextureStats(MAX_IMAGE_AREA); + mImage->setKnownDrawSize(LLViewerTexture::MAX_IMAGE_SIZE_DEFAULT, LLViewerTexture::MAX_IMAGE_SIZE_DEFAULT); + } + LLIconCtrl::draw(); +} + +boost::signals2::connection LLProfileImageCtrl::setImageLoadedCallback(const image_loaded_signal_t::slot_type& cb) +{ + if (!mImageLoadedSignal) + mImageLoadedSignal = new image_loaded_signal_t(); + + return mImageLoadedSignal->connect(cb); +} + +void LLProfileImageCtrl::setImageAssetId(const LLUUID& asset_id) +{ + if (mImageID == asset_id) + { + return; + } + + releaseTexture(); + + mImageID = asset_id; + if (mImageID.notNull()) + { + mImage = LLViewerTextureManager::getFetchedTexture(mImageID, FTT_DEFAULT, MIPMAP_YES, LLGLTexture::BOOST_NONE, + LLViewerTexture::LOD_TEXTURE); + mWasNoDelete = mImage->getTextureState() == LLGLTexture::NO_DELETE; + mImageOldBoostLevel = mImage->getBoostLevel(); + mImage->setBoostLevel(LLGLTexture::BOOST_PREVIEW); + mImage->setKnownDrawSize(LLViewerTexture::MAX_IMAGE_SIZE_DEFAULT, LLViewerTexture::MAX_IMAGE_SIZE_DEFAULT); + mImage->forceToSaveRawImage(0); + + if ((mImage->getFullWidth() * mImage->getFullHeight()) == 0) + { + mImage->setLoadedCallback(LLProfileImageCtrl::onImageLoaded, 0, TRUE, FALSE, new LLHandle(getHandle()), + &mCallbackTextureList); + } + else + { + onImageLoaded(true, mImage); + } + } +} + +void LLProfileImageCtrl::onImageLoaded(bool success, LLViewerFetchedTexture* img) +{ + if (mImageLoadedSignal) + { + (*mImageLoadedSignal)(success, img); + } +} + +// static +void LLProfileImageCtrl::onImageLoaded(BOOL success, + LLViewerFetchedTexture* src_vi, + LLImageRaw* src, + LLImageRaw* aux_src, + S32 discard_level, + BOOL final, + void* userdata) +{ + if (!userdata) + return; + + LLHandle* handle = (LLHandle*) userdata; + + if (!handle->isDead()) + { + LLProfileImageCtrl* caller = static_cast(handle->get()); + if (caller && caller->mImageLoadedSignal) + { + (*caller->mImageLoadedSignal)(success, src_vi); + } + } + + if (final || !success) + { + delete handle; + } +} diff --git a/indra/newview/llprofileimagectrl.h b/indra/newview/llprofileimagectrl.h new file mode 100644 index 0000000000..2916d0c0c2 --- /dev/null +++ b/indra/newview/llprofileimagectrl.h @@ -0,0 +1,73 @@ +/** + * @file llprofileimagectrl.h + * @brief LLProfileImageCtrl class definition + * + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2022, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLPROFILEIMAGECTRL_H +#define LL_LLPROFILEIMAGECTRL_H + +#include "lliconctrl.h" +#include "llviewertexture.h" + +class LLProfileImageCtrl : public LLIconCtrl +{ + public: + struct Params : public LLInitParam::Block + { + }; + + LLProfileImageCtrl(const Params& p); + virtual ~LLProfileImageCtrl(); + + virtual void setValue(const LLSD& value) override; + LLUUID getImageAssetId() { return mImageID; } + LLPointer getImage() { return mImage; } + void draw() override; + + typedef boost::signals2::signal image_loaded_signal_t; + boost::signals2::connection setImageLoadedCallback(const image_loaded_signal_t::slot_type& cb); + + private: + void onImageLoaded(bool success, LLViewerFetchedTexture* src_vi); + static void onImageLoaded(BOOL success, + LLViewerFetchedTexture* src_vi, + LLImageRaw* src, + LLImageRaw* aux_src, + S32 discard_level, + BOOL final, + void* userdata); + void releaseTexture(); + + void setImageAssetId(const LLUUID& asset_id); + + private: + LLPointer mImage; + LLUUID mImageID; + S32 mImageOldBoostLevel; + bool mWasNoDelete; + image_loaded_signal_t* mImageLoadedSignal; + LLLoadedCallbackEntry::source_callback_list_t mCallbackTextureList; +}; + +#endif // LL_LLPROFILEIMAGECTRL_H diff --git a/indra/newview/skins/default/xui/en/menu_profile_image.xml b/indra/newview/skins/default/xui/en/menu_profile_image.xml new file mode 100644 index 0000000000..2e554e314e --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_profile_image.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/panel_profile_legacy_firstlife.xml b/indra/newview/skins/default/xui/en/panel_profile_legacy_firstlife.xml index 8e32d3e701..034df1eb7c 100644 --- a/indra/newview/skins/default/xui/en/panel_profile_legacy_firstlife.xml +++ b/indra/newview/skins/default/xui/en/panel_profile_legacy_firstlife.xml @@ -12,19 +12,25 @@ height="129" width="304" layout="topleft"> - + width="120"> + + - + width="120"> + +