diff --git a/indra/newview/fsfloatersearch.cpp b/indra/newview/fsfloatersearch.cpp index b9b08e1e3f..4595e70436 100644 --- a/indra/newview/fsfloatersearch.cpp +++ b/indra/newview/fsfloatersearch.cpp @@ -308,15 +308,6 @@ BOOL FSFloaterSearch::postBuild() mPanelClassifieds = findChild("panel_ls_classifieds"); mPanelWeb = findChild("panel_ls_web"); - // If skin has legacy full profile view, use it - mPanelProfile = mPanelPeople->findChild("panel_profile_view"); - if (mPanelProfile) - { - mPanelProfile->setVisible(false); - mPanelProfile->setEmbedded(true); - mPanelPeople->childSetAction("people_profile_btn", boost::bind(&FSFloaterSearch::onBtnPeopleProfile, this)); - } - mDetailsPanel = getChild("panel_ls_details"); mDetailTitle = getChild("title"); mDetailDesc = getChild("desc"); diff --git a/indra/newview/llfloaterdisplayname.cpp b/indra/newview/llfloaterdisplayname.cpp index e6742727d6..3b0c67415a 100644 --- a/indra/newview/llfloaterdisplayname.cpp +++ b/indra/newview/llfloaterdisplayname.cpp @@ -47,7 +47,6 @@ public: virtual ~LLFloaterDisplayName() { } /*virtual*/ BOOL postBuild(); void onSave(); - void onReset(); void onCancel(); /*virtual*/ void onOpen(const LLSD& key); @@ -102,7 +101,6 @@ void LLFloaterDisplayName::onOpen(const LLSD& key) BOOL LLFloaterDisplayName::postBuild() { - getChild("reset_btn")->setCommitCallback(boost::bind(&LLFloaterDisplayName::onReset, this)); getChild("cancel_btn")->setCommitCallback(boost::bind(&LLFloaterDisplayName::onCancel, this)); getChild("save_btn")->setCommitCallback(boost::bind(&LLFloaterDisplayName::onSave, this)); @@ -158,21 +156,6 @@ void LLFloaterDisplayName::onCancel() setVisible(false); } -void LLFloaterDisplayName::onReset() -{ - if (LLAvatarNameCache::getInstance()->hasNameLookupURL()) - { - LLViewerDisplayName::set("",boost::bind(&LLFloaterDisplayName::onCacheSetName, this, _1, _2, _3)); - } - else - { - LLNotificationsUtil::add("SetDisplayNameFailedGeneric"); - } - - setVisible(false); -} - - void LLFloaterDisplayName::onSave() { std::string display_name_utf8 = getChild("display_name_editor")->getValue().asString(); diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index a92cea23c6..4faa60b12b 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -606,10 +606,13 @@ void LLFloaterPreference::saveAvatarProperties( void ) // "allow publish" flag, the last remaining profile setting in the viewer // that doesn't exist in the web profile. // + if ((LLStartUp::getStartupState() == STATE_STARTED) && mAvatarDataInitialized && (allowPublish != mAvatarProperties.allow_publish)) { mAvatarProperties.allow_publish = allowPublish; + // TODO!!!: replace with an AgentProfile cap, once allow_publish works correctly + // otherwise this will trim long descritions/reset profile LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesUpdate( &mAvatarProperties ); } } diff --git a/indra/newview/llfloaterprofile.cpp b/indra/newview/llfloaterprofile.cpp index f2863e1e27..b259bd28f5 100644 --- a/indra/newview/llfloaterprofile.cpp +++ b/indra/newview/llfloaterprofile.cpp @@ -62,9 +62,6 @@ BOOL LLFloaterProfile::postBuild() { mPanelProfile = findChild(PANEL_PROFILE_VIEW); - childSetAction("ok_btn", boost::bind(&LLFloaterProfile::onOKBtn, this)); - childSetAction("cancel_btn", boost::bind(&LLFloaterProfile::onCancelBtn, this)); - return TRUE; } @@ -83,17 +80,6 @@ void LLFloaterProfile::showClassified(const LLUUID& classified_id, bool edit) mPanelProfile->showClassified(classified_id, edit); } -void LLFloaterProfile::onOKBtn() -{ - mPanelProfile->apply(); - closeFloater(); -} - -void LLFloaterProfile::onCancelBtn() -{ - closeFloater(); -} - void LLFloaterProfile::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name) { mNameCallbackConnection.disconnect(); diff --git a/indra/newview/llfloaterprofile.h b/indra/newview/llfloaterprofile.h index 22ed47e54f..3026f174fb 100644 --- a/indra/newview/llfloaterprofile.h +++ b/indra/newview/llfloaterprofile.h @@ -47,10 +47,6 @@ public: void showClassified(const LLUUID& classified_id = LLUUID::null, bool edit = false); -protected: - void onOKBtn(); - void onCancelBtn(); - private: LLAvatarNameCache::callback_connection_t mNameCallbackConnection; void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); diff --git a/indra/newview/llgrouplist.cpp b/indra/newview/llgrouplist.cpp index dbfe1bf944..3d64a58110 100644 --- a/indra/newview/llgrouplist.cpp +++ b/indra/newview/llgrouplist.cpp @@ -70,7 +70,35 @@ public: } }; +class LLSharedGroupComparator : public LLFlatListView::ItemComparator +{ +public: + LLSharedGroupComparator() {}; + + /*virtual*/ bool compare(const LLPanel* item1, const LLPanel* item2) const + { + const LLGroupListItem* group_item1 = static_cast(item1); + std::string name1 = group_item1->getGroupName(); + bool item1_shared = gAgent.isInGroup(group_item1->getGroupID(), true); + + const LLGroupListItem* group_item2 = static_cast(item2); + std::string name2 = group_item2->getGroupName(); + bool item2_shared = gAgent.isInGroup(group_item2->getGroupID(), true); + + if (item2_shared != item1_shared) + { + return item1_shared; + } + + LLStringUtil::toUpper(name1); + LLStringUtil::toUpper(name2); + + return name1 < name2; + } +}; + static LLGroupComparator GROUP_COMPARATOR; +static LLSharedGroupComparator SHARED_GROUP_COMPARATOR; LLGroupList::Params::Params() : for_agent("for_agent", true) @@ -87,7 +115,15 @@ LLGroupList::LLGroupList(const Params& p) setCommitOnSelectionChange(true); // Set default sort order. - setComparator(&GROUP_COMPARATOR); + if (mForAgent) + { + setComparator(&GROUP_COMPARATOR); + } + else + { + // shared groups first + setComparator(&SHARED_GROUP_COMPARATOR); + } if (mForAgent) { @@ -265,7 +301,7 @@ void LLGroupList::setGroups(const std::map< std::string,LLUUID> group_list) void LLGroupList::addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, EAddPosition pos, bool visible_in_profile) { - LLGroupListItem* item = new LLGroupListItem(mForAgent && mShowIcons); + LLGroupListItem* item = new LLGroupListItem(mForAgent, mShowIcons); item->setGroupID(id); item->setName(name, mNameFilter); @@ -380,14 +416,15 @@ bool LLGroupList::onContextMenuItemEnable(const LLSD& userdata) /* LLGroupListItem implementation */ /************************************************************************/ -LLGroupListItem::LLGroupListItem(bool for_agent) +LLGroupListItem::LLGroupListItem(bool for_agent, bool show_icons) : LLPanel(), mGroupIcon(NULL), mGroupNameBox(NULL), mInfoBtn(NULL), -mGroupID(LLUUID::null) +mGroupID(LLUUID::null), +mForAgent(for_agent) { - if (for_agent) + if (show_icons) { buildFromFile( "panel_group_list_item.xml"); } @@ -465,7 +502,17 @@ void LLGroupListItem::setGroupID(const LLUUID& group_id) mID = group_id; mGroupID = group_id; - setActive(group_id == gAgent.getGroupID()); + + if (mForAgent) + { + // Active group should be bold. + setBold(group_id == gAgent.getGroupID()); + } + else + { + // Groups shared with the agent should be bold + setBold(gAgent.isInGroup(group_id, true)); + } LLGroupMgr::getInstance()->addObserver(this); } @@ -498,17 +545,16 @@ void LLGroupListItem::setVisibleInProfile(bool visible) ////////////////////////////////////////////////////////////////////////// // Private Section ////////////////////////////////////////////////////////////////////////// -void LLGroupListItem::setActive(bool active) +void LLGroupListItem::setBold(bool bold) { // *BUG: setName() overrides the style params. - // Active group should be bold. LLFontDescriptor new_desc(mGroupNameBox->getFont()->getFontDesc()); // *NOTE dzaporozhan // On Windows LLFontGL::NORMAL will not remove LLFontGL::BOLD if font // is predefined as bold (SansSerifSmallBold, for example) - new_desc.setStyle(active ? LLFontGL::BOLD : LLFontGL::NORMAL); + new_desc.setStyle(bold ? LLFontGL::BOLD : LLFontGL::NORMAL); LLFontGL* new_font = LLFontGL::getFont(new_desc); mGroupNameStyle.font = new_font; diff --git a/indra/newview/llgrouplist.h b/indra/newview/llgrouplist.h index 063b50f5c5..a85e20d819 100644 --- a/indra/newview/llgrouplist.h +++ b/indra/newview/llgrouplist.h @@ -101,7 +101,7 @@ class LLGroupListItem : public LLPanel , public LLGroupMgrObserver { public: - LLGroupListItem(bool for_agent); + LLGroupListItem(bool for_agent, bool show_icons); ~LLGroupListItem(); /*virtual*/ BOOL postBuild(); /*virtual*/ void setValue(const LLSD& value); @@ -120,7 +120,7 @@ public: void setVisibleInProfile(bool visible); private: - void setActive(bool active); + void setBold(bool bold); void onInfoBtnClick(); void onProfileBtnClick(); @@ -130,6 +130,7 @@ private: LLButton* mInfoBtn; std::string mGroupName; + bool mForAgent; LLStyle::Params mGroupNameStyle; static S32 sIconWidth; // icon width + padding diff --git a/indra/newview/lllocalbitmaps.cpp b/indra/newview/lllocalbitmaps.cpp index c28e6c7fc8..550b2aa042 100644 --- a/indra/newview/lllocalbitmaps.cpp +++ b/indra/newview/lllocalbitmaps.cpp @@ -931,6 +931,36 @@ LLLocalBitmapMgr::~LLLocalBitmapMgr() mBitmapsAddedSignal.disconnect_all_slots(); // Threaded filepickers } +LLUUID LLLocalBitmapMgr::addUnit(const std::string &filename) +{ + if (!checkTextureDimensions(filename)) + { + return LLUUID::null; + } + + LLLocalBitmap* unit = new LLLocalBitmap(filename); + + if (unit->getValid()) + { + mBitmapList.push_back(unit); + return unit->getTrackingID(); + } + else + { + LL_WARNS() << "Attempted to add invalid or unreadable image file, attempt cancelled.\n" + << "Filename: " << filename << LL_ENDL; + + LLSD notif_args; + notif_args["FNAME"] = filename; + LLNotificationsUtil::add("LocalBitmapsVerifyFail", notif_args); + + delete unit; + unit = NULL; + } + + return LLUUID::null; +} + // Threaded filepickers //bool LLLocalBitmapMgr::addUnit() //{ @@ -989,34 +1019,12 @@ void LLLocalBitmapMgr::filePickerCallback(const std::vector& filena bool add_successful = false; mTimer.stopTimer(); - for (std::vector::const_iterator it = filenames.begin(); it != filenames.end(); ++it) + for (const auto& filename : filenames) { - std::string filename = *it; - - if(!checkTextureDimensions(filename)) + if (addUnit(filename).notNull()) { - continue; - } - - LLLocalBitmap* unit = new LLLocalBitmap(filename); - - if (unit->getValid()) - { - mBitmapList.push_back(unit); add_successful = true; } - else - { - LL_WARNS() << "Attempted to add invalid or unreadable image file, attempt cancelled.\n" - << "Filename: " << filename << LL_ENDL; - - LLSD notif_args; - notif_args["FNAME"] = filename; - LLNotificationsUtil::add("LocalBitmapsVerifyFail", notif_args); - - delete unit; - unit = NULL; - } } mTimer.startTimer(); diff --git a/indra/newview/lllocalbitmaps.h b/indra/newview/lllocalbitmaps.h index e16167f2b7..e3514cd441 100644 --- a/indra/newview/lllocalbitmaps.h +++ b/indra/newview/lllocalbitmaps.h @@ -119,6 +119,7 @@ public: //bool addUnit(); void addUnit(); // + LLUUID addUnit(const std::string &filename); void delUnit(LLUUID tracking_id); bool checkTextureDimensions(std::string filename); diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index dbda6070fa..5d5d4b676c 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -69,32 +69,20 @@ static LLDefaultChildRegistry::Register r("profile_drop_tar LLPanelProfileTab::LLPanelProfileTab() : LLPanel() , mAvatarId(LLUUID::null) -, mLoading(false) -, mLoaded(false) -, mEmbedded(false) +, mLoadingState(PROFILE_INIT) , mSelfProfile(false) { } LLPanelProfileTab::~LLPanelProfileTab() { - if(getAvatarId().notNull()) - { - LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(),this); - } } void LLPanelProfileTab::setAvatarId(const LLUUID& avatar_id) { if (avatar_id.notNull()) { - if (getAvatarId().notNull()) - { - LLAvatarPropertiesProcessor::getInstance()->removeObserver(mAvatarId, this); - } mAvatarId = avatar_id; - LLAvatarPropertiesProcessor::getInstance()->addObserver(getAvatarId(), this); - mSelfProfile = (getAvatarId() == gAgentID); } } @@ -107,11 +95,11 @@ void LLPanelProfileTab::onOpen(const LLSD& key) setApplyProgress(true); } -void LLPanelProfileTab::updateButtons() +void LLPanelProfileTab::setLoaded() { setApplyProgress(false); - mLoaded = true; + mLoadingState = PROFILE_LOADED; } void LLPanelProfileTab::setApplyProgress(bool started) @@ -132,3 +120,29 @@ void LLPanelProfileTab::setApplyProgress(bool started) } } } + +LLPanelProfilePropertiesProcessorTab::LLPanelProfilePropertiesProcessorTab() + : LLPanelProfileTab() +{ +} + +LLPanelProfilePropertiesProcessorTab::~LLPanelProfilePropertiesProcessorTab() +{ + if (getAvatarId().notNull()) + { + LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(), this); + } +} + +void LLPanelProfilePropertiesProcessorTab::setAvatarId(const LLUUID & avatar_id) +{ + if (avatar_id.notNull()) + { + if (getAvatarId().notNull()) + { + LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(), this); + } + LLPanelProfileTab::setAvatarId(avatar_id); + LLAvatarPropertiesProcessor::getInstance()->addObserver(getAvatarId(), this); + } +} diff --git a/indra/newview/llpanelavatar.h b/indra/newview/llpanelavatar.h index f73ea0643d..249b895abb 100644 --- a/indra/newview/llpanelavatar.h +++ b/indra/newview/llpanelavatar.h @@ -81,7 +81,6 @@ protected: */ class LLPanelProfileTab : public LLPanel - , public LLAvatarPropertiesObserver { public: @@ -105,11 +104,6 @@ public: */ virtual void onOpen(const LLSD& key); - /** - * Processes data received from server. - */ - virtual void processProperties(void* data, EAvatarProcessorType type) = 0; - /** * Clears all data received from server. */ @@ -117,37 +111,55 @@ public: /*virtual*/ ~LLPanelProfileTab(); - void setEmbedded(bool embedded) { mEmbedded = embedded; } - protected: LLPanelProfileTab(); + enum ELoadingState + { + PROFILE_INIT, + PROFILE_LOADING, + PROFILE_LOADED, + }; + // mLoading: false: Initial state, can request // true: Data requested, skip duplicate requests (happens due to LLUI's habit of repeated callbacks) // mLoaded: false: Initial state, show loading indicator // true: Data recieved, which comes in a single message, hide indicator - bool getIsLoading() { return mLoading; } - void setIsLoading() { mLoading = true; } - bool getIsLoaded() { return mLoaded; } - void resetLoading() { mLoading = false; mLoaded = false; } + ELoadingState getLoadingState() { return mLoadingState; } + void setIsLoading() { mLoadingState = PROFILE_LOADING; } + virtual void setLoaded(); + void resetLoading() { mLoadingState = PROFILE_INIT; } - const bool getEmbedded() const { return mEmbedded; } + bool getStarted() { return mLoadingState != PROFILE_INIT; } + bool getIsLoaded() { return mLoadingState == PROFILE_LOADED; } const bool getSelfProfile() const { return mSelfProfile; } void setApplyProgress(bool started); - virtual void updateButtons(); - private: LLUUID mAvatarId; - bool mLoading; - bool mLoaded; - bool mEmbedded; + ELoadingState mLoadingState; bool mSelfProfile; }; +class LLPanelProfilePropertiesProcessorTab + : public LLPanelProfileTab + , public LLAvatarPropertiesObserver +{ +public: + LLPanelProfilePropertiesProcessorTab(); + ~LLPanelProfilePropertiesProcessorTab(); + + /*virtual*/ void setAvatarId(const LLUUID& avatar_id); + + /** + * Processes data received from server via LLAvatarPropertiesObserver. + */ + virtual void processProperties(void* data, EAvatarProcessorType type) = 0; +}; + #endif // LL_LLPANELAVATAR_H diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index 606ea97f82..045932a52e 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -37,6 +37,7 @@ #include "llavatariconctrl.h" #include "llclipboard.h" #include "llcheckboxctrl.h" +#include "llcombobox.h" #include "lllineeditor.h" #include "llloadingindicator.h" #include "llmenubutton.h" @@ -46,6 +47,10 @@ #include "lltexturectrl.h" #include "lltoggleablemenu.h" #include "llgrouplist.h" +#include "llurlaction.h" + +// Image +#include "llimagej2c.h" // Newview #include "llagent.h" //gAgent @@ -55,8 +60,10 @@ #include "llcallingcard.h" #include "llcommandhandler.h" #include "llfloaterreg.h" +#include "llfilepicker.h" #include "llfirstuse.h" #include "llgroupactions.h" +#include "lllogchat.h" #include "llmutelist.h" #include "llnotificationsutil.h" #include "llpanelblockedlist.h" @@ -65,6 +72,8 @@ #include "lltrans.h" #include "llviewercontrol.h" #include "llviewermenu.h" //is_agent_mappable +#include "llviewermenufile.h" +#include "llviewertexturelist.h" #include "llvoiceclient.h" #include "llweb.h" @@ -73,7 +82,6 @@ static LLPanelInjector t_panel_profile_secondlife("panel_profile_secondlife"); static LLPanelInjector t_panel_web("panel_profile_web"); -static LLPanelInjector t_panel_interests("panel_profile_interests"); static LLPanelInjector t_panel_picks("panel_profile_picks"); static LLPanelInjector t_panel_firstlife("panel_profile_firstlife"); static LLPanelInjector t_panel_notes("panel_profile_notes"); @@ -81,7 +89,6 @@ static LLPanelInjector t_panel_profile("panel_profile") static const std::string PANEL_SECONDLIFE = "panel_profile_secondlife"; static const std::string PANEL_WEB = "panel_profile_web"; -static const std::string PANEL_INTERESTS = "panel_profile_interests"; static const std::string PANEL_PICKS = "panel_profile_picks"; static const std::string PANEL_CLASSIFIEDS = "panel_profile_classifieds"; static const std::string PANEL_FIRSTLIFE = "panel_profile_firstlife"; @@ -89,6 +96,7 @@ static const std::string PANEL_NOTES = "panel_profile_notes"; static const std::string PANEL_PROFILE_VIEW = "panel_profile_view"; static const std::string PROFILE_PROPERTIES_CAP = "AgentProfile"; +static const std::string PROFILE_IMAGE_UPLOAD_CAP = "UploadAgentProfileImage"; ////////////////////////////////////////////////////////////////////////// @@ -111,6 +119,8 @@ void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id) LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + LL_DEBUGS("AvatarProperties") << "Agent id: " << agent_id << " Result: " << httpResults << LL_ENDL; + if (!status || !result.has("id") || agent_id != result["id"].asUUID()) @@ -145,9 +155,7 @@ void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id) avatar_data->image_id = result["sl_image_id"].asUUID(); avatar_data->fl_image_id = result["fl_image_id"].asUUID(); avatar_data->partner_id = result["partner_id"].asUUID(); - // Todo: new descriptio size is 65536, check if it actually fits or has scroll avatar_data->about_text = result["sl_about_text"].asString(); - // Todo: new descriptio size is 65536, check if it actually fits or has scroll avatar_data->fl_about_text = result["fl_about_text"].asString(); avatar_data->born_on = result["member_since"].asDate(); avatar_data->profile_url = getProfileURL(agent_id.asString()); @@ -184,17 +192,14 @@ void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id) LLPanelProfileWeb *panel_web = dynamic_cast(panel); if (panel_web) { - panel_web->updateButtons(); + panel_web->setLoaded(); } panel = floater_profile->findChild(PANEL_FIRSTLIFE, TRUE); LLPanelProfileFirstLife *panel_first = dynamic_cast(panel); if (panel_first) { - panel_first->mCurrentDescription = avatar_data->fl_about_text; - panel_first->mDescriptionEdit->setValue(panel_first->mCurrentDescription); - panel_first->mPicture->setValue(avatar_data->fl_image_id); - panel_first->updateButtons(); + panel_first->processProperties(avatar_data); } // Picks @@ -266,7 +271,7 @@ void put_avatar_properties_coro(std::string cap_url, LLUUID agent_id, LLSD data) { LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t - httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("request_avatar_properties_coro", httpPolicy)); + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("put_avatar_properties_coro", httpPolicy)); LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); LLCore::HttpHeaders::ptr_t httpHeaders; @@ -282,9 +287,158 @@ void put_avatar_properties_coro(std::string cap_url, LLUUID agent_id, LLSD data) if (!status) { - LL_WARNS("AvatarProperties") << "Failed to put agent information for id " << agent_id << LL_ENDL; + LL_WARNS("AvatarProperties") << "Failed to put agent information " << data << " for id " << agent_id << LL_ENDL; return; } + + LL_DEBUGS("AvatarProperties") << "Agent id: " << agent_id << " Data: " << data << " Result: " << httpResults << LL_ENDL; +} + +LLUUID post_profile_image(std::string cap_url, const LLSD &first_data, std::string path_to_image, LLHandle *handle) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("post_profile_image_coro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpHeaders::ptr_t httpHeaders; + + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + httpOpts->setFollowRedirects(true); + + LLSD result = httpAdapter->postAndSuspend(httpRequest, cap_url, first_data, httpOpts, httpHeaders); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + // todo: notification? + LL_WARNS("AvatarProperties") << "Failed to get uploader cap " << status.toString() << LL_ENDL; + return LLUUID::null; + } + if (!result.has("uploader")) + { + // todo: notification? + LL_WARNS("AvatarProperties") << "Failed to get uploader cap, response contains no data." << LL_ENDL; + return LLUUID::null; + } + std::string uploader_cap = result["uploader"].asString(); + if (uploader_cap.empty()) + { + LL_WARNS("AvatarProperties") << "Failed to get uploader cap, cap invalid." << LL_ENDL; + return LLUUID::null; + } + + // Upload the image + + LLCore::HttpRequest::ptr_t uploaderhttpRequest(new LLCore::HttpRequest); + LLCore::HttpHeaders::ptr_t uploaderhttpHeaders(new LLCore::HttpHeaders); + LLCore::HttpOptions::ptr_t uploaderhttpOpts(new LLCore::HttpOptions); + S64 length; + + { + llifstream instream(path_to_image.c_str(), std::iostream::binary | std::iostream::ate); + if (!instream.is_open()) + { + LL_WARNS("AvatarProperties") << "Failed to open file " << path_to_image << LL_ENDL; + return LLUUID::null; + } + length = instream.tellg(); + } + + uploaderhttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, "application/jp2"); // optional + uploaderhttpHeaders->append(HTTP_OUT_HEADER_CONTENT_LENGTH, llformat("%d", length)); // required! + uploaderhttpOpts->setFollowRedirects(true); + + result = httpAdapter->postFileAndSuspend(uploaderhttpRequest, uploader_cap, path_to_image, uploaderhttpOpts, uploaderhttpHeaders); + + httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + LL_WARNS("AvatarProperties") << result << LL_ENDL; + + if (!status) + { + LL_WARNS("AvatarProperties") << "Failed to upload image " << status.toString() << LL_ENDL; + return LLUUID::null; + } + + if (result["state"].asString() != "complete") + { + if (result.has("message")) + { + LL_WARNS("AvatarProperties") << "Failed to upload image, state " << result["state"] << " message: " << result["message"] << LL_ENDL; + } + else + { + LL_WARNS("AvatarProperties") << "Failed to upload image " << result << LL_ENDL; + } + return LLUUID::null; + } + + return result["new_asset"].asUUID(); +} + +enum EProfileImageType +{ + PROFILE_IMAGE_SL, + PROFILE_IMAGE_FL, +}; + +void post_profile_image_coro(std::string cap_url, EProfileImageType type, std::string path_to_image, LLHandle *handle) +{ + LLSD data; + switch (type) + { + case PROFILE_IMAGE_SL: + data["profile-image-asset"] = "sl_image_id"; + break; + case PROFILE_IMAGE_FL: + data["profile-image-asset"] = "fl_image_id"; + break; + } + + LLUUID result = post_profile_image(cap_url, data, path_to_image, handle); + + // reset loading indicator + if (!handle->isDead()) + { + switch (type) + { + case PROFILE_IMAGE_SL: + { + LLPanelProfileSecondLife* panel = static_cast(handle->get()); + if (result.notNull()) + { + panel->setProfileImageUploaded(result); + } + else + { + // failure, just stop progress indicator + panel->setProfileImageUploading(false); + } + break; + } + case PROFILE_IMAGE_FL: + { + LLPanelProfileFirstLife* panel = static_cast(handle->get()); + if (result.notNull()) + { + panel->setProfileImageUploaded(result); + } + else + { + // failure, just stop progress indicator + panel->setProfileImageUploading(false); + } + break; + } + } + } + + // Cleanup + LLFile::remove(path_to_image); + delete handle; } ////////////////////////////////////////////////////////////////////////// @@ -442,15 +596,219 @@ public: FSAgentSelfHandler gAgentSelfHandler; // +///---------------------------------------------------------------------------- +/// LLFloaterInventoryFinder +///---------------------------------------------------------------------------- + +class LLFloaterProfilePermissions + : public LLFloater + , public LLFriendObserver +{ +public: + LLFloaterProfilePermissions(LLView * owner, const LLUUID &avatar_id); + ~LLFloaterProfilePermissions(); + BOOL postBuild() override; + void onOpen(const LLSD& key) override; + void draw() override; + void changed(U32 mask) override; // LLFriendObserver + + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); + +private: + void fillRightsData(); + void rightsConfirmationCallback(const LLSD& notification, const LLSD& response); + void confirmModifyRights(bool grant); + void onCommitRights(); + + void onApplyRights(); + void onCancel(); + + LLTextBase* mDescription; + LLCheckBoxCtrl* mOnlineStatus; + LLCheckBoxCtrl* mMapRights; + LLCheckBoxCtrl* mEditObjectRights; + LLButton* mOkBtn; + LLButton* mCancelBtn; + + LLUUID mAvatarID; + F32 mContextConeOpacity; + LLHandle mOwnerHandle; + + boost::signals2::connection mAvatarNameCacheConnection; +}; + +LLFloaterProfilePermissions::LLFloaterProfilePermissions(LLView * owner, const LLUUID &avatar_id) + : LLFloater(LLSD()) + , mAvatarID(avatar_id) + , mContextConeOpacity(0.0f) + , mOwnerHandle(owner->getHandle()) +{ + buildFromFile("floater_profile_permissions.xml"); +} + +LLFloaterProfilePermissions::~LLFloaterProfilePermissions() +{ + mAvatarNameCacheConnection.disconnect(); + if (mAvatarID.notNull()) + { + LLAvatarTracker::instance().removeParticularFriendObserver(mAvatarID, this); + } +} + +BOOL LLFloaterProfilePermissions::postBuild() +{ + mDescription = getChild("perm_description"); + mOnlineStatus = getChild("online_check"); + mMapRights = getChild("map_check"); + mEditObjectRights = getChild("objects_check"); + mOkBtn = getChild("perms_btn_ok"); + mCancelBtn = getChild("perms_btn_cancel"); + + mEditObjectRights->setCommitCallback([this](LLUICtrl*, void*) { onCommitRights(); }, nullptr); + mOkBtn->setCommitCallback([this](LLUICtrl*, void*) { onApplyRights(); }, nullptr); + mCancelBtn->setCommitCallback([this](LLUICtrl*, void*) { onCancel(); }, nullptr); + + return TRUE; +} + +void LLFloaterProfilePermissions::onOpen(const LLSD& key) +{ + if (LLAvatarActions::isFriend(mAvatarID)) + { + LLAvatarTracker::instance().addParticularFriendObserver(mAvatarID, this); + fillRightsData(); + } + + mAvatarNameCacheConnection = LLAvatarNameCache::get(mAvatarID, boost::bind(&LLFloaterProfilePermissions::onAvatarNameCache, this, _1, _2)); +} + +void LLFloaterProfilePermissions::draw() +{ + // drawFrustum + LLView *owner = mOwnerHandle.get(); + static LLCachedControl max_opacity(gSavedSettings, "PickerContextOpacity", 0.4f); + drawConeToOwner(mContextConeOpacity, max_opacity, owner); + LLFloater::draw(); +} + +void LLFloaterProfilePermissions::changed(U32 mask) +{ + if (mask != LLFriendObserver::ONLINE) + { + fillRightsData(); + } +} + +void LLFloaterProfilePermissions::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name) +{ + mAvatarNameCacheConnection.disconnect(); + + LLStringUtil::format_map_t args; + args["[AGENT_NAME]"] = av_name.getDisplayName(); + std::string descritpion = getString("description_string", args); + mDescription->setValue(descritpion); +} + +void LLFloaterProfilePermissions::fillRightsData() +{ + const LLRelationship* relation = LLAvatarTracker::instance().getBuddyInfo(mAvatarID); + // If true - we are viewing friend's profile, enable check boxes and set values. + if (relation) + { + S32 rights = relation->getRightsGrantedTo(); + + mOnlineStatus->setValue(LLRelationship::GRANT_ONLINE_STATUS & rights ? TRUE : FALSE); + mMapRights->setValue(LLRelationship::GRANT_MAP_LOCATION & rights ? TRUE : FALSE); + mEditObjectRights->setValue(LLRelationship::GRANT_MODIFY_OBJECTS & rights ? TRUE : FALSE); + } + else + { + closeFloater(); + LL_INFOS("ProfilePermissions") << "Floater closing since agent is no longer a friend" << LL_ENDL; + } +} + +void LLFloaterProfilePermissions::rightsConfirmationCallback(const LLSD& notification, + const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option != 0) + { + mEditObjectRights->setValue(mEditObjectRights->getValue().asBoolean() ? FALSE : TRUE); + } +} + +void LLFloaterProfilePermissions::confirmModifyRights(bool grant) +{ + LLSD args; + args["NAME"] = LLSLURL("agent", mAvatarID, "completename").getSLURLString(); + LLNotificationsUtil::add(grant ? "GrantModifyRights" : "RevokeModifyRights", args, LLSD(), + boost::bind(&LLFloaterProfilePermissions::rightsConfirmationCallback, this, _1, _2)); +} + +void LLFloaterProfilePermissions::onCommitRights() +{ + const LLRelationship* buddy_relationship = LLAvatarTracker::instance().getBuddyInfo(mAvatarID); + + if (!buddy_relationship) + { + LL_WARNS("ProfilePermissions") << "Trying to modify rights for non-friend avatar. Skipped." << LL_ENDL; + return; + } + + bool allow_modify_objects = mEditObjectRights->getValue().asBoolean(); + + // if modify objects checkbox clicked + if (buddy_relationship->isRightGrantedTo( + LLRelationship::GRANT_MODIFY_OBJECTS) != allow_modify_objects) + { + confirmModifyRights(allow_modify_objects); + } +} + +void LLFloaterProfilePermissions::onApplyRights() +{ + const LLRelationship* buddy_relationship = LLAvatarTracker::instance().getBuddyInfo(mAvatarID); + + if (!buddy_relationship) + { + LL_WARNS("ProfilePermissions") << "Trying to modify rights for non-friend avatar. Skipped." << LL_ENDL; + return; + } + + S32 rights = 0; + + if (mOnlineStatus->getValue().asBoolean()) + { + rights |= LLRelationship::GRANT_ONLINE_STATUS; + } + if (mMapRights->getValue().asBoolean()) + { + rights |= LLRelationship::GRANT_MAP_LOCATION; + } + if (mEditObjectRights->getValue().asBoolean()) + { + rights |= LLRelationship::GRANT_MODIFY_OBJECTS; + } + + LLAvatarPropertiesProcessor::getInstance()->sendFriendRights(mAvatarID, rights); + + closeFloater(); +} + +void LLFloaterProfilePermissions::onCancel() +{ + closeFloater(); +} ////////////////////////////////////////////////////////////////////////// // LLPanelProfileSecondLife LLPanelProfileSecondLife::LLPanelProfileSecondLife() - : LLPanelProfileTab() - , mStatusText(NULL) - , mAvatarNameCacheConnection() - , mRlvBehaviorCallbackConnection() // RLVa support + : LLPanelProfileTab() + , mAvatarNameCacheConnection() + , mWaitingForImageUpload(false) + , mAllowPublish(false) { } @@ -470,84 +828,31 @@ LLPanelProfileSecondLife::~LLPanelProfileSecondLife() { mAvatarNameCacheConnection.disconnect(); } - - // RLVa support - if (mRlvBehaviorCallbackConnection.connected()) - { - mRlvBehaviorCallbackConnection.disconnect(); - } - // } BOOL LLPanelProfileSecondLife::postBuild() { - mStatusText = getChild("status"); mGroupList = getChild("group_list"); - mShowInSearchCheckbox = getChild("show_in_search_checkbox"); - mSecondLifePic = getChild("2nd_life_pic"); + mShowInSearchCombo = getChild("show_in_search"); + mSecondLifePic = getChild("2nd_life_pic"); mSecondLifePicLayout = getChild("image_stack"); - mDescriptionEdit = getChild("sl_description_edit"); - mTeleportButton = getChild("teleport"); - mShowOnMapButton = getChild("show_on_map_btn"); - mBlockButton = getChild("block"); - mUnblockButton = getChild("unblock"); - mNameLabel = getChild("name_label"); - mDisplayNameButton = getChild("set_name"); - mAddFriendButton = getChild("add_friend"); - mGroupInviteButton = getChild("group_invite"); - mPayButton = getChild("pay"); - mIMButton = getChild("im"); - mCopyMenuButton = getChild("copy_btn"); - mGiveInvPanel = getChild("give_stack"); - mOverflowButton = getChild("overflow_btn"); // Gear button + mDescriptionEdit = getChild("sl_description_edit"); + mAgentActionMenuButton = getChild("agent_actions_menu"); + mSaveDescriptionChanges = getChild("save_description_changes"); + mDiscardDescriptionChanges = getChild("discard_description_changes"); + mSeeOnlineToggle = getChild("allow_to_see_online"); + mSeeOnMapToggle = getChild("allow_to_see_on_map"); + mEditObjectsToggle = getChild("allow_edit_my_objects"); - mStatusText->setVisible(FALSE); - mCopyMenuButton->setVisible(FALSE); - - mAddFriendButton->setCommitCallback(boost::bind(&LLPanelProfileSecondLife::onAddFriendButtonClick, this)); - mIMButton->setCommitCallback(boost::bind(&LLPanelProfileSecondLife::onIMButtonClick, this)); - mTeleportButton->setCommitCallback(boost::bind(&LLPanelProfileSecondLife::onTeleportButtonClick, this)); - mShowOnMapButton->setCommitCallback(boost::bind(&LLPanelProfileSecondLife::onMapButtonClick, this)); - mPayButton->setCommitCallback(boost::bind(&LLPanelProfileSecondLife::pay, this)); - mBlockButton->setCommitCallback(boost::bind(&LLPanelProfileSecondLife::onClickToggleBlock, this)); - mUnblockButton->setCommitCallback(boost::bind(&LLPanelProfileSecondLife::onClickToggleBlock, this)); - mGroupInviteButton->setCommitCallback(boost::bind(&LLPanelProfileSecondLife::onGroupInvite,this)); - mDisplayNameButton->setCommitCallback(boost::bind(&LLPanelProfileSecondLife::onClickSetName, this)); - mSecondLifePic->setCommitCallback(boost::bind(&LLPanelProfileSecondLife::onCommitTexture, this)); - - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar commit; - commit.add("Profile.CopyName", [this](LLUICtrl*, const LLSD& userdata) { onCommitMenu(userdata); }); - - LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable; - enable.add("Profile.EnableCall", [this](LLUICtrl*, const LLSD&) { return mVoiceStatus; }); - enable.add("Profile.EnableGod", [](LLUICtrl*, const LLSD&) { return gAgent.isGodlike(); }); - - mGroupList->setDoubleClickCallback(boost::bind(&LLPanelProfileSecondLife::openGroupProfile, this)); - mGroupList->setReturnCallback(boost::bind(&LLPanelProfileSecondLife::openGroupProfile, this)); - - // Gear button - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - registrar.add("Profile.Call", [this](LLUICtrl*, const LLSD&) { LLAvatarActions::startCall(getAvatarId()); }); - registrar.add("Profile.AddToContactSet", [this](LLUICtrl*, const LLSD&) { LLAvatarActions::addToContactSet(getAvatarId()); }); - registrar.add("Profile.Share", [this](LLUICtrl*, const LLSD&) { LLAvatarActions::share(getAvatarId()); }); - registrar.add("Profile.Kick", [this](LLUICtrl*, const LLSD&) { LLAvatarActions::kick(getAvatarId()); }); - registrar.add("Profile.Freeze", [this](LLUICtrl*, const LLSD&) { LLAvatarActions::freeze(getAvatarId()); }); - registrar.add("Profile.Unfreeze", [this](LLUICtrl*, const LLSD&) { LLAvatarActions::unfreeze(getAvatarId()); }); - registrar.add("Profile.CSR", [this](LLUICtrl*, const LLSD&) { LLAvatarName av_name; LLAvatarNameCache::get(getAvatarId(), &av_name); std::string name = av_name.getUserName(); LLAvatarActions::csr(getAvatarId(), name); }); - registrar.add("Profile.CopyNameToClipboard", [this](LLUICtrl*, const LLSD&) { onCommitMenu("complete_name"); }); - registrar.add("Profile.CopyURI", [this](LLUICtrl*, const LLSD&) { onCommitMenu("uri"); }); - registrar.add("Profile.CopyKey", [this](LLUICtrl*, const LLSD&) { onCommitMenu("id"); }); - registrar.add("Profile.Report", [this](LLUICtrl*, const LLSD&) { LLAvatarActions::report(getAvatarId()); }); - - LLToggleableMenu* profile_menu = LLUICtrlFactory::getInstance()->createFromFile("menu_profile_overflow.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - mOverflowButton->setMenu(profile_menu, LLMenuButton::MP_TOP_RIGHT); - // - - LLVoiceClient::getInstance()->addObserver((LLVoiceClientStatusObserver*)this); - mCopyMenuButton->setMenu("menu_name_field.xml", LLMenuButton::MP_BOTTOM_RIGHT); - - // RLVa support - mRlvBehaviorCallbackConnection = gRlvHandler.setBehaviourCallback(boost::bind(&LLPanelProfileSecondLife::updateRlvRestrictions, this, _1)); + mShowInSearchCombo->setCommitCallback([this](LLUICtrl*, void*) { onShowInSearchCallback(); }, nullptr); + mGroupList->setDoubleClickCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { LLPanelProfileSecondLife::openGroupProfile(); }); + mGroupList->setReturnCallback([this](LLUICtrl*, const LLSD&) { LLPanelProfileSecondLife::openGroupProfile(); }); + mSaveDescriptionChanges->setCommitCallback([this](LLUICtrl*, void*) { onSaveDescriptionChanges(); }, nullptr); + mDiscardDescriptionChanges->setCommitCallback([this](LLUICtrl*, void*) { onDiscardDescriptionChanges(); }, nullptr); + mDescriptionEdit->setKeystrokeCallback([this](LLTextEditor* caller) { onSetDescriptionDirty(); }); + mSeeOnlineToggle->setMouseUpCallback([this](LLUICtrl*, const LLSD&) { onShowAgentPermissionsDialog(); }); + mSeeOnMapToggle->setMouseUpCallback([this](LLUICtrl*, const LLSD&) { onShowAgentPermissionsDialog(); }); + mEditObjectsToggle->setMouseUpCallback([this](LLUICtrl*, const LLSD&) { onShowAgentPermissionsDialog(); }); return TRUE; } @@ -559,101 +864,59 @@ void LLPanelProfileSecondLife::onOpen(const LLSD& key) resetData(); LLUUID avatar_id = getAvatarId(); - LLAvatarPropertiesProcessor::getInstance()->addObserver(avatar_id, this); BOOL own_profile = getSelfProfile(); - mGroupInviteButton->setVisible(!own_profile); - mShowOnMapButton->setVisible(!own_profile); - mPayButton->setVisible(!own_profile); - mTeleportButton->setVisible(!own_profile); - mIMButton->setVisible(!own_profile); - mAddFriendButton->setVisible(!own_profile); - mBlockButton->setVisible(!own_profile); - mUnblockButton->setVisible(!own_profile); mGroupList->setShowNone(!own_profile); - mGiveInvPanel->setVisible(!own_profile); - mOverflowButton->setVisible(!own_profile); // Gear button - mSecondLifePic->setOpenTexPreview(!own_profile); + childSetVisible("notes_panel", !own_profile); + childSetVisible("settings_panel", own_profile); + childSetVisible("about_buttons_panel", own_profile); - if (own_profile && !getEmbedded()) + if (own_profile) { // Group list control cannot toggle ForAgent loading // Less than ideal, but viewing own profile via search is edge case mGroupList->enableForAgent(false); } - // Show display name button only if display names are enabled - //if (own_profile && !getEmbedded() ) - if (own_profile && LLAvatarName::useDisplayNames() && !getEmbedded()) - // + // Init menu, menu needs to be created in scope of a registar to work correctly. + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar commit; + commit.add("Profile.Commit", [this](LLUICtrl*, const LLSD& userdata) { onCommitMenu(userdata); }); + + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable; + enable.add("Profile.EnableItem", [this](LLUICtrl*, const LLSD& userdata) { return onEnableMenu(userdata); }); + enable.add("Profile.CheckItem", [this](LLUICtrl*, const LLSD& userdata) { return onCheckMenu(userdata); }); + + if (own_profile) { - mNameLabel->setVisible(FALSE); - mDisplayNameButton->setVisible(TRUE); - mDisplayNameButton->setEnabled(TRUE); + mAgentActionMenuButton->setMenu("menu_profile_self.xml", LLMenuButton::MP_BOTTOM_RIGHT); + } + else + { + // Todo: use PeopleContextMenu instead? + mAgentActionMenuButton->setMenu("menu_profile_other.xml", LLMenuButton::MP_BOTTOM_RIGHT); } - mDescriptionEdit->setParseHTML(!own_profile && !getEmbedded()); - - LLProfileDropTarget* drop_target = getChild("drop_target"); - drop_target->setVisible(!own_profile); - drop_target->setEnabled(!own_profile); + mDescriptionEdit->setParseHTML(!own_profile); if (!own_profile) { mVoiceStatus = LLAvatarActions::canCall() && (LLAvatarActions::isFriend(avatar_id) ? LLAvatarTracker::instance().isBuddyOnline(avatar_id) : TRUE); - drop_target->setAgentID(avatar_id); updateOnlineStatus(); + fillRightsData(); } - updateButtons(); - // Display agent ID getChild("user_key")->setValue(avatar_id.asString()); mAvatarNameCacheConnection = LLAvatarNameCache::get(getAvatarId(), boost::bind(&LLPanelProfileSecondLife::onAvatarNameCache, this, _1, _2)); } -void LLPanelProfileSecondLife::apply(LLAvatarData* data) -{ - if (getIsLoaded() && getSelfProfile()) - { - // Might be a better idea to accumulate changes in floater - // instead of sending a request per tab - std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); - if (!cap_url.empty()) - { - LLSD params = LLSDMap(); - if (data->image_id != mSecondLifePic->getImageAssetID()) - { - params["sl_image_id"] = mSecondLifePic->getImageAssetID(); - } - if (data->about_text != mDescriptionEdit->getValue().asString()) - { - params["sl_about_text"] = mDescriptionEdit->getValue().asString(); - } - if ((bool)data->allow_publish != mShowInSearchCheckbox->getValue().asBoolean()) - { - params["allow_publish"] = mShowInSearchCheckbox->getValue().asBoolean(); - } - if (!params.emptyMap()) - { - LLCoros::instance().launch("putAgentUserInfoCoro", - boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), params)); - } - } - else - { - LL_WARNS() << "Failed to update profile data, no cap found" << LL_ENDL; - } - } -} - void LLPanelProfileSecondLife::updateData() { LLUUID avatar_id = getAvatarId(); - if (!getIsLoading() && avatar_id.notNull() && !(getSelfProfile() && !getEmbedded())) + if (!getStarted() && avatar_id.notNull()) { setIsLoading(); @@ -670,37 +933,23 @@ void LLPanelProfileSecondLife::updateData() } } -void LLPanelProfileSecondLife::processProperties(void* data, EAvatarProcessorType type) -{ - - if (APT_PROPERTIES == type) - { - const LLAvatarData* avatar_data = static_cast(data); - if(avatar_data && getAvatarId() == avatar_data->avatar_id) - { - processProfileProperties(avatar_data); - } - } -} - void LLPanelProfileSecondLife::resetData() { resetLoading(); - getChild("complete_name")->setValue(LLStringUtil::null); - getChild("register_date")->setValue(LLStringUtil::null); - getChild("acc_status_text")->setValue(LLStringUtil::null); - getChild("partner_text")->setValue(LLStringUtil::null); // Set default image and 1:1 dimensions for it - mSecondLifePic->setValue(mSecondLifePic->getDefaultImageAssetID()); + mSecondLifePic->setValue("Generic_Person_Large"); LLRect imageRect = mSecondLifePicLayout->getRect(); mSecondLifePicLayout->reshape(imageRect.getHeight(), imageRect.getHeight()); - mDescriptionEdit->setValue(LLStringUtil::null); - mStatusText->setVisible(FALSE); - mCopyMenuButton->setVisible(FALSE); + setDescriptionText(LLStringUtil::null); mGroups.clear(); mGroupList->setGroups(mGroups); + + mSeeOnlineToggle->setToggleState(false); + mSeeOnMapToggle->setToggleState(false); + mEditObjectsToggle->setToggleState(false); + childSetVisible("permissions_panel", false); } void LLPanelProfileSecondLife::processProfileProperties(const LLAvatarData* avatar_data) @@ -708,9 +957,6 @@ void LLPanelProfileSecondLife::processProfileProperties(const LLAvatarData* avat LLUUID avatar_id = getAvatarId(); if (!LLAvatarActions::isFriend(avatar_id) && !getSelfProfile()) { - // this is non-friend avatar. Status will be updated from LLAvatarPropertiesProcessor. - // in LLPanelProfileSecondLife::processOnlineStatus() - // subscribe observer to get online status. Request will be sent by LLPanelProfileSecondLife itself. // do not subscribe for friend avatar because online status can be wrong overridden // via LLAvatarData::flags if Preferences: "Only Friends & Groups can see when I am online" is set. @@ -723,20 +969,11 @@ void LLPanelProfileSecondLife::processProfileProperties(const LLAvatarData* avat fillAccountStatus(avatar_data); - updateButtons(); + setLoaded(); } void LLPanelProfileSecondLife::processGroupProperties(const LLAvatarGroups* avatar_groups) { - //KC: the group_list ctrl can handle all this for us on our own profile - if (getSelfProfile() && !getEmbedded()) - { - return; - } - - // *NOTE dzaporozhan - // Group properties may arrive in two callbacks, we need to save them across - // different calls. We can't do that in textbox as textbox may change the text. LLAvatarGroups::group_list_t::const_iterator it = avatar_groups->group_list.begin(); const LLAvatarGroups::group_list_t::const_iterator it_end = avatar_groups->group_list.end(); @@ -759,9 +996,47 @@ void LLPanelProfileSecondLife::openGroupProfile() void LLPanelProfileSecondLife::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name) { mAvatarNameCacheConnection.disconnect(); + // Should be possible to get this from AgentProfile capability + getChild("display_name")->setValue( av_name.getDisplayName() ); + getChild("user_name")->setValue(av_name.getAccountName()); +} - getChild("complete_name")->setValue( av_name.getCompleteName() ); - mCopyMenuButton->setVisible(TRUE); +void LLPanelProfileSecondLife::setProfileImageUploading(bool loading) +{ + LLLoadingIndicator* indicator = getChild("image_upload_indicator"); + indicator->setVisible(loading); + if (loading) + { + indicator->start(); + } + else + { + indicator->stop(); + } + mWaitingForImageUpload = loading; +} + +void LLPanelProfileSecondLife::setProfileImageUploaded(const LLUUID &image_asset_id) +{ + mSecondLifePic->setValue(image_asset_id); + + LLViewerFetchedTexture* imagep = LLViewerTextureManager::getFetchedTexture(image_asset_id); + if (imagep->getFullHeight()) + { + onImageLoaded(true, imagep); + } + else + { + imagep->setLoadedCallback(onImageLoaded, + MAX_DISCARD_LEVEL, + FALSE, + FALSE, + new LLHandle(getHandle()), + NULL, + FALSE); + } + + setProfileImageUploading(false); } void LLPanelProfileSecondLife::fillCommonData(const LLAvatarData* avatar_data) @@ -771,22 +1046,26 @@ void LLPanelProfileSecondLife::fillCommonData(const LLAvatarData* avatar_data) LLAvatarIconIDCache::getInstance()->add(avatar_data->avatar_id, avatar_data->image_id); LLStringUtil::format_map_t args; + // Re-add register date + std::string birth_date = LLTrans::getString("AvatarBirthDateFormat"); + LLStringUtil::format(birth_date, LLSD().with("datetime", (S32)avatar_data->born_on.secondsSinceEpoch())); + args["[REG_DATE]"] = birth_date; + // + args["[AGE]"] = LLDateUtil::ageFromDate( avatar_data->born_on, LLDate::now()); + std::string register_date = getString("AgeFormat", args); + getChild("user_age")->setValue(register_date); + setDescriptionText(avatar_data->about_text); + + if (avatar_data->image_id.notNull()) { - std::string birth_date = LLTrans::getString("AvatarBirthDateFormat"); - LLStringUtil::format(birth_date, LLSD().with("datetime", (S32) avatar_data->born_on.secondsSinceEpoch())); - args["[REG_DATE]"] = birth_date; + mSecondLifePic->setValue(avatar_data->image_id); + } + else + { + mSecondLifePic->setValue("Generic_Person_Large"); } - args["[AGE]"] = LLDateUtil::ageFromDate( avatar_data->born_on, LLDate::now()); - // Avatar age in days - args["[AGEDAYS]"] = LLSD((S32) (LLDate::now().secondsSinceEpoch() - avatar_data->born_on.secondsSinceEpoch()) / 86400).asString(); - // - std::string register_date = getString("RegisterDateFormat", args); - getChild("register_date")->setValue(register_date ); - mDescriptionEdit->setValue(avatar_data->about_text); - mSecondLifePic->setValue(avatar_data->image_id); - - //Don't bother about boost level, picker will set it + // Will be loaded as a LLViewerFetchedTexture::BOOST_UI due to mSecondLifePic LLViewerFetchedTexture* imagep = LLViewerTextureManager::getFetchedTexture(avatar_data->image_id); if (imagep->getFullHeight()) { @@ -805,20 +1084,24 @@ void LLPanelProfileSecondLife::fillCommonData(const LLAvatarData* avatar_data) if (getSelfProfile()) { - mShowInSearchCheckbox->setValue((BOOL)(avatar_data->flags & AVATAR_ALLOW_PUBLISH)); + mAllowPublish = avatar_data->flags & AVATAR_ALLOW_PUBLISH; + mShowInSearchCombo->setValue((BOOL)mAllowPublish); } } void LLPanelProfileSecondLife::fillPartnerData(const LLAvatarData* avatar_data) { - LLTextEditor* partner_text = getChild("partner_text"); + LLTextBox* partner_text_ctrl = getChild("partner_link"); if (avatar_data->partner_id.notNull()) { - partner_text->setText(LLSLURL("agent", avatar_data->partner_id, "inspect").getSLURLString()); + LLStringUtil::format_map_t args; + args["[LINK]"] = LLSLURL("agent", avatar_data->partner_id, "inspect").getSLURLString(); + std::string partner_text = getString("partner_text", args); + partner_text_ctrl->setText(partner_text); } else { - partner_text->setText(getString("no_partner_text")); + partner_text_ctrl->setText(getString("no_partner_text")); } } @@ -886,53 +1169,29 @@ void LLPanelProfileSecondLife::fillAccountStatus(const LLAvatarData* avatar_data // std::string caption_text = getString("CaptionTextAcctInfo", args); - getChild("acc_status_text")->setValue(caption_text); + getChild("account_info")->setValue(caption_text); } -void LLPanelProfileSecondLife::onMapButtonClick() +void LLPanelProfileSecondLife::fillRightsData() { - LLAvatarActions::showOnMap(getAvatarId()); -} - -void LLPanelProfileSecondLife::pay() -{ - LLAvatarActions::pay(getAvatarId()); -} - -void LLPanelProfileSecondLife::onClickToggleBlock() -{ - bool blocked = LLAvatarActions::toggleBlock(getAvatarId()); - - updateButtons(); - // we are hiding one button and showing another, set focus - if (blocked) + const LLRelationship* relation = LLAvatarTracker::instance().getBuddyInfo(getAvatarId()); + // If true - we are viewing friend's profile, enable check boxes and set values. + if (relation) { - mUnblockButton->setFocus(true); + S32 rights = relation->getRightsGrantedTo(); + + mSeeOnlineToggle->setToggleState(LLRelationship::GRANT_ONLINE_STATUS & rights ? TRUE : FALSE); + mSeeOnMapToggle->setToggleState(LLRelationship::GRANT_MAP_LOCATION & rights ? TRUE : FALSE); + mEditObjectsToggle->setToggleState(LLRelationship::GRANT_MODIFY_OBJECTS & rights ? TRUE : FALSE); } else { - mBlockButton->setFocus(true); + mSeeOnlineToggle->setToggleState(false); + mSeeOnMapToggle->setToggleState(false); + mEditObjectsToggle->setToggleState(false); } -} -void LLPanelProfileSecondLife::onAddFriendButtonClick() -{ - LLAvatarActions::requestFriendshipDialog(getAvatarId()); -} - -void LLPanelProfileSecondLife::onIMButtonClick() -{ - LLAvatarActions::startIM(getAvatarId()); -} - -void LLPanelProfileSecondLife::onTeleportButtonClick() -{ - LLAvatarActions::offerTeleport(getAvatarId()); -} - -void LLPanelProfileSecondLife::onGroupInvite() -{ - LLAvatarActions::inviteToGroup(getAvatarId()); + childSetVisible("permissions_panel", NULL != relation); } void LLPanelProfileSecondLife::onImageLoaded(BOOL success, LLViewerFetchedTexture *imagep) @@ -980,8 +1239,14 @@ void LLPanelProfileSecondLife::onImageLoaded(BOOL success, // virtual, called by LLAvatarTracker void LLPanelProfileSecondLife::changed(U32 mask) { - updateOnlineStatus(); - updateButtons(); + if (mask & LLFriendObserver::ONLINE) + { + updateOnlineStatus(); + } + if (mask != LLFriendObserver::ONLINE) + { + fillRightsData(); + } } // virtual, called by LLVoiceClient @@ -1042,132 +1307,273 @@ void LLPanelProfileSecondLife::updateOnlineStatus() void LLPanelProfileSecondLife::processOnlineStatus(bool online) { - mStatusText->setVisible(isGrantedToSeeOnlineStatus()); - - std::string status = getString(online ? "status_online" : "status_offline"); - - mStatusText->setValue(status); - mStatusText->setColor(online ? - LLUIColorTable::instance().getColor("StatusUserOnline") : - LLUIColorTable::instance().getColor("StatusUserOffline")); } -void LLPanelProfileSecondLife::updateButtons() +void LLPanelProfileSecondLife::setLoaded() { - LLPanelProfileTab::updateButtons(); + LLPanelProfileTab::setLoaded(); - if (getSelfProfile() && !getEmbedded()) + if (getSelfProfile()) { - mShowInSearchCheckbox->setVisible(TRUE); - mShowInSearchCheckbox->setEnabled(TRUE); + mShowInSearchCombo->setEnabled(TRUE); mDescriptionEdit->setEnabled(TRUE); } - - if (!getSelfProfile()) - { - LLUUID av_id = getAvatarId(); - bool is_buddy_online = LLAvatarTracker::instance().isBuddyOnline(getAvatarId()); - - if (LLAvatarActions::isFriend(av_id)) - { - // RLVa support - //mTeleportButton->setEnabled(is_buddy_online); - const LLRelationship* friend_status = LLAvatarTracker::instance().getBuddyInfo(av_id); - bool can_offer_tp = (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC) || - (gRlvHandler.isException(RLV_BHVR_TPLURE, av_id, ERlvExceptionCheck::Permissive) || - friend_status->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION))); - - mTeleportButton->setEnabled(is_buddy_online && can_offer_tp); - // - //Disable "Add Friend" button for friends. - mAddFriendButton->setEnabled(false); - } - else - { - // RLVa support - //mTeleportButton->setEnabled(true); - bool can_offer_tp = (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC) || - gRlvHandler.isException(RLV_BHVR_TPLURE, av_id, ERlvExceptionCheck::Permissive)); - mTeleportButton->setEnabled(can_offer_tp); - // - mAddFriendButton->setEnabled(true); - } - - // RLVa support - //bool enable_map_btn = (is_buddy_online && is_agent_mappable(av_id)) || gAgent.isGodlike(); - bool enable_map_btn = ((is_buddy_online && is_agent_mappable(av_id)) || gAgent.isGodlike()) && !gRlvHandler.hasBehaviour(RLV_BHVR_SHOWWORLDMAP); - // - mShowOnMapButton->setEnabled(enable_map_btn); - - bool enable_block_btn = LLAvatarActions::canBlock(av_id) && !LLAvatarActions::isBlocked(av_id); - mBlockButton->setVisible(enable_block_btn); - - bool enable_unblock_btn = LLAvatarActions::isBlocked(av_id); - mUnblockButton->setVisible(enable_unblock_btn); - } } -void LLPanelProfileSecondLife::onClickSetName() -{ - LLAvatarNameCache::get(getAvatarId(), boost::bind(&LLPanelProfileSecondLife::onAvatarNameCacheSetName, this, _1, _2)); - LLFirstUse::setDisplayName(false); + +class LLProfileImagePicker : public LLFilePickerThread +{ +public: + LLProfileImagePicker(EProfileImageType type, LLHandle *handle); + ~LLProfileImagePicker(); + void notify(const std::vector& filenames) override; + +private: + LLHandle *mHandle; + EProfileImageType mType; +}; + +LLProfileImagePicker::LLProfileImagePicker(EProfileImageType type, LLHandle *handle) + : LLFilePickerThread(LLFilePicker::FFLOAD_IMAGE), + mHandle(handle), + mType(type) +{ } -void LLPanelProfileSecondLife::onCommitTexture() +LLProfileImagePicker::~LLProfileImagePicker() { - LLViewerFetchedTexture* imagep = LLViewerTextureManager::getFetchedTexture(mSecondLifePic->getImageAssetID()); - if (imagep->getFullHeight()) + delete mHandle; +} + +void LLProfileImagePicker::notify(const std::vector& filenames) +{ + if (mHandle->isDead()) { - onImageLoaded(true, imagep); + return; } - else + std::string file_path = filenames[0]; + if (file_path.empty()) { - imagep->setLoadedCallback(onImageLoaded, - MAX_DISCARD_LEVEL, - FALSE, - FALSE, - new LLHandle(getHandle()), - NULL, - FALSE); + return; } + + // generate a temp texture file for coroutine + std::string temp_file = gDirUtilp->getTempFilename(); + U32 codec = LLImageBase::getCodecFromExtension(gDirUtilp->getExtension(file_path)); + const S32 MAX_DIM = 256; + if (!LLViewerTextureList::createUploadFile(file_path, temp_file, codec, MAX_DIM)) + { + //todo: image not supported notification + LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)PROFILE_IMAGE_SL << ", failed to open image" << LL_ENDL; + return; + } + + std::string cap_url = gAgent.getRegionCapability(PROFILE_IMAGE_UPLOAD_CAP); + if (cap_url.empty()) + { + LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)PROFILE_IMAGE_SL << ", no cap found" << LL_ENDL; + return; + } + + LLPanelProfileSecondLife* panel = static_cast(mHandle->get()); + panel->setProfileImageUploading(true); + + LLCoros::instance().launch("postAgentUserImageCoro", + boost::bind(post_profile_image_coro, cap_url, mType, temp_file, mHandle)); + + mHandle = nullptr; // transferred to post_profile_image_coro } void LLPanelProfileSecondLife::onCommitMenu(const LLSD& userdata) { - LLAvatarName av_name; - if (!LLAvatarNameCache::get(getAvatarId(), &av_name)) + const std::string item_name = userdata.asString(); + const LLUUID agent_id = getAvatarId(); + // todo: consider moving this into LLAvatarActions::onCommit(name, id) + // and making all other flaoters, like people menu do the same + if (item_name == "im") { - // shouldn't happen, button(menu) is supposed to be invisible while name is fetching - LL_WARNS() << "Failed to get agent data" << LL_ENDL; - return; + LLAvatarActions::startIM(agent_id); + } + else if (item_name == "offer_teleport") + { + LLAvatarActions::offerTeleport(agent_id); + } + else if (item_name == "request_teleport") + { + LLAvatarActions::teleportRequest(agent_id); + } + else if (item_name == "voice_call") + { + LLAvatarActions::startCall(agent_id); + } + else if (item_name == "callog") + { + LLAvatarActions::viewChatHistory(agent_id); + } + else if (item_name == "add_friend") + { + LLAvatarActions::requestFriendshipDialog(agent_id); + } + else if (item_name == "remove_friend") + { + LLAvatarActions::removeFriendDialog(agent_id); + } + else if (item_name == "invite_to_group") + { + LLAvatarActions::inviteToGroup(agent_id); + } + else if (item_name == "can_show_on_map") + { + LLAvatarActions::showOnMap(agent_id); + } + else if (item_name == "share") + { + LLAvatarActions::share(agent_id); + } + else if (item_name == "pay") + { + LLAvatarActions::pay(agent_id); + } + else if (item_name == "toggle_block_agent") + { + LLAvatarActions::toggleBlock(agent_id); + } + else if (item_name == "copy_user_id") + { + LLWString wstr = utf8str_to_wstring(getAvatarId().asString()); + LLClipboard::instance().copyToClipboard(wstr, 0, wstr.size()); + } + else if (item_name == "agent_permissions") + { + onShowAgentPermissionsDialog(); + } + else if (item_name == "copy_display_name" + || item_name == "copy_username") + { + LLAvatarName av_name; + if (!LLAvatarNameCache::get(getAvatarId(), &av_name)) + { + // shouldn't happen, option is supposed to be invisible while name is fetching + LL_WARNS() << "Failed to get agent data" << LL_ENDL; + return; + } + LLWString wstr; + if (item_name == "copy_display_name") + { + wstr = utf8str_to_wstring(av_name.getDisplayName(true)); + } + else if (item_name == "copy_username") + { + wstr = utf8str_to_wstring(av_name.getUserName()); + } + LLClipboard::instance().copyToClipboard(wstr, 0, wstr.size()); + } + else if (item_name == "edit_display_name") + { + LLAvatarNameCache::get(getAvatarId(), boost::bind(&LLPanelProfileSecondLife::onAvatarNameCacheSetName, this, _1, _2)); + LLFirstUse::setDisplayName(false); + } + else if (item_name == "edit_partner") + { + std::string url = "https://[GRID]/my/account/partners.php"; + LLSD subs; + url = LLWeb::expandURLSubstitutions(url, subs); + LLUrlAction::openURL(url); + } + else if (item_name == "change_photo") + { + (new LLProfileImagePicker(PROFILE_IMAGE_SL, new LLHandle(getHandle())))->getFile(); + } + else if (item_name == "remove_photo") + { + LLSD params; + params["sl_image_id"] = LLUUID::null; + + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); + if (!cap_url.empty()) + { + LLCoros::instance().launch("putAgentUserInfoCoro", + boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), params)); + + mSecondLifePic->setValue("Generic_Person_Large"); + } + else + { + LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL; + } + } +} + +bool LLPanelProfileSecondLife::onEnableMenu(const LLSD& userdata) +{ + const std::string item_name = userdata.asString(); + const LLUUID agent_id = getAvatarId(); + if (item_name == "offer_teleport" || item_name == "request_teleport") + { + return LLAvatarActions::canOfferTeleport(agent_id); + } + else if (item_name == "voice_call") + { + return mVoiceStatus; + } + else if (item_name == "callog") + { + return LLLogChat::isTranscriptExist(agent_id); + } + else if (item_name == "add_friend") + { + return !LLAvatarActions::isFriend(agent_id); + } + else if (item_name == "remove_friend") + { + return LLAvatarActions::isFriend(agent_id); + } + else if (item_name == "can_show_on_map") + { + // RLVa + //return (LLAvatarTracker::instance().isBuddyOnline(agent_id) && is_agent_mappable(agent_id)) + //|| gAgent.isGodlike(); + return ((LLAvatarTracker::instance().isBuddyOnline(agent_id) && is_agent_mappable(agent_id)) + || gAgent.isGodlike()) && !gRlvHandler.hasBehaviour(RLV_BHVR_SHOWWORLDMAP); + // + } + else if (item_name == "toggle_block_agent") + { + return LLAvatarActions::canBlock(agent_id); + } + else if (item_name == "agent_permissions") + { + return LLAvatarActions::isFriend(agent_id); + } + else if (item_name == "copy_display_name" + || item_name == "copy_username") + { + return !mAvatarNameCacheConnection.connected(); + } + else if (item_name == "change_photo") + { + std::string cap_url = gAgent.getRegionCapability(PROFILE_IMAGE_UPLOAD_CAP); + return !cap_url.empty() && !mWaitingForImageUpload; + } + else if (item_name == "remove_photo") + { + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); + return !cap_url.empty() && !mWaitingForImageUpload; } + return false; +} + +bool LLPanelProfileSecondLife::onCheckMenu(const LLSD& userdata) +{ const std::string item_name = userdata.asString(); - LLWString wstr; - if (item_name == "display") + const LLUUID agent_id = getAvatarId(); + if (item_name == "toggle_block_agent") { - wstr = utf8str_to_wstring(av_name.getDisplayName(true)); + return LLAvatarActions::isBlocked(agent_id); } - else if (item_name == "name") - { - wstr = utf8str_to_wstring(av_name.getAccountName()); - } - // Gear button - else if (item_name == "complete_name") - { - wstr = utf8str_to_wstring(av_name.getCompleteName()); - } - else if (item_name == "uri") - { - wstr = utf8str_to_wstring(LLSLURL("agent", getAvatarId(), "about").getSLURLString()); - } - // - else if (item_name == "id") - { - wstr = utf8str_to_wstring(getAvatarId().asString()); - } - LLClipboard::instance().copyToClipboard(wstr, 0, wstr.size()); + return false; } void LLPanelProfileSecondLife::onAvatarNameCacheSetName(const LLUUID& agent_id, const LLAvatarName& av_name) @@ -1198,15 +1604,84 @@ void LLPanelProfileSecondLife::onAvatarNameCacheSetName(const LLUUID& agent_id, LLFloaterReg::showInstance("display_name"); } -// RLVa support -void LLPanelProfileSecondLife::updateRlvRestrictions(ERlvBehaviour behavior) +void LLPanelProfileSecondLife::setDescriptionText(const std::string &text) { - if (behavior == RLV_BHVR_SHOWLOC || behavior == RLV_BHVR_SHOWWORLDMAP) + mSaveDescriptionChanges->setEnabled(FALSE); + mDiscardDescriptionChanges->setEnabled(FALSE); + mDescriptionText = text; + mDescriptionEdit->setValue(mDescriptionText); +} + +void LLPanelProfileSecondLife::onSetDescriptionDirty() +{ + mSaveDescriptionChanges->setEnabled(TRUE); + mDiscardDescriptionChanges->setEnabled(TRUE); +} + +void LLPanelProfileSecondLife::onShowInSearchCallback() +{ + if (mAllowPublish == mShowInSearchCombo->getValue().asBoolean()) { - updateButtons(); + return; + } + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); + if (!cap_url.empty()) + { + mAllowPublish = mShowInSearchCombo->getValue().asBoolean(); + LLSD data; + data["allow_publish"] = mAllowPublish; + LLCoros::instance().launch("putAgentUserInfoCoro", + boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), data)); + } + else + { + LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL; + } +} + +void LLPanelProfileSecondLife::onSaveDescriptionChanges() +{ + mDescriptionText = mDescriptionEdit->getValue().asString(); + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); + if (!cap_url.empty()) + { + LLCoros::instance().launch("putAgentUserInfoCoro", + boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), LLSD().with("sl_about_text", mDescriptionText))); + } + else + { + LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL; + } + + mSaveDescriptionChanges->setEnabled(FALSE); + mDiscardDescriptionChanges->setEnabled(FALSE); +} + +void LLPanelProfileSecondLife::onDiscardDescriptionChanges() +{ + setDescriptionText(mDescriptionText); +} + +void LLPanelProfileSecondLife::onShowAgentPermissionsDialog() +{ + LLFloater *floater = mFloaterPermissionsHandle.get(); + if (!floater) + { + LLFloater* parent_floater = gFloaterView->getParentFloater(this); + if (parent_floater) + { + LLFloaterProfilePermissions * perms = new LLFloaterProfilePermissions(parent_floater, getAvatarId()); + mFloaterPermissionsHandle = perms->getHandle(); + perms->openFloater(); + + parent_floater->addDependentFloater(mFloaterPermissionsHandle); + } + } + else // already open + { + floater->closeFloater(); } } -// ////////////////////////////////////////////////////////////////////////// // LLPanelProfileWeb @@ -1244,32 +1719,15 @@ BOOL LLPanelProfileWeb::postBuild() return TRUE; } -void LLPanelProfileWeb::processProperties(void* data, EAvatarProcessorType type) -{ - if (APT_PROPERTIES == type) - { - const LLAvatarData* avatar_data = static_cast(data); - if (avatar_data && getAvatarId() == avatar_data->avatar_id) - { - updateButtons(); - } - } -} - void LLPanelProfileWeb::resetData() { mWebBrowser->navigateHome(); } -void LLPanelProfileWeb::apply(LLAvatarData* data) -{ - -} - void LLPanelProfileWeb::updateData() { LLUUID avatar_id = getAvatarId(); - if (!getIsLoading() && avatar_id.notNull() && !mURLWebProfile.empty()) + if (!getStarted() && avatar_id.notNull() && !mURLWebProfile.empty()) { setIsLoading(); @@ -1362,180 +1820,13 @@ void LLPanelProfileWeb::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent e } } -void LLPanelProfileWeb::updateButtons() -{ - LLPanelProfileTab::updateButtons(); -} - -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// - -static const S32 WANT_CHECKS = 8; -static const S32 SKILL_CHECKS = 6; - -LLPanelProfileInterests::LLPanelProfileInterests() - : LLPanelProfileTab() -{ -} - -LLPanelProfileInterests::~LLPanelProfileInterests() -{ -} - -void LLPanelProfileInterests::onOpen(const LLSD& key) -{ - LLPanelProfileTab::onOpen(key); - - resetData(); -} - -BOOL LLPanelProfileInterests::postBuild() -{ - mWantToEditor = getChild("want_to_edit"); - mSkillsEditor = getChild("skills_edit"); - mLanguagesEditor = getChild("languages_edit"); - - for (S32 i = 0; i < WANT_CHECKS; ++i) - { - std::string check_name = llformat("chk%d", i); - mWantChecks[i] = getChild(check_name); - } - - for (S32 i = 0; i < SKILL_CHECKS; ++i) - { - std::string check_name = llformat("schk%d", i); - mSkillChecks[i] = getChild(check_name); - } - - return TRUE; -} - - -void LLPanelProfileInterests::processProperties(void* data, EAvatarProcessorType type) -{ - if (APT_INTERESTS_INFO == type) - { - const LLInterestsData* interests_data = static_cast(data); - if (interests_data && getAvatarId() == interests_data->avatar_id) - { - for (S32 i = 0; i < WANT_CHECKS; ++i) - { - if (interests_data->want_to_mask & (1<setValue(TRUE); - } - else - { - mWantChecks[i]->setValue(FALSE); - } - } - - for (S32 i = 0; i < SKILL_CHECKS; ++i) - { - if (interests_data->skills_mask & (1<setValue(TRUE); - } - else - { - mSkillChecks[i]->setValue(FALSE); - } - } - - mWantToEditor->setText(interests_data->want_to_text); - mSkillsEditor->setText(interests_data->skills_text); - mLanguagesEditor->setText(interests_data->languages_text); - - updateButtons(); - } - } -} - -void LLPanelProfileInterests::resetData() -{ - mWantToEditor->setValue(LLStringUtil::null); - mSkillsEditor->setValue(LLStringUtil::null); - mLanguagesEditor->setValue(LLStringUtil::null); - - for (S32 i = 0; i < WANT_CHECKS; ++i) - { - mWantChecks[i]->setValue(FALSE); - } - - for (S32 i = 0; i < SKILL_CHECKS; ++i) - { - mSkillChecks[i]->setValue(FALSE); - } -} - -void LLPanelProfileInterests::apply() -{ - if (getIsLoaded() && getSelfProfile()) - { - LLInterestsData interests_data = LLInterestsData(); - - interests_data.want_to_mask = 0; - for (S32 i = 0; i < WANT_CHECKS; ++i) - { - if (mWantChecks[i]->getValue().asBoolean()) - { - interests_data.want_to_mask |= (1 << i); - } - } - - interests_data.skills_mask = 0; - for (S32 i = 0; i < SKILL_CHECKS; ++i) - { - if (mSkillChecks[i]->getValue().asBoolean()) - { - interests_data.skills_mask |= (1 << i); - } - } - - interests_data.want_to_text = mWantToEditor->getText(); - interests_data.skills_text = mSkillsEditor->getText(); - interests_data.languages_text = mLanguagesEditor->getText(); - - LLAvatarPropertiesProcessor::getInstance()->sendInterestsInfoUpdate(&interests_data); - } - -} - -void LLPanelProfileInterests::updateButtons() -{ - LLPanelProfileTab::updateButtons(); - - if (getSelfProfile() && !getEmbedded()) - { - mWantToEditor->setEnabled(TRUE); - mSkillsEditor->setEnabled(TRUE); - mLanguagesEditor->setEnabled(TRUE); - - for (S32 i = 0; i < WANT_CHECKS; ++i) - { - mWantChecks[i]->setEnabled(TRUE); - } - - for (S32 i = 0; i < SKILL_CHECKS; ++i) - { - mSkillChecks[i]->setEnabled(TRUE); - } - } -} - -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// LLPanelProfileFirstLife::LLPanelProfileFirstLife() - : LLPanelProfileTab(), - mIsEditing(false) + : LLPanelProfileTab() { } @@ -1546,9 +1837,18 @@ LLPanelProfileFirstLife::~LLPanelProfileFirstLife() BOOL LLPanelProfileFirstLife::postBuild() { mDescriptionEdit = getChild("fl_description_edit"); - mPicture = getChild("real_world_pic"); + mPicture = getChild("real_world_pic"); - mDescriptionEdit->setFocusReceivedCallback(boost::bind(&LLPanelProfileFirstLife::onDescriptionFocusReceived, this)); + mChangePhoto = getChild("fl_upload_image"); + mRemovePhoto = getChild("fl_remove_image"); + mSaveChanges = getChild("fl_save_changes"); + mDiscardChanges = getChild("fl_discard_changes"); + + mChangePhoto->setCommitCallback([this](LLUICtrl*, void*) { onChangePhoto(); }, nullptr); + mRemovePhoto->setCommitCallback([this](LLUICtrl*, void*) { onRemovePhoto(); }, nullptr); + mSaveChanges->setCommitCallback([this](LLUICtrl*, void*) { onSaveDescriptionChanges(); }, nullptr); + mDiscardChanges->setCommitCallback([this](LLUICtrl*, void*) { onDiscardDescriptionChanges(); }, nullptr); + mDescriptionEdit->setKeystrokeCallback([this](LLTextEditor* caller) { onSetDescriptionDirty(); }); return TRUE; } @@ -1560,77 +1860,120 @@ void LLPanelProfileFirstLife::onOpen(const LLSD& key) resetData(); } - -void LLPanelProfileFirstLife::onDescriptionFocusReceived() +void LLPanelProfileFirstLife::setProfileImageUploading(bool loading) { - if (!mIsEditing && getSelfProfile()) + mChangePhoto->setEnabled(!loading); + mRemovePhoto->setEnabled(!loading); + + LLLoadingIndicator* indicator = getChild("image_upload_indicator"); + indicator->setVisible(loading); + if (loading) { - mIsEditing = true; - mDescriptionEdit->setParseHTML(false); - mDescriptionEdit->setText(mCurrentDescription); + indicator->start(); + } + else + { + indicator->stop(); } } -void LLPanelProfileFirstLife::processProperties(void* data, EAvatarProcessorType type) +void LLPanelProfileFirstLife::setProfileImageUploaded(const LLUUID &image_asset_id) { - if (APT_PROPERTIES == type) + mPicture->setValue(image_asset_id); + setProfileImageUploading(false); +} + +void LLPanelProfileFirstLife::onChangePhoto() +{ + (new LLProfileImagePicker(PROFILE_IMAGE_FL, new LLHandle(getHandle())))->getFile(); +} + +void LLPanelProfileFirstLife::onRemovePhoto() +{ + LLSD params; + params["fl_image_id"] = LLUUID::null; + + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); + if (!cap_url.empty()) { - const LLAvatarData* avatar_data = static_cast(data); - if (avatar_data && getAvatarId() == avatar_data->avatar_id) - { - mCurrentDescription = avatar_data->fl_about_text; - mDescriptionEdit->setValue(mCurrentDescription); - mPicture->setValue(avatar_data->fl_image_id); - updateButtons(); - } + LLCoros::instance().launch("putAgentUserInfoCoro", + boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), params)); + + mPicture->setValue("Generic_Person_Large"); } + else + { + LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL; + } +} + +void LLPanelProfileFirstLife::setDescriptionText(const std::string &text) +{ + mSaveChanges->setEnabled(FALSE); + mDiscardChanges->setEnabled(FALSE); + mCurrentDescription = text; + mDescriptionEdit->setValue(mCurrentDescription); +} + +void LLPanelProfileFirstLife::onSetDescriptionDirty() +{ + mSaveChanges->setEnabled(TRUE); + mDiscardChanges->setEnabled(TRUE); +} + +void LLPanelProfileFirstLife::onSaveDescriptionChanges() +{ + mCurrentDescription = mDescriptionEdit->getValue().asString(); + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); + if (!cap_url.empty()) + { + LLCoros::instance().launch("putAgentUserInfoCoro", + boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), LLSD().with("fl_about_text", mCurrentDescription))); + } + else + { + LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL; + } + + mSaveChanges->setEnabled(FALSE); + mDiscardChanges->setEnabled(FALSE); +} + +void LLPanelProfileFirstLife::onDiscardDescriptionChanges() +{ + setDescriptionText(mCurrentDescription); } void LLPanelProfileFirstLife::processProperties(const LLAvatarData* avatar_data) { - mCurrentDescription = avatar_data->fl_about_text; - mDescriptionEdit->setValue(mCurrentDescription); - mPicture->setValue(avatar_data->fl_image_id); - updateButtons(); + setDescriptionText(avatar_data->fl_about_text); + if (avatar_data->fl_image_id.notNull()) + { + mPicture->setValue(avatar_data->fl_image_id); + } + else + { + mPicture->setValue("Generic_Person_Large"); + } + setLoaded(); } void LLPanelProfileFirstLife::resetData() { mDescriptionEdit->setValue(LLStringUtil::null); - mPicture->setValue(mPicture->getDefaultImageAssetID()); + mPicture->setValue("Generic_Person_Large"); + + mChangePhoto->setVisible(getSelfProfile()); + mRemovePhoto->setVisible(getSelfProfile()); + mSaveChanges->setVisible(getSelfProfile()); + mDiscardChanges->setVisible(getSelfProfile()); } -void LLPanelProfileFirstLife::apply(LLAvatarData* data) +void LLPanelProfileFirstLife::setLoaded() { + LLPanelProfileTab::setLoaded(); - std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); - if (getIsLoaded() && !cap_url.empty()) - { - LLSD params = LLSDMap(); - if (data->fl_image_id != mPicture->getImageAssetID()) - { - params["fl_image_id"] = mPicture->getImageAssetID(); - } - if (data->fl_about_text != mDescriptionEdit->getValue().asString()) - { - params["fl_about_text"] = mDescriptionEdit->getValue().asString(); - } - if (!params.emptyMap()) - { - LLCoros::instance().launch("putAgentUserInfoCoro", - boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), params)); - } - } - - data->fl_image_id = mPicture->getImageAssetID(); - data->fl_about_text = mDescriptionEdit->getValue().asString(); -} - -void LLPanelProfileFirstLife::updateButtons() -{ - LLPanelProfileTab::updateButtons(); - - if (getSelfProfile() && !getEmbedded()) + if (getSelfProfile()) { mDescriptionEdit->setEnabled(TRUE); mPicture->setEnabled(TRUE); @@ -1643,28 +1986,18 @@ void LLPanelProfileFirstLife::updateButtons() LLPanelProfileNotes::LLPanelProfileNotes() : LLPanelProfileTab() -, mAvatarNameCacheConnection() { } LLPanelProfileNotes::~LLPanelProfileNotes() { - if (getAvatarId().notNull()) - { - LLAvatarTracker::instance().removeParticularFriendObserver(getAvatarId(), this); - } - - if (mAvatarNameCacheConnection.connected()) - { - mAvatarNameCacheConnection.disconnect(); - } } void LLPanelProfileNotes::updateData() { LLUUID avatar_id = getAvatarId(); - if (!getIsLoading() && avatar_id.notNull()) + if (!getStarted() && avatar_id.notNull()) { setIsLoading(); @@ -1679,14 +2012,13 @@ void LLPanelProfileNotes::updateData() BOOL LLPanelProfileNotes::postBuild() { - mOnlineStatus = getChild("status_check"); - mMapRights = getChild("map_check"); - mEditObjectRights = getChild("objects_check"); mNotesEditor = getChild("notes_edit"); + mSaveChanges = getChild("notes_save_changes"); + mDiscardChanges = getChild("notes_discard_changes"); - mEditObjectRights->setCommitCallback(boost::bind(&LLPanelProfileNotes::onCommitRights, this)); - - mNotesEditor->setCommitCallback(boost::bind(&LLPanelProfileNotes::onCommitNotes,this)); + mSaveChanges->setCommitCallback([this](LLUICtrl*, void*) { onSaveNotesChanges(); }, nullptr); + mDiscardChanges->setCommitCallback([this](LLUICtrl*, void*) { onDiscardNotesChanges(); }, nullptr); + mNotesEditor->setKeystrokeCallback([this](LLTextEditor* caller) { onSetNotesDirty(); }); return TRUE; } @@ -1696,36 +2028,6 @@ void LLPanelProfileNotes::onOpen(const LLSD& key) LLPanelProfileTab::onOpen(key); resetData(); - - fillRightsData(); - - mAvatarNameCacheConnection = LLAvatarNameCache::get(getAvatarId(), boost::bind(&LLPanelProfileNotes::onAvatarNameCache, this, _1, _2)); -} - -void LLPanelProfileNotes::apply() -{ - onCommitNotes(); - applyRights(); -} - -void LLPanelProfileNotes::fillRightsData() -{ - mOnlineStatus->setValue(FALSE); - mMapRights->setValue(FALSE); - mEditObjectRights->setValue(FALSE); - - const LLRelationship* relation = LLAvatarTracker::instance().getBuddyInfo(getAvatarId()); - // If true - we are viewing friend's profile, enable check boxes and set values. - if(relation) - { - S32 rights = relation->getRightsGrantedTo(); - - mOnlineStatus->setValue(LLRelationship::GRANT_ONLINE_STATUS & rights ? TRUE : FALSE); - mMapRights->setValue(LLRelationship::GRANT_MAP_LOCATION & rights ? TRUE : FALSE); - mEditObjectRights->setValue(LLRelationship::GRANT_MODIFY_OBJECTS & rights ? TRUE : FALSE); - } - - enableCheckboxes(NULL != relation); } void LLPanelProfileNotes::onCommitNotes() @@ -1746,154 +2048,64 @@ void LLPanelProfileNotes::onCommitNotes() } } -void LLPanelProfileNotes::rightsConfirmationCallback(const LLSD& notification, - const LLSD& response) +void LLPanelProfileNotes::setNotesText(const std::string &text) { - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (option != 0) - { - mEditObjectRights->setValue(mEditObjectRights->getValue().asBoolean() ? FALSE : TRUE); - } + mSaveChanges->setEnabled(FALSE); + mDiscardChanges->setEnabled(FALSE); + mCurrentNotes = text; + mNotesEditor->setValue(mCurrentNotes); } -void LLPanelProfileNotes::confirmModifyRights(bool grant) +void LLPanelProfileNotes::onSetNotesDirty() { - LLSD args; - args["NAME"] = LLSLURL("agent", getAvatarId(), "completename").getSLURLString(); - - - LLNotificationsUtil::add(grant ? "GrantModifyRights" : "RevokeModifyRights", args, LLSD(), - boost::bind(&LLPanelProfileNotes::rightsConfirmationCallback, this, _1, _2)); - + mSaveChanges->setEnabled(TRUE); + mDiscardChanges->setEnabled(TRUE); } -void LLPanelProfileNotes::onCommitRights() +void LLPanelProfileNotes::onSaveNotesChanges() { - const LLRelationship* buddy_relationship = LLAvatarTracker::instance().getBuddyInfo(getAvatarId()); + mCurrentNotes = mNotesEditor->getValue().asString(); + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); + if (!cap_url.empty()) + { + LLCoros::instance().launch("putAgentUserInfoCoro", + boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), LLSD().with("notes", mCurrentNotes))); + } + else + { + LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL; + } - if (!buddy_relationship) - { - LL_WARNS("LegacyProfile") << "Trying to modify rights for non-friend avatar. Skipped." << LL_ENDL; - return; - } - - bool allow_modify_objects = mEditObjectRights->getValue().asBoolean(); - - // if modify objects checkbox clicked - if (buddy_relationship->isRightGrantedTo( - LLRelationship::GRANT_MODIFY_OBJECTS) != allow_modify_objects) - { - confirmModifyRights(allow_modify_objects); - } + mSaveChanges->setEnabled(FALSE); + mDiscardChanges->setEnabled(FALSE); } -void LLPanelProfileNotes::applyRights() +void LLPanelProfileNotes::onDiscardNotesChanges() { - const LLRelationship* buddy_relationship = LLAvatarTracker::instance().getBuddyInfo(getAvatarId()); - - if (!buddy_relationship) - { - // Lets have a warning log message instead of having a crash. EXT-4947. - LL_WARNS("LegacyProfile") << "Trying to modify rights for non-friend avatar. Skipped." << LL_ENDL; - return; - } - - S32 rights = 0; - - if (mOnlineStatus->getValue().asBoolean()) - { - rights |= LLRelationship::GRANT_ONLINE_STATUS; - } - if (mMapRights->getValue().asBoolean()) - { - rights |= LLRelationship::GRANT_MAP_LOCATION; - } - if (mEditObjectRights->getValue().asBoolean()) - { - rights |= LLRelationship::GRANT_MODIFY_OBJECTS; - } - - LLAvatarPropertiesProcessor::getInstance()->sendFriendRights(getAvatarId(), rights); -} - -void LLPanelProfileNotes::processProperties(void* data, EAvatarProcessorType type) -{ - if (APT_NOTES == type) - { - LLAvatarNotes* avatar_notes = static_cast(data); - if (avatar_notes && getAvatarId() == avatar_notes->target_id) - { - processProperties(avatar_notes); - LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(),this); - } - } + setNotesText(mCurrentNotes); } void LLPanelProfileNotes::processProperties(LLAvatarNotes* avatar_notes) { mNotesEditor->setValue(avatar_notes->notes); - // Don't enable editor in embedded mode since there is no way to save them - //mNotesEditor->setEnabled(TRUE); - mNotesEditor->setEnabled(!getEmbedded()); - // - updateButtons(); + mNotesEditor->setEnabled(TRUE); + setLoaded(); } void LLPanelProfileNotes::resetData() { resetLoading(); mNotesEditor->setValue(LLStringUtil::null); - mOnlineStatus->setValue(FALSE); - mMapRights->setValue(FALSE); - mEditObjectRights->setValue(FALSE); - - mURLWebProfile.clear(); -} - -void LLPanelProfileNotes::enableCheckboxes(bool enable) -{ - mOnlineStatus->setEnabled(enable); - mMapRights->setEnabled(enable); - mEditObjectRights->setEnabled(enable); -} - -// virtual, called by LLAvatarTracker -void LLPanelProfileNotes::changed(U32 mask) -{ - // update rights to avoid have checkboxes enabled when friendship is terminated. EXT-4947. - fillRightsData(); } void LLPanelProfileNotes::setAvatarId(const LLUUID& avatar_id) { if (avatar_id.notNull()) { - if (getAvatarId().notNull()) - { - LLAvatarTracker::instance().removeParticularFriendObserver(getAvatarId(), this); - } LLPanelProfileTab::setAvatarId(avatar_id); - LLAvatarTracker::instance().addParticularFriendObserver(getAvatarId(), this); } } -void LLPanelProfileNotes::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name) -{ - mAvatarNameCacheConnection.disconnect(); - - std::string username = av_name.getAccountName(); - if (username.empty()) - { - username = LLCacheName::buildUsername(av_name.getDisplayName()); - } - else - { - LLStringUtil::replaceChar(username, ' ', '.'); - } - - mURLWebProfile = getProfileURL(username, false); -} - ////////////////////////////////////////////////////////////////////////// // LLPanelProfile @@ -1912,15 +2124,6 @@ BOOL LLPanelProfile::postBuild() return TRUE; } -void LLPanelProfile::processProperties(void* data, EAvatarProcessorType type) -{ - //*TODO: figure out what this does - mTabContainer->setCommitCallback(boost::bind(&LLPanelProfile::onTabChange, this)); - - // Load data on currently opened tab as well - onTabChange(); -} - void LLPanelProfile::onTabChange() { LLPanelProfileTab* active_panel = dynamic_cast(mTabContainer->getCurrentPanel()); @@ -1933,8 +2136,6 @@ void LLPanelProfile::onTabChange() void LLPanelProfile::updateBtnsVisibility() { - getChild("ok_btn")->setVisible(((getSelfProfile() && !getEmbedded()) || isNotesTabSelected())); - getChild("cancel_btn")->setVisible(((getSelfProfile() && !getEmbedded()) || isNotesTabSelected())); } void LLPanelProfile::onOpen(const LLSD& key) @@ -1952,7 +2153,6 @@ void LLPanelProfile::onOpen(const LLSD& key) mTabContainer = getChild("panel_profile_tabs"); mPanelSecondlife = findChild(PANEL_SECONDLIFE); mPanelWeb = findChild(PANEL_WEB); - mPanelInterests = findChild(PANEL_INTERESTS); mPanelPicks = findChild(PANEL_PICKS); mPanelClassifieds = findChild(PANEL_CLASSIFIEDS); mPanelFirstlife = findChild(PANEL_FIRSTLIFE); @@ -1960,29 +2160,19 @@ void LLPanelProfile::onOpen(const LLSD& key) mPanelSecondlife->onOpen(avatar_id); mPanelWeb->onOpen(avatar_id); - mPanelInterests->onOpen(avatar_id); mPanelPicks->onOpen(avatar_id); mPanelClassifieds->onOpen(avatar_id); mPanelFirstlife->onOpen(avatar_id); mPanelNotes->onOpen(avatar_id); - mPanelSecondlife->setEmbedded(getEmbedded()); - mPanelWeb->setEmbedded(getEmbedded()); - mPanelInterests->setEmbedded(getEmbedded()); - mPanelPicks->setEmbedded(getEmbedded()); - mPanelClassifieds->setEmbedded(getEmbedded()); - mPanelFirstlife->setEmbedded(getEmbedded()); - mPanelNotes->setEmbedded(getEmbedded()); - // Always request the base profile info resetLoading(); updateData(); updateBtnsVisibility(); - // KC - Not handling pick and classified opening thru onOpen - // because this would make unique profile floaters per slurl - // and result in multiple profile floaters for the same avatar + // Some tabs only request data when opened + mTabContainer->setCommitCallback(boost::bind(&LLPanelProfile::onTabChange, this)); } void LLPanelProfile::updateData() @@ -1990,7 +2180,7 @@ void LLPanelProfile::updateData() LLUUID avatar_id = getAvatarId(); // Todo: getIsloading functionality needs to be expanded to // include 'inited' or 'data_provided' state to not rerequest - if (!getIsLoading() && avatar_id.notNull()) + if (!getStarted() && avatar_id.notNull()) { setIsLoading(); @@ -2003,29 +2193,6 @@ void LLPanelProfile::updateData() } } -void LLPanelProfile::apply() -{ - if (getSelfProfile()) - { - //KC - AvatarData is spread over 3 different panels - // collect data from the last 2 and give to the first to save - mPanelFirstlife->apply(&mAvatarData); - mPanelWeb->apply(&mAvatarData); - mPanelSecondlife->apply(&mAvatarData); - - mPanelInterests->apply(); - mPanelPicks->apply(); - mPanelNotes->apply(); - mPanelClassifieds->apply(); - - //KC - Classifieds handles this itself - } - else - { - mPanelNotes->apply(); - } -} - void LLPanelProfile::showPick(const LLUUID& pick_id) { if (pick_id.notNull()) diff --git a/indra/newview/llpanelprofile.h b/indra/newview/llpanelprofile.h index 20eeb0d6a2..904215d3a3 100644 --- a/indra/newview/llpanelprofile.h +++ b/indra/newview/llpanelprofile.h @@ -41,13 +41,15 @@ // class LLPanelProfileSecondLife; // class LLPanelProfileWeb; -// class LLPanelProfileInterests; // class LLPanelProfilePicks; // class LLPanelProfileFirstLife; // class LLPanelProfileNotes; class LLAvatarName; +class LLButton; class LLCheckBoxCtrl; +class LLComboBox; +class LLIconCtrl; class LLTabContainer; class LLTextBox; class LLTextureCtrl; @@ -61,6 +63,7 @@ class LLPanelProfileClassifieds; class LLPanelProfilePicks; class LLViewerFetchedTexture; + /** * Panel for displaying Avatar's second life related info. */ @@ -75,11 +78,6 @@ public: /*virtual*/ void onOpen(const LLSD& key); - /** - * Saves changes. - */ - void apply(LLAvatarData* data); - /** * LLFriendObserver trigger */ @@ -93,8 +91,6 @@ public: /*virtual*/ BOOL postBuild(); - /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); - void resetData(); /** @@ -104,52 +100,38 @@ public: void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); + void setProfileImageUploading(bool loading); + void setProfileImageUploaded(const LLUUID &image_asset_id); + friend void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id); protected: /** * Process profile related data received from server. */ - virtual void processProfileProperties(const LLAvatarData* avatar_data); + void processProfileProperties(const LLAvatarData* avatar_data); /** * Processes group related data received from server. */ - virtual void processGroupProperties(const LLAvatarGroups* avatar_groups); + void processGroupProperties(const LLAvatarGroups* avatar_groups); /** * Fills common for Avatar profile and My Profile fields. */ - virtual void fillCommonData(const LLAvatarData* avatar_data); + void fillCommonData(const LLAvatarData* avatar_data); /** * Fills partner data. */ - virtual void fillPartnerData(const LLAvatarData* avatar_data); + void fillPartnerData(const LLAvatarData* avatar_data); /** * Fills account status. */ - virtual void fillAccountStatus(const LLAvatarData* avatar_data); + void fillAccountStatus(const LLAvatarData* avatar_data); - void onMapButtonClick(); - - /** - * Opens "Pay Resident" dialog. - */ - void pay(); - - /** - * Add/remove resident to/from your block list. - * Updates button focus - */ - void onClickToggleBlock(); - - void onAddFriendButtonClick(); - void onIMButtonClick(); - void onTeleportButtonClick(); - - void onGroupInvite(); + void fillRightsData(); void onImageLoaded(BOOL success, LLViewerFetchedTexture *imagep); static void onImageLoaded(BOOL success, @@ -178,45 +160,45 @@ protected: void processOnlineStatus(bool online); private: - /*virtual*/ void updateButtons(); - void onClickSetName(); - void onCommitTexture(); - void onCommitMenu(const LLSD& userdata); + /*virtual*/ void setLoaded(); + void onCommitMenu(const LLSD& userdata); + bool onEnableMenu(const LLSD& userdata); + bool onCheckMenu(const LLSD& userdata); void onAvatarNameCacheSetName(const LLUUID& id, const LLAvatarName& av_name); + void setDescriptionText(const std::string &text); + void onSetDescriptionDirty(); + void onShowInSearchCallback(); + void onSaveDescriptionChanges(); + void onDiscardDescriptionChanges(); + void onShowAgentPermissionsDialog(); + private: typedef std::map group_map_t; group_map_t mGroups; void openGroupProfile(); - LLTextBox* mStatusText; LLGroupList* mGroupList; - LLCheckBoxCtrl* mShowInSearchCheckbox; - LLTextureCtrl* mSecondLifePic; + LLComboBox* mShowInSearchCombo; + LLIconCtrl* mSecondLifePic; LLPanel* mSecondLifePicLayout; - LLTextBase* mDescriptionEdit; - LLButton* mTeleportButton; - LLButton* mShowOnMapButton; - LLButton* mBlockButton; - LLButton* mUnblockButton; - LLUICtrl* mNameLabel; - LLButton* mDisplayNameButton; - LLButton* mAddFriendButton; - LLButton* mGroupInviteButton; - LLButton* mPayButton; - LLButton* mIMButton; - LLMenuButton* mCopyMenuButton; - LLPanel* mGiveInvPanel; + LLTextEditor* mDescriptionEdit; + LLMenuButton* mAgentActionMenuButton; + LLButton* mSaveDescriptionChanges; + LLButton* mDiscardDescriptionChanges; + LLButton* mSeeOnlineToggle; + LLButton* mSeeOnMapToggle; + LLButton* mEditObjectsToggle; LLMenuButton* mOverflowButton; // Gear button + LLHandle mFloaterPermissionsHandle; + bool mVoiceStatus; + bool mWaitingForImageUpload; + bool mAllowPublish; + std::string mDescriptionText; boost::signals2::connection mAvatarNameCacheConnection; - - // RLVa support - boost::signals2::connection mRlvBehaviorCallbackConnection; - void updateRlvRestrictions(ERlvBehaviour behavior); - // }; @@ -235,15 +217,8 @@ public: /*virtual*/ BOOL postBuild(); - /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); - void resetData(); - /** - * Saves changes. - */ - void apply(LLAvatarData* data); - /** * Loads web profile. */ @@ -256,7 +231,6 @@ public: friend void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id); protected: - /*virtual*/ void updateButtons(); void onCommitLoad(LLUICtrl* ctrl); private: @@ -270,41 +244,6 @@ private: boost::signals2::connection mAvatarNameCacheConnection; }; -/** -* Panel for displaying Avatar's interests. -*/ -class LLPanelProfileInterests - : public LLPanelProfileTab -{ -public: - LLPanelProfileInterests(); - /*virtual*/ ~LLPanelProfileInterests(); - - /*virtual*/ void onOpen(const LLSD& key); - - /*virtual*/ BOOL postBuild(); - - /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); - - void resetData(); - - /** - * Saves changes. - */ - virtual void apply(); - -protected: - /*virtual*/ void updateButtons(); - -private: - LLCheckBoxCtrl* mWantChecks[8]; - LLCheckBoxCtrl* mSkillChecks[6]; - LLLineEditor* mWantToEditor; - LLLineEditor* mSkillsEditor; - LLLineEditor* mLanguagesEditor; -}; - - /** * Panel for displaying Avatar's first life related info. */ @@ -319,26 +258,32 @@ public: /*virtual*/ BOOL postBuild(); - /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); void processProperties(const LLAvatarData* avatar_data); void resetData(); - /** - * Saves changes. - */ - void apply(LLAvatarData* data); + void setProfileImageUploading(bool loading); + void setProfileImageUploaded(const LLUUID &image_asset_id); friend void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id); protected: - /*virtual*/ void updateButtons(); - void onDescriptionFocusReceived(); + /*virtual*/ void setLoaded(); + + void onChangePhoto(); + void onRemovePhoto(); + void setDescriptionText(const std::string &text); + void onSetDescriptionDirty(); + void onSaveDescriptionChanges(); + void onDiscardDescriptionChanges(); LLTextEditor* mDescriptionEdit; - LLTextureCtrl* mPicture; + LLIconCtrl* mPicture; + LLButton* mChangePhoto; + LLButton* mRemovePhoto; + LLButton* mSaveChanges; + LLButton* mDiscardChanges; - bool mIsEditing; std::string mCurrentDescription; }; @@ -347,7 +292,6 @@ protected: */ class LLPanelProfileNotes : public LLPanelProfileTab - , public LLFriendObserver { public: LLPanelProfileNotes(); @@ -355,51 +299,28 @@ public: virtual void setAvatarId(const LLUUID& avatar_id); - /** - * LLFriendObserver trigger - */ - virtual void changed(U32 mask); - /*virtual*/ void onOpen(const LLSD& key); /*virtual*/ BOOL postBuild(); - /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); void processProperties(LLAvatarNotes* avatar_notes); void resetData(); /*virtual*/ void updateData(); - /** - * Saves changes. - */ - virtual void apply(); - - void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); - protected: - /** - * Fills rights data for friends. - */ - void fillRightsData(); - - void rightsConfirmationCallback(const LLSD& notification, const LLSD& response); - void confirmModifyRights(bool grant); - void onCommitRights(); void onCommitNotes(); - void enableCheckboxes(bool enable); + void setNotesText(const std::string &text); + void onSetNotesDirty(); + void onSaveNotesChanges(); + void onDiscardNotesChanges(); - void applyRights(); - - LLCheckBoxCtrl* mOnlineStatus; - LLCheckBoxCtrl* mMapRights; - LLCheckBoxCtrl* mEditObjectRights; LLTextEditor* mNotesEditor; + LLButton* mSaveChanges; + LLButton* mDiscardChanges; - std::string mURLWebProfile; - - boost::signals2::connection mAvatarNameCacheConnection; + std::string mCurrentNotes; }; @@ -417,15 +338,8 @@ public: /*virtual*/ void updateData(); - /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); - /*virtual*/ void onOpen(const LLSD& key); - /** - * Saves changes. - */ - void apply(); - void showPick(const LLUUID& pick_id = LLUUID::null); bool isPickTabSelected(); bool isNotesTabSelected(); @@ -443,11 +357,10 @@ private: LLPanelProfileSecondLife* mPanelSecondlife; LLPanelProfileWeb* mPanelWeb; - LLPanelProfileInterests* mPanelInterests; LLPanelProfilePicks* mPanelPicks; LLPanelProfileClassifieds* mPanelClassifieds; LLPanelProfileFirstLife* mPanelFirstlife; - LLPanelProfileNotes* mPanelNotes; + LLPanelProfileNotes* mPanelNotes; LLTabContainer* mTabContainer; // Todo: due to server taking minutes to update this needs a more long term storage diff --git a/indra/newview/llpanelprofileclassifieds.cpp b/indra/newview/llpanelprofileclassifieds.cpp index 293cd12a8a..0b0ee910cc 100644 --- a/indra/newview/llpanelprofileclassifieds.cpp +++ b/indra/newview/llpanelprofileclassifieds.cpp @@ -193,7 +193,7 @@ LLClassifiedHandler gClassifiedHandler; //----------------------------------------------------------------------------- LLPanelProfileClassifieds::LLPanelProfileClassifieds() - : LLPanelProfileTab() + : LLPanelProfilePropertiesProcessorTab() , mClassifiedToSelectOnLoad(LLUUID::null) , mClassifiedEditOnLoad(false) , mRlvBehaviorCallbackConnection() // RLVa support @@ -216,7 +216,7 @@ void LLPanelProfileClassifieds::onOpen(const LLSD& key) resetData(); - if (getSelfProfile() && !getEmbedded()) + if (getSelfProfile()) { mNewButton->setVisible(TRUE); mNewButton->setEnabled(FALSE); @@ -386,6 +386,7 @@ void LLPanelProfileClassifieds::processProperties(void* data, EAvatarProcessorTy mTabContainer->selectFirstTab(); } + setLoaded(); updateButtons(); } } @@ -399,9 +400,7 @@ void LLPanelProfileClassifieds::resetData() void LLPanelProfileClassifieds::updateButtons() { - LLPanelProfileTab::updateButtons(); - - if (getSelfProfile() && !getEmbedded()) + if (getSelfProfile()) { // RLVa support //mNewButton->setEnabled(canAddNewClassified()); @@ -415,7 +414,7 @@ void LLPanelProfileClassifieds::updateData() { // Send picks request only once LLUUID avatar_id = getAvatarId(); - if (!getIsLoading() && avatar_id.notNull()) + if (!getStarted() && avatar_id.notNull()) { setIsLoading(); mNoItemsLabel->setValue(LLTrans::getString("PicksClassifiedsLoadingText")); @@ -501,7 +500,7 @@ static const S32 CB_ITEM_MATURE = 0; static const S32 CB_ITEM_PG = 1; LLPanelProfileClassified::LLPanelProfileClassified() - : LLPanelProfileTab() + : LLPanelProfilePropertiesProcessorTab() , mInfoLoaded(false) , mTeleportClicksOld(0) , mMapClicksOld(0) @@ -766,6 +765,7 @@ void LLPanelProfileClassified::processProperties(void* data, EAvatarProcessorTyp // for just created classified - in case user opened edit panel before processProperties() callback mSaveButton->setLabelArg("[LABEL]", getString("save_label")); + setLoaded(); updateButtons(); if (mEditOnLoad) diff --git a/indra/newview/llpanelprofileclassifieds.h b/indra/newview/llpanelprofileclassifieds.h index a5a16cf9dc..79d74faa30 100644 --- a/indra/newview/llpanelprofileclassifieds.h +++ b/indra/newview/llpanelprofileclassifieds.h @@ -70,7 +70,7 @@ public: * Panel for displaying Avatar's picks. */ class LLPanelProfileClassifieds - : public LLPanelProfileTab + : public LLPanelProfilePropertiesProcessorTab { public: LLPanelProfileClassifieds(); @@ -86,7 +86,7 @@ public: /*virtual*/ void resetData(); - /*virtual*/ void updateButtons(); + void updateButtons(); /*virtual*/ void updateData(); @@ -116,7 +116,7 @@ private: class LLPanelProfileClassified - : public LLPanelProfileTab + : public LLPanelProfilePropertiesProcessorTab { public: @@ -220,7 +220,7 @@ protected: void resetControls(); - /*virtual*/ void updateButtons(); + void updateButtons(); void updateInfoRect(); static std::string createLocationText( diff --git a/indra/newview/llpanelprofilepicks.cpp b/indra/newview/llpanelprofilepicks.cpp index 8f76fef2b6..8c2a6ef3f9 100644 --- a/indra/newview/llpanelprofilepicks.cpp +++ b/indra/newview/llpanelprofilepicks.cpp @@ -122,7 +122,7 @@ LLPickHandler gPickHandler; //----------------------------------------------------------------------------- LLPanelProfilePicks::LLPanelProfilePicks() - : LLPanelProfileTab() + : LLPanelProfilePropertiesProcessorTab() , mPickToSelectOnLoad(LLUUID::null) , mRlvBehaviorCallbackConnection() // FIRE-15556: Picks can circumvent RLVa @showloc restriction { @@ -144,7 +144,7 @@ void LLPanelProfilePicks::onOpen(const LLSD& key) resetData(); - if (getSelfProfile() && !getEmbedded()) + if (getSelfProfile()) { mNewButton->setVisible(TRUE); mNewButton->setEnabled(FALSE); @@ -322,6 +322,7 @@ void LLPanelProfilePicks::processProperties(const LLAvatarPicks* avatar_picks) mTabContainer->selectFirstTab(); } + setLoaded(); updateButtons(); } @@ -333,9 +334,7 @@ void LLPanelProfilePicks::resetData() void LLPanelProfilePicks::updateButtons() { - LLPanelProfileTab::updateButtons(); - - if (getSelfProfile() && !getEmbedded()) + if (getSelfProfile()) { // RLVa support //mNewButton->setEnabled(canAddNewPick()); @@ -364,7 +363,7 @@ void LLPanelProfilePicks::updateData() { // Send picks request only once LLUUID avatar_id = getAvatarId(); - if (!getIsLoading() && avatar_id.notNull()) + if (!getStarted() && avatar_id.notNull()) { setIsLoading(); mNoItemsLabel->setValue(LLTrans::getString("PicksClassifiedsLoadingText")); @@ -404,7 +403,7 @@ bool LLPanelProfilePicks::canDeletePick() //----------------------------------------------------------------------------- LLPanelProfilePick::LLPanelProfilePick() - : LLPanelProfileTab() + : LLPanelProfilePropertiesProcessorTab() , LLRemoteParcelInfoObserver() , mSnapshotCtrl(NULL) , mPickId(LLUUID::null) @@ -483,7 +482,7 @@ void LLPanelProfilePick::setAvatarId(const LLUUID& avatar_id) resetDirty(); - if (getSelfProfile() && !getEmbedded()) + if (getSelfProfile()) { mPickName->setEnabled(TRUE); mPickDescription->setEnabled(TRUE); @@ -551,7 +550,7 @@ void LLPanelProfilePick::processProperties(void* data, EAvatarProcessorType type mPickDescription->setParseHTML(true); mParcelId = pick_info->parcel_id; setSnapshotId(pick_info->snapshot_id); - if (!getSelfProfile() || getEmbedded()) + if (!getSelfProfile()) { mSnapshotCtrl->setEnabled(FALSE); } @@ -568,7 +567,7 @@ void LLPanelProfilePick::processProperties(void* data, EAvatarProcessorType type // edit the Pick and we have to update Pick info panel. // revomeObserver is called from onClickBack - updateButtons(); + setLoaded(); } void LLPanelProfilePick::apply() diff --git a/indra/newview/llpanelprofilepicks.h b/indra/newview/llpanelprofilepicks.h index d0232a18ce..e1201040e6 100644 --- a/indra/newview/llpanelprofilepicks.h +++ b/indra/newview/llpanelprofilepicks.h @@ -46,7 +46,7 @@ class LLTextEditor; * Panel for displaying Avatar's picks. */ class LLPanelProfilePicks - : public LLPanelProfileTab + : public LLPanelProfilePropertiesProcessorTab { public: LLPanelProfilePicks(); @@ -63,7 +63,7 @@ public: /*virtual*/ void resetData(); - /*virtual*/ void updateButtons(); + void updateButtons(); /** * Saves changes. @@ -100,7 +100,7 @@ private: class LLPanelProfilePick - : public LLPanelProfileTab + : public LLPanelProfilePropertiesProcessorTab , public LLRemoteParcelInfoObserver { public: diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index bad9b878b9..809298183a 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -3277,6 +3277,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("UpdateScriptTask"); capabilityNames.append("UpdateSettingsAgentInventory"); capabilityNames.append("UpdateSettingsTaskInventory"); + capabilityNames.append("UploadAgentProfileImage"); capabilityNames.append("UploadBakedTexture"); capabilityNames.append("UserInfo"); capabilityNames.append("ViewerAsset"); diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index a713246161..c30cbde4d2 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -1397,14 +1397,13 @@ void LLViewerTextureList::decodeAllImages(F32 max_time) BOOL LLViewerTextureList::createUploadFile(const std::string& filename, const std::string& out_filename, - const U8 codec) + const U8 codec, + const S32 max_image_dimentions) { // Load the image LLPointer image = LLImageFormatted::createFromType(codec); if (image.isNull()) { - // Pointer is null! - //image->setLastError("Couldn't open the image to be uploaded."); return FALSE; } if (!image->load(filename)) @@ -1426,7 +1425,7 @@ BOOL LLViewerTextureList::createUploadFile(const std::string& filename, return FALSE; } // Convert to j2c (JPEG2000) and save the file locally - LLPointer compressedImage = convertToUploadFile(raw_image); + LLPointer compressedImage = convertToUploadFile(raw_image, max_image_dimentions); if (compressedImage.isNull()) { image->setLastError("Couldn't convert the image to jpeg2000."); @@ -1451,9 +1450,9 @@ BOOL LLViewerTextureList::createUploadFile(const std::string& filename, } // note: modifies the argument raw_image!!!! -LLPointer LLViewerTextureList::convertToUploadFile(LLPointer raw_image) +LLPointer LLViewerTextureList::convertToUploadFile(LLPointer raw_image, const S32 max_image_dimentions) { - raw_image->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT); + raw_image->biasedScaleToPowerOfTwo(max_image_dimentions); LLPointer compressedImage = new LLImageJ2C(); if (gSavedSettings.getBOOL("LosslessJ2CUpload") && diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h index 7af3ed7e8b..80d7dcbfab 100644 --- a/indra/newview/llviewertexturelist.h +++ b/indra/newview/llviewertexturelist.h @@ -93,8 +93,11 @@ class LLViewerTextureList friend class LLLocalBitmap; public: - static BOOL createUploadFile(const std::string& filename, const std::string& out_filename, const U8 codec); - static LLPointer convertToUploadFile(LLPointer raw_image); + static BOOL createUploadFile(const std::string& filename, + const std::string& out_filename, + const U8 codec, + const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT); + static LLPointer convertToUploadFile(LLPointer raw_image, const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT); static void processImageNotInDatabase( LLMessageSystem *msg, void **user_data ); static void receiveImageHeader(LLMessageSystem *msg, void **user_data); static void receiveImagePacket(LLMessageSystem *msg, void **user_data); diff --git a/indra/newview/skins/default/xui/en/floater_display_name.xml b/indra/newview/skins/default/xui/en/floater_display_name.xml index 53541b23ec..a8b54383cd 100644 --- a/indra/newview/skins/default/xui/en/floater_display_name.xml +++ b/indra/newview/skins/default/xui/en/floater_display_name.xml @@ -83,21 +83,12 @@ tool_tip="Save your new Display Name" top_pad="10" width="120" /> -